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 3 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 DATA_BLOB msg1 = data_blob_null;
32 DATA_BLOB blob = data_blob_null;
33 DATA_BLOB blob_in = data_blob_null;
34 DATA_BLOB blob_out = data_blob_null;
35 struct berval cred, *scred = NULL;
40 struct ntlmssp_state *ntlmssp_state;
42 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
43 return ADS_ERROR_NT(nt_status);
45 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
47 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
48 return ADS_ERROR_NT(nt_status);
50 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
51 return ADS_ERROR_NT(nt_status);
53 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
54 return ADS_ERROR_NT(nt_status);
57 blob_in = data_blob_null;
60 nt_status = ntlmssp_update(ntlmssp_state,
62 data_blob_free(&blob_in);
63 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
64 || NT_STATUS_IS_OK(nt_status))
67 /* and wrap it in a SPNEGO wrapper */
68 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
70 /* wrap it in SPNEGO */
71 msg1 = spnego_gen_auth(blob_out);
74 data_blob_free(&blob_out);
76 cred.bv_val = (char *)msg1.data;
77 cred.bv_len = msg1.length;
79 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
80 data_blob_free(&msg1);
81 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
86 ntlmssp_end(&ntlmssp_state);
90 blob = data_blob(scred->bv_val, scred->bv_len);
93 blob = data_blob_null;
98 ntlmssp_end(&ntlmssp_state);
99 data_blob_free(&blob_out);
100 return ADS_ERROR_NT(nt_status);
104 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
105 DATA_BLOB tmp_blob = data_blob_null;
106 /* the server might give us back two challenges */
107 if (!spnego_parse_challenge(blob, &blob_in,
110 ntlmssp_end(&ntlmssp_state);
111 data_blob_free(&blob);
112 DEBUG(3,("Failed to parse challenges\n"));
113 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
115 data_blob_free(&tmp_blob);
116 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
117 if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
120 ntlmssp_end(&ntlmssp_state);
121 data_blob_free(&blob);
122 DEBUG(3,("Failed to parse auth response\n"));
123 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
126 data_blob_free(&blob);
127 data_blob_free(&blob_out);
129 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
131 /* we have a reference conter on ntlmssp_state, if we are signing
132 then the state will be kept by the signing engine */
134 ntlmssp_end(&ntlmssp_state);
136 return ADS_ERROR(rc);
141 perform a LDAP/SASL/SPNEGO/KRB5 bind
143 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
145 DATA_BLOB blob = data_blob_null;
146 struct berval cred, *scred = NULL;
147 DATA_BLOB session_key = data_blob_null;
150 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
151 &ads->auth.tgs_expire);
154 return ADS_ERROR_KRB5(rc);
157 /* now send the auth packet and we should be done */
158 cred.bv_val = (char *)blob.data;
159 cred.bv_len = blob.length;
161 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
163 data_blob_free(&blob);
164 data_blob_free(&session_key);
168 return ADS_ERROR(rc);
173 this performs a SASL/SPNEGO bind
175 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
177 struct berval *scred=NULL;
181 char *principal = NULL;
182 char *OIDs[ASN1_MAX_OIDS];
184 BOOL got_kerberos_mechanism = False;
187 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
189 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
190 status = ADS_ERROR(rc);
194 blob = data_blob(scred->bv_val, scred->bv_len);
199 file_save("sasl_spnego.dat", blob.data, blob.length);
202 /* the server sent us the first part of the SPNEGO exchange in the negprot
204 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
205 data_blob_free(&blob);
206 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
209 data_blob_free(&blob);
211 /* make sure the server understands kerberos */
212 for (i=0;OIDs[i];i++) {
213 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
215 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
216 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
217 got_kerberos_mechanism = True;
222 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
225 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
226 got_kerberos_mechanism)
228 /* I've seen a child Windows 2000 domain not send
229 the principal name back in the first round of
230 the SASL bind reply. So we guess based on server
231 name and realm. --jerry */
233 if ( ads->server.realm && ads->server.ldap_server ) {
234 char *server, *server_realm;
236 server = SMB_STRDUP( ads->server.ldap_server );
237 server_realm = SMB_STRDUP( ads->server.realm );
239 if ( !server || !server_realm )
240 return ADS_ERROR(LDAP_NO_MEMORY);
242 strlower_m( server );
243 strupper_m( server_realm );
244 asprintf( &principal, "ldap/%s@%s", server, server_realm );
247 SAFE_FREE( server_realm );
250 return ADS_ERROR(LDAP_NO_MEMORY);
255 status = ads_sasl_spnego_krb5_bind(ads, principal);
256 if (ADS_ERR_OK(status)) {
257 SAFE_FREE(principal);
261 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
262 "calling kinit\n", ads_errstr(status)));
264 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
266 if (ADS_ERR_OK(status)) {
267 status = ads_sasl_spnego_krb5_bind(ads, principal);
270 /* only fallback to NTLMSSP if allowed */
271 if (ADS_ERR_OK(status) ||
272 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
273 SAFE_FREE(principal);
279 SAFE_FREE(principal);
281 /* lets do NTLMSSP ... this has the big advantage that we don't need
282 to sync clocks, and we don't rely on special versions of the krb5
283 library for HMAC_MD4 encryption */
284 return ads_sasl_spnego_ntlmssp_bind(ads);
291 #define MAX_GSS_PASSES 3
293 /* this performs a SASL/gssapi bind
294 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
295 is very dependent on correctly configured DNS whereas
296 this routine is much less fragile
297 see RFC2078 and RFC2222 for details
299 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
302 gss_name_t serv_name;
303 gss_buffer_desc input_name;
304 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
305 gss_OID mech_type = GSS_C_NULL_OID;
306 gss_buffer_desc output_token, input_token;
307 uint32 ret_flags, conf_state;
309 struct berval *scred = NULL;
313 uint32 max_msg_size = 0;
316 krb5_principal principal = NULL;
317 krb5_context ctx = NULL;
318 krb5_enctype enc_types[] = {
319 #ifdef ENCTYPE_ARCFOUR_HMAC
320 ENCTYPE_ARCFOUR_HMAC,
324 gss_OID_desc nt_principal =
325 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
327 /* we need to fetch a service ticket as the ldap user in the
328 servers realm, regardless of our realm */
329 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
331 initialize_krb5_error_table();
332 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
333 if (!ADS_ERR_OK(status)) {
337 status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
338 if (!ADS_ERR_OK(status)) {
340 krb5_free_context(ctx);
343 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
344 if (!ADS_ERR_OK(status)) {
346 krb5_free_context(ctx);
350 input_name.value = &principal;
351 input_name.length = sizeof(principal);
353 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
356 * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
357 * to point to the *address* of the krb5_principal, and the gss libraries
358 * to a shallow copy of the krb5_principal pointer - so we need to keep
359 * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
360 * Just one more way in which MIT engineers screwed me over.... JRA.
366 krb5_free_principal(ctx, principal);
367 krb5_free_context(ctx);
368 return ADS_ERROR_GSS(gss_rc, minor_status);
371 input_token.value = NULL;
372 input_token.length = 0;
374 for (i=0; i < MAX_GSS_PASSES; i++) {
375 gss_rc = gss_init_sec_context(&minor_status,
380 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
389 if (input_token.value) {
390 gss_release_buffer(&minor_status, &input_token);
393 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
394 status = ADS_ERROR_GSS(gss_rc, minor_status);
398 cred.bv_val = (char *)output_token.value;
399 cred.bv_len = output_token.length;
401 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
403 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
404 status = ADS_ERROR(rc);
408 if (output_token.value) {
409 gss_release_buffer(&minor_status, &output_token);
413 input_token.value = scred->bv_val;
414 input_token.length = scred->bv_len;
416 input_token.value = NULL;
417 input_token.length = 0;
420 if (gss_rc == 0) break;
423 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
424 (int *)&conf_state,NULL);
426 status = ADS_ERROR_GSS(gss_rc, minor_status);
430 gss_release_buffer(&minor_status, &input_token);
432 p = (uint8 *)output_token.value;
435 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
439 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
442 gss_release_buffer(&minor_status, &output_token);
444 output_token.length = 4;
445 output_token.value = SMB_MALLOC(output_token.length);
446 p = (uint8 *)output_token.value;
448 *p++ = 1; /* no sign & seal selection */
449 /* choose the same size as the server gave us */
450 *p++ = max_msg_size>>16;
451 *p++ = max_msg_size>>8;
454 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
455 * but using ads->config.bind_path is the wrong! It should be
456 * the DN of the user object!
458 * w2k3 gives an error when we send an incorrect DN, but sending nothing
459 * is ok and matches the information flow used in GSS-SPNEGO.
462 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
463 &output_token, (int *)&conf_state,
466 status = ADS_ERROR_GSS(gss_rc, minor_status);
470 free(output_token.value);
472 cred.bv_val = (char *)input_token.value;
473 cred.bv_len = input_token.length;
475 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
477 status = ADS_ERROR(rc);
479 gss_release_buffer(&minor_status, &input_token);
483 gss_release_name(&minor_status, &serv_name);
484 if (context_handle != GSS_C_NO_CONTEXT)
485 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
486 krb5_free_principal(ctx, principal);
487 krb5_free_context(ctx);
493 #endif /* HAVE_GGSAPI */
495 /* mapping between SASL mechanisms and functions */
498 ADS_STATUS (*fn)(ADS_STRUCT *);
499 } sasl_mechanisms[] = {
500 {"GSS-SPNEGO", ads_sasl_spnego_bind},
502 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
507 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
509 const char *attrs[] = {"supportedSASLMechanisms", NULL};
515 /* get a list of supported SASL mechanisms */
516 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
517 if (!ADS_ERR_OK(status)) return status;
519 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
521 /* try our supported mechanisms in order */
522 for (i=0;sasl_mechanisms[i].name;i++) {
523 /* see if the server supports it */
524 for (j=0;values && values[j];j++) {
525 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
526 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
527 status = sasl_mechanisms[i].fn(ads);
528 ldap_value_free(values);
535 ldap_value_free(values);
537 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
540 #endif /* HAVE_LDAP */