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, see <http://www.gnu.org/licenses/>.
24 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
26 struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
30 uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
32 /* copy the data to the right location */
33 memcpy(dptr, buf, len);
35 /* create the signature and may encrypt the data */
36 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
37 nt_status = ntlmssp_seal_packet(ntlmssp_state,
42 nt_status = ntlmssp_sign_packet(ntlmssp_state,
47 status = ADS_ERROR_NT(nt_status);
48 if (!ADS_ERR_OK(status)) return status;
50 /* copy the signature to the right location */
51 memcpy(ads->ldap.out.buf + 4,
52 sig.data, NTLMSSP_SIG_SIZE);
56 /* set how many bytes must be written to the underlying socket */
57 ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
62 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
64 struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
68 uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
69 uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
71 /* wrap the signature into a DATA_BLOB */
72 sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
74 /* verify the signature and maybe decrypt the data */
75 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
76 nt_status = ntlmssp_unseal_packet(ntlmssp_state,
81 nt_status = ntlmssp_check_packet(ntlmssp_state,
86 status = ADS_ERROR_NT(nt_status);
87 if (!ADS_ERR_OK(status)) return status;
89 /* set the amount of bytes for the upper layer and set the ofs to the data */
90 ads->ldap.in.left = dlen;
91 ads->ldap.in.ofs = 4 + NTLMSSP_SIG_SIZE;
96 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
98 struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
100 ntlmssp_end(&ntlmssp_state);
102 ads->ldap.wrap_ops = NULL;
103 ads->ldap.wrap_private_data = NULL;
106 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
108 .wrap = ads_sasl_ntlmssp_wrap,
109 .unwrap = ads_sasl_ntlmssp_unwrap,
110 .disconnect = ads_sasl_ntlmssp_disconnect
114 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
115 we fit on one socket??)
117 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
119 DATA_BLOB msg1 = data_blob_null;
120 DATA_BLOB blob = data_blob_null;
121 DATA_BLOB blob_in = data_blob_null;
122 DATA_BLOB blob_out = data_blob_null;
123 struct berval cred, *scred = NULL;
129 struct ntlmssp_state *ntlmssp_state;
131 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
132 return ADS_ERROR_NT(nt_status);
134 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
136 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
137 return ADS_ERROR_NT(nt_status);
139 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
140 return ADS_ERROR_NT(nt_status);
142 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
143 return ADS_ERROR_NT(nt_status);
146 switch (ads->ldap.wrap_type) {
147 case ADS_SASLWRAP_TYPE_SEAL:
148 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
150 case ADS_SASLWRAP_TYPE_SIGN:
151 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
152 features = NTLMSSP_FEATURE_SIGN;
155 * windows servers are broken with sign only,
156 * so we need to use seal here too
158 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
159 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
162 case ADS_SASLWRAP_TYPE_PLAIN:
166 ntlmssp_want_feature(ntlmssp_state, features);
168 blob_in = data_blob_null;
171 nt_status = ntlmssp_update(ntlmssp_state,
173 data_blob_free(&blob_in);
174 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
175 || NT_STATUS_IS_OK(nt_status))
176 && blob_out.length) {
178 /* and wrap it in a SPNEGO wrapper */
179 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
181 /* wrap it in SPNEGO */
182 msg1 = spnego_gen_auth(blob_out);
185 data_blob_free(&blob_out);
187 cred.bv_val = (char *)msg1.data;
188 cred.bv_len = msg1.length;
190 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
191 data_blob_free(&msg1);
192 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
197 ntlmssp_end(&ntlmssp_state);
198 return ADS_ERROR(rc);
201 blob = data_blob(scred->bv_val, scred->bv_len);
204 blob = data_blob_null;
209 ntlmssp_end(&ntlmssp_state);
210 data_blob_free(&blob_out);
211 return ADS_ERROR_NT(nt_status);
215 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
216 DATA_BLOB tmp_blob = data_blob_null;
217 /* the server might give us back two challenges */
218 if (!spnego_parse_challenge(blob, &blob_in,
221 ntlmssp_end(&ntlmssp_state);
222 data_blob_free(&blob);
223 DEBUG(3,("Failed to parse challenges\n"));
224 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
226 data_blob_free(&tmp_blob);
227 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
228 if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
231 ntlmssp_end(&ntlmssp_state);
232 data_blob_free(&blob);
233 DEBUG(3,("Failed to parse auth response\n"));
234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
237 data_blob_free(&blob);
238 data_blob_free(&blob_out);
240 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
242 /* we have a reference conter on ntlmssp_state, if we are signing
243 then the state will be kept by the signing engine */
245 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
246 ads->ldap.out.min = 4;
247 ads->ldap.out.max = 0x0FFFFFFF - NTLMSSP_SIG_SIZE;
248 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
249 ads->ldap.in.min = 4;
250 ads->ldap.in.max = 0x0FFFFFFF;
251 ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
253 ntlmssp_end(&ntlmssp_state);
256 return ADS_ERROR(rc);
260 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
262 gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
266 gss_buffer_desc unwrapped, wrapped;
267 int conf_req_flag, conf_state;
269 unwrapped.value = buf;
270 unwrapped.length = len;
272 /* for now request sign and seal */
273 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
275 gss_rc = gss_wrap(&minor_status, context_handle,
276 conf_req_flag, GSS_C_QOP_DEFAULT,
277 &unwrapped, &conf_state,
279 status = ADS_ERROR_GSS(gss_rc, minor_status);
280 if (!ADS_ERR_OK(status)) return status;
282 if (conf_req_flag && conf_state == 0) {
283 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
286 if ((ads->ldap.out.size - 4) < wrapped.length) {
287 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
290 /* copy the wrapped blob to the right location */
291 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
293 /* set how many bytes must be written to the underlying socket */
294 ads->ldap.out.left = 4 + wrapped.length;
296 gss_release_buffer(&minor_status, &wrapped);
301 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
303 gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
307 gss_buffer_desc unwrapped, wrapped;
310 wrapped.value = ads->ldap.in.buf + 4;
311 wrapped.length = ads->ldap.in.ofs - 4;
313 gss_rc = gss_unwrap(&minor_status, context_handle,
314 &wrapped, &unwrapped,
315 &conf_state, GSS_C_QOP_DEFAULT);
316 status = ADS_ERROR_GSS(gss_rc, minor_status);
317 if (!ADS_ERR_OK(status)) return status;
319 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
320 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
323 if (wrapped.length < wrapped.length) {
324 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
327 /* copy the wrapped blob to the right location */
328 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
330 /* set how many bytes must be written to the underlying socket */
331 ads->ldap.in.left = unwrapped.length;
332 ads->ldap.in.ofs = 4;
334 gss_release_buffer(&minor_status, &unwrapped);
339 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
341 gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
344 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
346 ads->ldap.wrap_ops = NULL;
347 ads->ldap.wrap_private_data = NULL;
350 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
352 .wrap = ads_sasl_gssapi_wrap,
353 .unwrap = ads_sasl_gssapi_unwrap,
354 .disconnect = ads_sasl_gssapi_disconnect
360 perform a LDAP/SASL/SPNEGO/KRB5 bind
362 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
364 DATA_BLOB blob = data_blob_null;
365 struct berval cred, *scred = NULL;
366 DATA_BLOB session_key = data_blob_null;
369 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
370 &ads->auth.tgs_expire);
373 return ADS_ERROR_KRB5(rc);
376 /* now send the auth packet and we should be done */
377 cred.bv_val = (char *)blob.data;
378 cred.bv_len = blob.length;
380 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
382 data_blob_free(&blob);
383 data_blob_free(&session_key);
387 return ADS_ERROR(rc);
392 this performs a SASL/SPNEGO bind
394 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
396 struct berval *scred=NULL;
400 char *principal = NULL;
401 char *OIDs[ASN1_MAX_OIDS];
403 BOOL got_kerberos_mechanism = False;
406 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
408 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
409 status = ADS_ERROR(rc);
413 blob = data_blob(scred->bv_val, scred->bv_len);
418 file_save("sasl_spnego.dat", blob.data, blob.length);
421 /* the server sent us the first part of the SPNEGO exchange in the negprot
423 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
424 data_blob_free(&blob);
425 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
428 data_blob_free(&blob);
430 /* make sure the server understands kerberos */
431 for (i=0;OIDs[i];i++) {
432 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
434 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
435 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
436 got_kerberos_mechanism = True;
441 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
444 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
445 got_kerberos_mechanism)
447 /* I've seen a child Windows 2000 domain not send
448 the principal name back in the first round of
449 the SASL bind reply. So we guess based on server
450 name and realm. --jerry */
452 if ( ads->server.realm && ads->server.ldap_server ) {
453 char *server, *server_realm;
455 server = SMB_STRDUP( ads->server.ldap_server );
456 server_realm = SMB_STRDUP( ads->server.realm );
458 if ( !server || !server_realm )
459 return ADS_ERROR(LDAP_NO_MEMORY);
461 strlower_m( server );
462 strupper_m( server_realm );
463 asprintf( &principal, "ldap/%s@%s", server, server_realm );
466 SAFE_FREE( server_realm );
469 return ADS_ERROR(LDAP_NO_MEMORY);
474 status = ads_sasl_spnego_krb5_bind(ads, principal);
475 if (ADS_ERR_OK(status)) {
476 SAFE_FREE(principal);
480 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
481 "calling kinit\n", ads_errstr(status)));
483 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
485 if (ADS_ERR_OK(status)) {
486 status = ads_sasl_spnego_krb5_bind(ads, principal);
489 /* only fallback to NTLMSSP if allowed */
490 if (ADS_ERR_OK(status) ||
491 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
492 SAFE_FREE(principal);
498 SAFE_FREE(principal);
500 /* lets do NTLMSSP ... this has the big advantage that we don't need
501 to sync clocks, and we don't rely on special versions of the krb5
502 library for HMAC_MD4 encryption */
503 return ads_sasl_spnego_ntlmssp_bind(ads);
510 #define MAX_GSS_PASSES 3
512 /* this performs a SASL/gssapi bind
513 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
514 is very dependent on correctly configured DNS whereas
515 this routine is much less fragile
516 see RFC2078 and RFC2222 for details
518 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
521 gss_name_t serv_name;
522 gss_buffer_desc input_name;
523 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
524 gss_OID mech_type = GSS_C_NULL_OID;
525 gss_buffer_desc output_token, input_token;
526 uint32 req_flags, ret_flags;
529 struct berval *scred = NULL;
533 uint32 max_msg_size = 0;
536 krb5_principal principal = NULL;
537 krb5_context ctx = NULL;
538 krb5_enctype enc_types[] = {
539 #ifdef ENCTYPE_ARCFOUR_HMAC
540 ENCTYPE_ARCFOUR_HMAC,
544 gss_OID_desc nt_principal =
545 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
547 /* we need to fetch a service ticket as the ldap user in the
548 servers realm, regardless of our realm */
549 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
551 initialize_krb5_error_table();
552 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
553 if (!ADS_ERR_OK(status)) {
557 status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
558 if (!ADS_ERR_OK(status)) {
560 krb5_free_context(ctx);
563 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
564 if (!ADS_ERR_OK(status)) {
566 krb5_free_context(ctx);
570 input_name.value = &principal;
571 input_name.length = sizeof(principal);
573 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
576 * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
577 * to point to the *address* of the krb5_principal, and the gss libraries
578 * to a shallow copy of the krb5_principal pointer - so we need to keep
579 * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
580 * Just one more way in which MIT engineers screwed me over.... JRA.
586 krb5_free_principal(ctx, principal);
587 krb5_free_context(ctx);
588 return ADS_ERROR_GSS(gss_rc, minor_status);
591 input_token.value = NULL;
592 input_token.length = 0;
594 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
595 switch (ads->ldap.wrap_type) {
596 case ADS_SASLWRAP_TYPE_SEAL:
597 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
599 case ADS_SASLWRAP_TYPE_SIGN:
600 req_flags |= GSS_C_INTEG_FLAG;
602 case ADS_SASLWRAP_TYPE_PLAIN:
606 for (i=0; i < MAX_GSS_PASSES; i++) {
607 gss_rc = gss_init_sec_context(&minor_status,
621 if (input_token.value) {
622 gss_release_buffer(&minor_status, &input_token);
625 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
626 status = ADS_ERROR_GSS(gss_rc, minor_status);
630 cred.bv_val = (char *)output_token.value;
631 cred.bv_len = output_token.length;
633 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
635 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
636 status = ADS_ERROR(rc);
640 if (output_token.value) {
641 gss_release_buffer(&minor_status, &output_token);
645 input_token.value = scred->bv_val;
646 input_token.length = scred->bv_len;
648 input_token.value = NULL;
649 input_token.length = 0;
652 if (gss_rc == 0) break;
655 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
658 status = ADS_ERROR_GSS(gss_rc, minor_status);
662 gss_release_buffer(&minor_status, &input_token);
664 p = (uint8 *)output_token.value;
667 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
671 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
674 gss_release_buffer(&minor_status, &output_token);
676 output_token.length = 4;
677 output_token.value = SMB_MALLOC(output_token.length);
678 p = (uint8 *)output_token.value;
680 *p++ = ads->ldap.wrap_type;
681 /* choose the same size as the server gave us */
682 *p++ = max_msg_size>>16;
683 *p++ = max_msg_size>>8;
686 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
687 * but using ads->config.bind_path is the wrong! It should be
688 * the DN of the user object!
690 * w2k3 gives an error when we send an incorrect DN, but sending nothing
691 * is ok and matches the information flow used in GSS-SPNEGO.
694 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
695 &output_token, &conf_state,
698 status = ADS_ERROR_GSS(gss_rc, minor_status);
702 free(output_token.value);
704 cred.bv_val = (char *)input_token.value;
705 cred.bv_len = input_token.length;
707 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
709 status = ADS_ERROR(rc);
711 gss_release_buffer(&minor_status, &input_token);
713 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
714 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
715 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
717 max_msg_size, &ads->ldap.out.max);
719 status = ADS_ERROR_GSS(gss_rc, minor_status);
723 ads->ldap.out.min = 4;
724 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
725 ads->ldap.in.min = 4;
726 ads->ldap.in.max = max_msg_size;
727 ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
728 /* make sure we don't free context_handle */
729 context_handle = GSS_C_NO_CONTEXT;
733 gss_release_name(&minor_status, &serv_name);
734 if (context_handle != GSS_C_NO_CONTEXT)
735 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
736 krb5_free_principal(ctx, principal);
737 krb5_free_context(ctx);
743 #endif /* HAVE_GGSAPI */
745 /* mapping between SASL mechanisms and functions */
748 ADS_STATUS (*fn)(ADS_STRUCT *);
749 } sasl_mechanisms[] = {
750 {"GSS-SPNEGO", ads_sasl_spnego_bind},
752 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
757 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
759 const char *attrs[] = {"supportedSASLMechanisms", NULL};
765 /* get a list of supported SASL mechanisms */
766 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
767 if (!ADS_ERR_OK(status)) return status;
769 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
771 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
772 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
773 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
774 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
776 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
779 /* try our supported mechanisms in order */
780 for (i=0;sasl_mechanisms[i].name;i++) {
781 /* see if the server supports it */
782 for (j=0;values && values[j];j++) {
783 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
784 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
785 status = sasl_mechanisms[i].fn(ads);
786 ldap_value_free(values);
793 ldap_value_free(values);
795 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
798 #endif /* HAVE_LDAP */