08a6765e27d1230a69d9b37f6efbcaa90e90c480
[vlendec/samba-autobuild/.git] / source3 / libads / sasl.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads sasl code
4    Copyright (C) Andrew Tridgell 2001
5    
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.
10    
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.
15    
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/>.
18 */
19
20 #include "includes.h"
21
22 #ifdef HAVE_LDAP
23
24 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
25 {
26         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
27         ADS_STATUS status;
28         NTSTATUS nt_status;
29         DATA_BLOB sig;
30         uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
31
32         /* copy the data to the right location */
33         memcpy(dptr, buf, len);
34
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,
38                                                 dptr, len,
39                                                 dptr, len,
40                                                 &sig);
41         } else {
42                 nt_status = ntlmssp_sign_packet(ntlmssp_state,
43                                                 dptr, len,
44                                                 dptr, len,
45                                                 &sig);
46         }
47         status = ADS_ERROR_NT(nt_status);
48         if (!ADS_ERR_OK(status)) return status;
49
50         /* copy the signature to the right location */
51         memcpy(ads->ldap.out.buf + 4,
52                sig.data, NTLMSSP_SIG_SIZE);
53
54         data_blob_free(&sig);
55
56         /* set how many bytes must be written to the underlying socket */
57         ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
58
59         return ADS_SUCCESS;
60 }
61
62 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
63 {
64         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
65         ADS_STATUS status;
66         NTSTATUS nt_status;
67         DATA_BLOB sig;
68         uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
69         uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
70
71         /* wrap the signature into a DATA_BLOB */
72         sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
73
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,
77                                                   dptr, dlen,
78                                                   dptr, dlen,
79                                                   &sig);
80         } else {
81                 nt_status = ntlmssp_check_packet(ntlmssp_state,
82                                                  dptr, dlen,
83                                                  dptr, dlen,
84                                                  &sig);
85         }
86         status = ADS_ERROR_NT(nt_status);
87         if (!ADS_ERR_OK(status)) return status;
88
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;
92
93         return ADS_SUCCESS;
94 }
95
96 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
97 {
98         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
99
100         ntlmssp_end(&ntlmssp_state);
101
102         ads->ldap.wrap_ops = NULL;
103         ads->ldap.wrap_private_data = NULL;
104 }
105
106 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
107         .name           = "ntlmssp",
108         .wrap           = ads_sasl_ntlmssp_wrap,
109         .unwrap         = ads_sasl_ntlmssp_unwrap,
110         .disconnect     = ads_sasl_ntlmssp_disconnect
111 };
112
113 /* 
114    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
115    we fit on one socket??)
116 */
117 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
118 {
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;
124         int rc;
125         NTSTATUS nt_status;
126         int turn = 1;
127         uint32 features = 0;
128
129         struct ntlmssp_state *ntlmssp_state;
130
131         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
132                 return ADS_ERROR_NT(nt_status);
133         }
134         ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
135
136         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
137                 return ADS_ERROR_NT(nt_status);
138         }
139         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
140                 return ADS_ERROR_NT(nt_status);
141         }
142         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
143                 return ADS_ERROR_NT(nt_status);
144         }
145
146         switch (ads->ldap.wrap_type) {
147         case ADS_SASLWRAP_TYPE_SEAL:
148                 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
149                 break;
150         case ADS_SASLWRAP_TYPE_SIGN:
151                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
152                         features = NTLMSSP_FEATURE_SIGN;
153                 } else {
154                         /*
155                          * windows servers are broken with sign only,
156                          * so we need to use seal here too
157                          */
158                         features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
159                         ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
160                 }
161                 break;
162         case ADS_SASLWRAP_TYPE_PLAIN:
163                 break;
164         }
165
166         ntlmssp_want_feature(ntlmssp_state, features);
167
168         blob_in = data_blob_null;
169
170         do {
171                 nt_status = ntlmssp_update(ntlmssp_state, 
172                                            blob_in, &blob_out);
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) {
177                         if (turn == 1) {
178                                 /* and wrap it in a SPNEGO wrapper */
179                                 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
180                         } else {
181                                 /* wrap it in SPNEGO */
182                                 msg1 = spnego_gen_auth(blob_out);
183                         }
184
185                         data_blob_free(&blob_out);
186
187                         cred.bv_val = (char *)msg1.data;
188                         cred.bv_len = msg1.length;
189                         scred = NULL;
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)) {
193                                 if (scred) {
194                                         ber_bvfree(scred);
195                                 }
196
197                                 ntlmssp_end(&ntlmssp_state);
198                                 return ADS_ERROR(rc);
199                         }
200                         if (scred) {
201                                 blob = data_blob(scred->bv_val, scred->bv_len);
202                                 ber_bvfree(scred);
203                         } else {
204                                 blob = data_blob_null;
205                         }
206
207                 } else {
208
209                         ntlmssp_end(&ntlmssp_state);
210                         data_blob_free(&blob_out);
211                         return ADS_ERROR_NT(nt_status);
212                 }
213                 
214                 if ((turn == 1) && 
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, 
219                                                     &tmp_blob)) {
220
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);
225                         }
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, 
229                                                         &blob_in)) {
230
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);
235                         }
236                 }
237                 data_blob_free(&blob);
238                 data_blob_free(&blob_out);
239                 turn++;
240         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
241         
242         /* we have a reference conter on ntlmssp_state, if we are signing
243            then the state will be kept by the signing engine */
244
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);
252         } else {
253                 ntlmssp_end(&ntlmssp_state);
254         }
255
256         return ADS_ERROR(rc);
257 }
258
259 #ifdef HAVE_KRB5
260 /* 
261    perform a LDAP/SASL/SPNEGO/KRB5 bind
262 */
263 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
264 {
265         DATA_BLOB blob = data_blob_null;
266         struct berval cred, *scred = NULL;
267         DATA_BLOB session_key = data_blob_null;
268         int rc;
269
270         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
271                                      &ads->auth.tgs_expire);
272
273         if (rc) {
274                 return ADS_ERROR_KRB5(rc);
275         }
276
277         /* now send the auth packet and we should be done */
278         cred.bv_val = (char *)blob.data;
279         cred.bv_len = blob.length;
280
281         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
282
283         data_blob_free(&blob);
284         data_blob_free(&session_key);
285         if(scred)
286                 ber_bvfree(scred);
287
288         return ADS_ERROR(rc);
289 }
290 #endif
291
292 /* 
293    this performs a SASL/SPNEGO bind
294 */
295 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
296 {
297         struct berval *scred=NULL;
298         int rc, i;
299         ADS_STATUS status;
300         DATA_BLOB blob;
301         char *principal = NULL;
302         char *OIDs[ASN1_MAX_OIDS];
303 #ifdef HAVE_KRB5
304         BOOL got_kerberos_mechanism = False;
305 #endif
306
307         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
308
309         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
310                 status = ADS_ERROR(rc);
311                 goto failed;
312         }
313
314         blob = data_blob(scred->bv_val, scred->bv_len);
315
316         ber_bvfree(scred);
317
318 #if 0
319         file_save("sasl_spnego.dat", blob.data, blob.length);
320 #endif
321
322         /* the server sent us the first part of the SPNEGO exchange in the negprot 
323            reply */
324         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
325                 data_blob_free(&blob);
326                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
327                 goto failed;
328         }
329         data_blob_free(&blob);
330
331         /* make sure the server understands kerberos */
332         for (i=0;OIDs[i];i++) {
333                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
334 #ifdef HAVE_KRB5
335                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
336                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
337                         got_kerberos_mechanism = True;
338                 }
339 #endif
340                 free(OIDs[i]);
341         }
342         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
343
344 #ifdef HAVE_KRB5
345         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
346             got_kerberos_mechanism) 
347         {
348                 /* I've seen a child Windows 2000 domain not send 
349                    the principal name back in the first round of 
350                    the SASL bind reply.  So we guess based on server
351                    name and realm.  --jerry  */
352                 if ( !principal ) {
353                         if ( ads->server.realm && ads->server.ldap_server ) {
354                                 char *server, *server_realm;
355                                 
356                                 server = SMB_STRDUP( ads->server.ldap_server );
357                                 server_realm = SMB_STRDUP( ads->server.realm );
358                                 
359                                 if ( !server || !server_realm )
360                                         return ADS_ERROR(LDAP_NO_MEMORY);
361
362                                 strlower_m( server );
363                                 strupper_m( server_realm );                             
364                                 asprintf( &principal, "ldap/%s@%s", server, server_realm );
365
366                                 SAFE_FREE( server );
367                                 SAFE_FREE( server_realm );
368
369                                 if ( !principal )
370                                         return ADS_ERROR(LDAP_NO_MEMORY);                               
371                         }
372                         
373                 }
374                 
375                 status = ads_sasl_spnego_krb5_bind(ads, principal);
376                 if (ADS_ERR_OK(status)) {
377                         SAFE_FREE(principal);
378                         return status;
379                 }
380
381                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
382                           "calling kinit\n", ads_errstr(status)));
383
384                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
385
386                 if (ADS_ERR_OK(status)) {
387                         status = ads_sasl_spnego_krb5_bind(ads, principal);
388                 }
389
390                 /* only fallback to NTLMSSP if allowed */
391                 if (ADS_ERR_OK(status) || 
392                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
393                         SAFE_FREE(principal);
394                         return status;
395                 }
396         }
397 #endif
398
399         SAFE_FREE(principal);
400
401         /* lets do NTLMSSP ... this has the big advantage that we don't need
402            to sync clocks, and we don't rely on special versions of the krb5 
403            library for HMAC_MD4 encryption */
404         return ads_sasl_spnego_ntlmssp_bind(ads);
405
406 failed:
407         return status;
408 }
409
410 #ifdef HAVE_GSSAPI
411 #define MAX_GSS_PASSES 3
412
413 /* this performs a SASL/gssapi bind
414    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
415    is very dependent on correctly configured DNS whereas
416    this routine is much less fragile
417    see RFC2078 and RFC2222 for details
418 */
419 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
420 {
421         uint32 minor_status;
422         gss_name_t serv_name;
423         gss_buffer_desc input_name;
424         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
425         gss_OID mech_type = GSS_C_NULL_OID;
426         gss_buffer_desc output_token, input_token;
427         uint32 ret_flags, conf_state;
428         struct berval cred;
429         struct berval *scred = NULL;
430         int i=0;
431         int gss_rc, rc;
432         uint8 *p;
433         uint32 max_msg_size = 0;
434         char *sname = NULL;
435         ADS_STATUS status;
436         krb5_principal principal = NULL;
437         krb5_context ctx = NULL;
438         krb5_enctype enc_types[] = {
439 #ifdef ENCTYPE_ARCFOUR_HMAC
440                         ENCTYPE_ARCFOUR_HMAC,
441 #endif
442                         ENCTYPE_DES_CBC_MD5,
443                         ENCTYPE_NULL};
444         gss_OID_desc nt_principal = 
445         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
446
447         /* we need to fetch a service ticket as the ldap user in the
448            servers realm, regardless of our realm */
449         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
450
451         initialize_krb5_error_table();
452         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
453         if (!ADS_ERR_OK(status)) {
454                 SAFE_FREE(sname);
455                 return status;
456         }
457         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
458         if (!ADS_ERR_OK(status)) {
459                 SAFE_FREE(sname);
460                 krb5_free_context(ctx); 
461                 return status;
462         }
463         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
464         if (!ADS_ERR_OK(status)) {
465                 SAFE_FREE(sname);
466                 krb5_free_context(ctx); 
467                 return status;
468         }
469
470         input_name.value = &principal;
471         input_name.length = sizeof(principal);
472
473         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
474
475         /*
476          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
477          * to point to the *address* of the krb5_principal, and the gss libraries
478          * to a shallow copy of the krb5_principal pointer - so we need to keep
479          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
480          * Just one more way in which MIT engineers screwed me over.... JRA.
481          */
482
483         SAFE_FREE(sname);
484
485         if (gss_rc) {
486                 krb5_free_principal(ctx, principal);
487                 krb5_free_context(ctx); 
488                 return ADS_ERROR_GSS(gss_rc, minor_status);
489         }
490
491         input_token.value = NULL;
492         input_token.length = 0;
493
494         for (i=0; i < MAX_GSS_PASSES; i++) {
495                 gss_rc = gss_init_sec_context(&minor_status,
496                                           GSS_C_NO_CREDENTIAL,
497                                           &context_handle,
498                                           serv_name,
499                                           mech_type,
500                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
501                                           0,
502                                           NULL,
503                                           &input_token,
504                                           NULL,
505                                           &output_token,
506                                           &ret_flags,
507                                           NULL);
508
509                 if (input_token.value) {
510                         gss_release_buffer(&minor_status, &input_token);
511                 }
512
513                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
514                         status = ADS_ERROR_GSS(gss_rc, minor_status);
515                         goto failed;
516                 }
517
518                 cred.bv_val = (char *)output_token.value;
519                 cred.bv_len = output_token.length;
520
521                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
522                                       &scred);
523                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
524                         status = ADS_ERROR(rc);
525                         goto failed;
526                 }
527
528                 if (output_token.value) {
529                         gss_release_buffer(&minor_status, &output_token);
530                 }
531
532                 if (scred) {
533                         input_token.value = scred->bv_val;
534                         input_token.length = scred->bv_len;
535                 } else {
536                         input_token.value = NULL;
537                         input_token.length = 0;
538                 }
539
540                 if (gss_rc == 0) break;
541         }
542
543         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
544                             (int *)&conf_state,NULL);
545         if (gss_rc) {
546                 status = ADS_ERROR_GSS(gss_rc, minor_status);
547                 goto failed;
548         }
549
550         gss_release_buffer(&minor_status, &input_token);
551
552         p = (uint8 *)output_token.value;
553
554 #if 0
555         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
556 #endif
557
558         if (p) {
559                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
560         }
561
562         gss_release_buffer(&minor_status, &output_token);
563
564         output_token.length = 4;
565         output_token.value = SMB_MALLOC(output_token.length);
566         p = (uint8 *)output_token.value;
567
568         *p++ = 1; /* no sign & seal selection */
569         /* choose the same size as the server gave us */
570         *p++ = max_msg_size>>16;
571         *p++ = max_msg_size>>8;
572         *p++ = max_msg_size;
573         /*
574          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
575          * but using ads->config.bind_path is the wrong! It should be
576          * the DN of the user object!
577          *
578          * w2k3 gives an error when we send an incorrect DN, but sending nothing
579          * is ok and matches the information flow used in GSS-SPNEGO.
580          */
581
582         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
583                           &output_token, (int *)&conf_state,
584                           &input_token);
585         if (gss_rc) {
586                 status = ADS_ERROR_GSS(gss_rc, minor_status);
587                 goto failed;
588         }
589
590         free(output_token.value);
591
592         cred.bv_val = (char *)input_token.value;
593         cred.bv_len = input_token.length;
594
595         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
596                               &scred);
597         status = ADS_ERROR(rc);
598
599         gss_release_buffer(&minor_status, &input_token);
600
601 failed:
602
603         gss_release_name(&minor_status, &serv_name);
604         if (context_handle != GSS_C_NO_CONTEXT)
605                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
606         krb5_free_principal(ctx, principal);
607         krb5_free_context(ctx); 
608
609         if(scred)
610                 ber_bvfree(scred);
611         return status;
612 }
613 #endif /* HAVE_GGSAPI */
614
615 /* mapping between SASL mechanisms and functions */
616 static struct {
617         const char *name;
618         ADS_STATUS (*fn)(ADS_STRUCT *);
619 } sasl_mechanisms[] = {
620         {"GSS-SPNEGO", ads_sasl_spnego_bind},
621 #ifdef HAVE_GSSAPI
622         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
623 #endif
624         {NULL, NULL}
625 };
626
627 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
628 {
629         const char *attrs[] = {"supportedSASLMechanisms", NULL};
630         char **values;
631         ADS_STATUS status;
632         int i, j;
633         LDAPMessage *res;
634
635         /* get a list of supported SASL mechanisms */
636         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
637         if (!ADS_ERR_OK(status)) return status;
638
639         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
640
641         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
642                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
643         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
644                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
645         } else {
646                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
647         }
648
649         /* try our supported mechanisms in order */
650         for (i=0;sasl_mechanisms[i].name;i++) {
651                 /* see if the server supports it */
652                 for (j=0;values && values[j];j++) {
653                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
654                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
655                                 status = sasl_mechanisms[i].fn(ads);
656                                 ldap_value_free(values);
657                                 ldap_msgfree(res);
658                                 return status;
659                         }
660                 }
661         }
662
663         ldap_value_free(values);
664         ldap_msgfree(res);
665         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
666 }
667
668 #endif /* HAVE_LDAP */
669