2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2001
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
31 const char *mechs[] = {OID_NTLMSSP, NULL};
33 DATA_BLOB blob, chal1, chal2, auth;
35 uint8 nthash[24], lmhash[24], sess_key[16];
37 struct berval cred, *scred;
41 if (!ads->auth.password) {
42 /* No password, don't segfault below... */
43 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
46 neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
47 NTLMSSP_NEGOTIATE_128 |
48 NTLMSSP_NEGOTIATE_NTLM;
50 memset(sess_key, 0, 16);
52 /* generate the ntlmssp negotiate packet */
53 msrpc_gen(&blob, "CddB",
59 /* and wrap it in a SPNEGO wrapper */
60 msg1 = gen_negTokenTarg(mechs, blob);
61 data_blob_free(&blob);
63 cred.bv_val = msg1.data;
64 cred.bv_len = msg1.length;
66 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
67 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
68 status = ADS_ERROR(rc);
72 blob = data_blob(scred->bv_val, scred->bv_len);
74 /* the server gives us back two challenges */
75 if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
76 DEBUG(3,("Failed to parse challenges\n"));
77 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
81 data_blob_free(&blob);
83 /* encrypt the password with the challenge */
84 memcpy(challenge, chal1.data + 24, 8);
85 SMBencrypt(ads->auth.password, challenge,lmhash);
86 SMBNTencrypt(ads->auth.password, challenge,nthash);
88 data_blob_free(&chal1);
89 data_blob_free(&chal2);
91 /* this generates the actual auth packet */
92 msrpc_gen(&blob, "CdBBUUUBd",
103 /* wrap it in SPNEGO */
104 auth = spnego_gen_auth(blob);
106 data_blob_free(&blob);
108 /* now send the auth packet and we should be done */
109 cred.bv_val = auth.data;
110 cred.bv_len = auth.length;
112 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114 return ADS_ERROR(rc);
121 perform a LDAP/SASL/SPNEGO/KRB5 bind
123 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
126 struct berval cred, *scred;
129 blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
132 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
135 /* now send the auth packet and we should be done */
136 cred.bv_val = blob.data;
137 cred.bv_len = blob.length;
139 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141 data_blob_free(&blob);
143 return ADS_ERROR(rc);
147 this performs a SASL/SPNEGO bind
149 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 struct berval *scred=NULL;
156 char *OIDs[ASN1_MAX_OIDS];
157 BOOL got_kerberos_mechanism = False;
159 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
162 status = ADS_ERROR(rc);
166 blob = data_blob(scred->bv_val, scred->bv_len);
169 file_save("sasl_spnego.dat", blob.data, blob.length);
172 /* the server sent us the first part of the SPNEGO exchange in the negprot
174 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
175 data_blob_free(&blob);
176 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
179 data_blob_free(&blob);
181 /* make sure the server understands kerberos */
182 for (i=0;OIDs[i];i++) {
183 DEBUG(3,("got OID=%s\n", OIDs[i]));
184 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
185 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
186 got_kerberos_mechanism = True;
190 DEBUG(3,("got principal=%s\n", principal));
193 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
194 got_kerberos_mechanism) {
195 status = ads_sasl_spnego_krb5_bind(ads, principal);
196 if (ADS_ERR_OK(status))
198 if (ads_kinit_password(ads) == 0) {
199 status = ads_sasl_spnego_krb5_bind(ads, principal);
201 if (ADS_ERR_OK(status))
206 /* lets do NTLMSSP ... this has the big advantage that we don't need
207 to sync clocks, and we don't rely on special versions of the krb5
208 library for HMAC_MD4 encryption */
209 return ads_sasl_spnego_ntlmssp_bind(ads);
216 #define MAX_GSS_PASSES 3
218 /* this performs a SASL/gssapi bind
219 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
220 is very dependent on correctly configured DNS whereas
221 this routine is much less fragile
222 see RFC2078 and RFC2222 for details
224 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
227 gss_name_t serv_name;
228 gss_buffer_desc input_name;
229 gss_ctx_id_t context_handle;
230 gss_OID mech_type = GSS_C_NULL_OID;
231 gss_buffer_desc output_token, input_token;
232 OM_uint32 ret_flags, conf_state;
234 struct berval *scred;
242 krb5_principal principal;
244 krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
245 gss_OID_desc nt_principal =
246 {10, "\052\206\110\206\367\022\001\002\002\002"};
248 /* we need to fetch a service ticket as the ldap user in the
249 servers realm, regardless of our realm */
250 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
251 krb5_init_context(&ctx);
252 krb5_set_default_tgs_ktypes(ctx, enc_types);
253 krb5_parse_name(ctx, sname, &principal);
255 krb5_free_context(ctx);
257 input_name.value = &principal;
258 input_name.length = sizeof(principal);
260 gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
262 return ADS_ERROR_GSS(gss_rc, minor_status);
265 context_handle = GSS_C_NO_CONTEXT;
267 input_token.value = NULL;
268 input_token.length = 0;
270 for (i=0; i < MAX_GSS_PASSES; i++) {
271 gss_rc = gss_init_sec_context(&minor_status,
276 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
285 if (input_token.value) {
286 gss_release_buffer(&minor_status, &input_token);
289 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
290 status = ADS_ERROR_GSS(gss_rc, minor_status);
294 cred.bv_val = output_token.value;
295 cred.bv_len = output_token.length;
297 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
299 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
300 status = ADS_ERROR(rc);
304 if (output_token.value) {
305 gss_release_buffer(&minor_status, &output_token);
309 input_token.value = scred->bv_val;
310 input_token.length = scred->bv_len;
312 input_token.value = NULL;
313 input_token.length = 0;
316 if (gss_rc == 0) break;
319 gss_release_name(&minor_status, &serv_name);
321 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
324 status = ADS_ERROR_GSS(gss_rc, minor_status);
328 gss_release_buffer(&minor_status, &input_token);
330 p = (uint8 *)output_token.value;
332 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
334 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
337 gss_release_buffer(&minor_status, &output_token);
339 output_token.value = malloc(strlen(ads->config.bind_path) + 8);
340 p = output_token.value;
342 *p++ = 1; /* no sign & seal selection */
343 /* choose the same size as the server gave us */
344 *p++ = max_msg_size>>16;
345 *p++ = max_msg_size>>8;
347 snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
350 output_token.length = PTR_DIFF(p, output_token.value);
352 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
353 &output_token, &conf_state,
356 status = ADS_ERROR_GSS(gss_rc, minor_status);
360 free(output_token.value);
362 cred.bv_val = input_token.value;
363 cred.bv_len = input_token.length;
365 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
367 status = ADS_ERROR(rc);
369 gss_release_buffer(&minor_status, &input_token);
376 /* mapping between SASL mechanisms and functions */
379 ADS_STATUS (*fn)(ADS_STRUCT *);
380 } sasl_mechanisms[] = {
381 {"GSS-SPNEGO", ads_sasl_spnego_bind},
383 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
388 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
390 const char *attrs[] = {"supportedSASLMechanisms", NULL};
396 /* get a list of supported SASL mechanisms */
397 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
398 if (!ADS_ERR_OK(status)) return status;
400 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
402 /* try our supported mechanisms in order */
403 for (i=0;sasl_mechanisms[i].name;i++) {
404 /* see if the server supports it */
405 for (j=0;values && values[j];j++) {
406 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
407 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
408 status = sasl_mechanisms[i].fn(ads);
409 ldap_value_free(values);
416 ldap_value_free(values);
418 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);