TODO: s3-libads: Pass cli_credentials to ads_sasl_spnego_gensec_bind()
[metze/samba/wip.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 #include "../libcli/auth/spnego.h"
22 #include "auth/credentials/credentials.h"
23 #include "auth/gensec/gensec.h"
24 #include "auth_generic.h"
25 #include "ads.h"
26 #include "smb_krb5.h"
27 #include "system/gssapi.h"
28 #include "lib/param/param.h"
29 #include "krb5_env.h"
30
31 static struct cli_credentials *ads_sasl_creds_init(TALLOC_CTX *mem_ctx,
32                                                    const char *username,
33                                                    const char *password,
34                                                    const char *domain,
35                                                    const char *realm,
36                                                    enum credentials_use_kerberos krb5_state)
37 {
38         struct loadparm_context *lp_ctx;
39         struct cli_credentials *creds;
40         bool ok;
41
42         creds = cli_credentials_init(mem_ctx);
43         if (creds == NULL) {
44                 return NULL;
45         }
46
47         lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
48         if (lp_ctx == NULL) {
49                 goto fail;
50         }
51         cli_credentials_set_conf(creds, lp_ctx);
52
53         if (username != NULL) {
54                 ok = cli_credentials_set_username(creds,
55                                                   username,
56                                                   CRED_SPECIFIED);
57                 if (!ok) {
58                         goto fail;
59                 }
60         }
61
62         if (password != NULL) {
63                 ok = cli_credentials_set_password(creds,
64                                                   password,
65                                                   CRED_SPECIFIED);
66                 if (!ok) {
67                         goto fail;
68                 }
69         }
70
71         if (domain != NULL) {
72                 ok = cli_credentials_set_domain(creds,
73                                                 domain,
74                                                 CRED_SPECIFIED);
75                 if (!ok) {
76                         goto fail;
77                 }
78         }
79
80         if (realm != NULL) {
81                 ok = cli_credentials_set_realm(creds,
82                                                realm,
83                                                CRED_SPECIFIED);
84                 if (!ok) {
85                         goto fail;
86                 }
87         }
88
89         cli_credentials_set_kerberos_state(creds, krb5_state);
90
91         return creds;
92 fail:
93         TALLOC_FREE(creds);
94         return NULL;
95 }
96
97 #ifdef HAVE_LDAP
98
99 static ADS_STATUS ads_sasl_gensec_wrap(struct ads_saslwrap *wrap,
100                                        uint8_t *buf, uint32_t len)
101 {
102         struct gensec_security *gensec_security =
103                 talloc_get_type_abort(wrap->wrap_private_data,
104                 struct gensec_security);
105         NTSTATUS nt_status;
106         DATA_BLOB unwrapped, wrapped;
107         TALLOC_CTX *frame = talloc_stackframe();
108
109         unwrapped = data_blob_const(buf, len);
110
111         nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
112         if (!NT_STATUS_IS_OK(nt_status)) {
113                 TALLOC_FREE(frame);
114                 return ADS_ERROR_NT(nt_status);
115         }
116
117         if ((wrap->out.size - 4) < wrapped.length) {
118                 TALLOC_FREE(frame);
119                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
120         }
121
122         /* copy the wrapped blob to the right location */
123         memcpy(wrap->out.buf + 4, wrapped.data, wrapped.length);
124
125         /* set how many bytes must be written to the underlying socket */
126         wrap->out.left = 4 + wrapped.length;
127
128         TALLOC_FREE(frame);
129
130         return ADS_SUCCESS;
131 }
132
133 static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap)
134 {
135         struct gensec_security *gensec_security =
136                 talloc_get_type_abort(wrap->wrap_private_data,
137                 struct gensec_security);
138         NTSTATUS nt_status;
139         DATA_BLOB unwrapped, wrapped;
140         TALLOC_CTX *frame = talloc_stackframe();
141
142         wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4);
143
144         nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
145         if (!NT_STATUS_IS_OK(nt_status)) {
146                 TALLOC_FREE(frame);
147                 return ADS_ERROR_NT(nt_status);
148         }
149
150         if (wrapped.length < unwrapped.length) {
151                 TALLOC_FREE(frame);
152                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
153         }
154
155         /* copy the wrapped blob to the right location */
156         memcpy(wrap->in.buf + 4, unwrapped.data, unwrapped.length);
157
158         /* set how many bytes must be written to the underlying socket */
159         wrap->in.left   = unwrapped.length;
160         wrap->in.ofs    = 4;
161
162         TALLOC_FREE(frame);
163
164         return ADS_SUCCESS;
165 }
166
167 static void ads_sasl_gensec_disconnect(struct ads_saslwrap *wrap)
168 {
169         struct gensec_security *gensec_security =
170                 talloc_get_type_abort(wrap->wrap_private_data,
171                 struct gensec_security);
172
173         TALLOC_FREE(gensec_security);
174
175         wrap->wrap_ops = NULL;
176         wrap->wrap_private_data = NULL;
177 }
178
179 static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
180         .name           = "gensec",
181         .wrap           = ads_sasl_gensec_wrap,
182         .unwrap         = ads_sasl_gensec_unwrap,
183         .disconnect     = ads_sasl_gensec_disconnect
184 };
185
186 /* 
187    perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can
188    we fit on one socket??)
189 */
190 static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
191                                 const char *sasl,
192                                 struct cli_credentials *creds,
193                                 const char *target_service,
194                                 const char *target_hostname,
195                                 const DATA_BLOB server_blob)
196 {
197         DATA_BLOB blob_in = data_blob_null;
198         DATA_BLOB blob_out = data_blob_null;
199         int rc;
200         NTSTATUS nt_status;
201         ADS_STATUS status;
202         struct auth_generic_state *auth_generic_state;
203         bool use_spnego_principal = lp_client_use_spnego_principal();
204         const char *sasl_list[] = { sasl, NULL };
205         enum credentials_use_kerberos krb5_state;
206         NTTIME end_nt_time;
207         struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
208
209         nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
210         if (!NT_STATUS_IS_OK(nt_status)) {
211                 return ADS_ERROR_NT(nt_status);
212         }
213
214         nt_status = auth_generic_set_creds(auth_generic_state, creds);
215         if (!NT_STATUS_IS_OK(nt_status)) {
216                 return ADS_ERROR_NT(nt_status);
217         }
218
219         if (server_blob.length == 0) {
220                 use_spnego_principal = false;
221         }
222
223         krb5_state = cli_credentials_get_kerberos_state(creds);
224         if (krb5_state == CRED_DONT_USE_KERBEROS) {
225                 use_spnego_principal = false;
226         }
227
228         if (target_service != NULL) {
229                 nt_status = gensec_set_target_service(
230                                         auth_generic_state->gensec_security,
231                                         target_service);
232                 if (!NT_STATUS_IS_OK(nt_status)) {
233                         return ADS_ERROR_NT(nt_status);
234                 }
235         }
236
237         if (target_hostname != NULL) {
238                 nt_status = gensec_set_target_hostname(
239                                         auth_generic_state->gensec_security,
240                                         target_hostname);
241                 if (!NT_STATUS_IS_OK(nt_status)) {
242                         return ADS_ERROR_NT(nt_status);
243                 }
244         }
245
246         if (target_service != NULL && target_hostname != NULL) {
247                 use_spnego_principal = false;
248         }
249
250         switch (wrap->wrap_type) {
251         case ADS_SASLWRAP_TYPE_SEAL:
252                 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
253                 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
254                 break;
255         case ADS_SASLWRAP_TYPE_SIGN:
256                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
257                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
258                 } else {
259                         /*
260                          * windows servers are broken with sign only,
261                          * so we let the NTLMSSP backend to seal here,
262                          * via GENSEC_FEATURE_LDAP_STYLE.
263                          */
264                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
265                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE);
266                 }
267                 break;
268         case ADS_SASLWRAP_TYPE_PLAIN:
269                 break;
270         }
271
272         nt_status = auth_generic_client_start_by_sasl(auth_generic_state,
273                                                       sasl_list);
274         if (!NT_STATUS_IS_OK(nt_status)) {
275                 return ADS_ERROR_NT(nt_status);
276         }
277
278         rc = LDAP_SASL_BIND_IN_PROGRESS;
279         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
280         if (use_spnego_principal) {
281                 blob_in = data_blob_dup_talloc(talloc_tos(), server_blob);
282                 if (blob_in.length == 0) {
283                         TALLOC_FREE(auth_generic_state);
284                         return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
285                 }
286         } else {
287                 blob_in = data_blob_null;
288         }
289         blob_out = data_blob_null;
290
291         while (true) {
292                 struct berval cred, *scred = NULL;
293
294                 nt_status = gensec_update(auth_generic_state->gensec_security,
295                                           talloc_tos(), blob_in, &blob_out);
296                 data_blob_free(&blob_in);
297                 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
298                     && !NT_STATUS_IS_OK(nt_status))
299                 {
300                         TALLOC_FREE(auth_generic_state);
301                         data_blob_free(&blob_out);
302                         return ADS_ERROR_NT(nt_status);
303                 }
304
305                 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) {
306                         break;
307                 }
308
309                 cred.bv_val = (char *)blob_out.data;
310                 cred.bv_len = blob_out.length;
311                 scred = NULL;
312                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred);
313                 data_blob_free(&blob_out);
314                 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
315                         if (scred) {
316                                 ber_bvfree(scred);
317                         }
318
319                         TALLOC_FREE(auth_generic_state);
320                         return ADS_ERROR(rc);
321                 }
322                 if (scred) {
323                         blob_in = data_blob_talloc(talloc_tos(),
324                                                    scred->bv_val,
325                                                    scred->bv_len);
326                         if (blob_in.length != scred->bv_len) {
327                                 ber_bvfree(scred);
328                                 TALLOC_FREE(auth_generic_state);
329                                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
330                         }
331                         ber_bvfree(scred);
332                 } else {
333                         blob_in = data_blob_null;
334                 }
335                 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) {
336                         break;
337                 }
338         }
339
340         data_blob_free(&blob_in);
341         data_blob_free(&blob_out);
342
343         if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) {
344                 bool ok;
345
346                 ok = gensec_have_feature(auth_generic_state->gensec_security,
347                                          GENSEC_FEATURE_SEAL);
348                 if (!ok) {
349                         DEBUG(0,("The gensec feature sealing request, but unavailable\n"));
350                         TALLOC_FREE(auth_generic_state);
351                         return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
352                 }
353
354                 ok = gensec_have_feature(auth_generic_state->gensec_security,
355                                          GENSEC_FEATURE_SIGN);
356                 if (!ok) {
357                         DEBUG(0,("The gensec feature signing request, but unavailable\n"));
358                         TALLOC_FREE(auth_generic_state);
359                         return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
360                 }
361
362         } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) {
363                 bool ok;
364
365                 ok = gensec_have_feature(auth_generic_state->gensec_security,
366                                          GENSEC_FEATURE_SIGN);
367                 if (!ok) {
368                         DEBUG(0,("The gensec feature signing request, but unavailable\n"));
369                         TALLOC_FREE(auth_generic_state);
370                         return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
371                 }
372         }
373
374         ads->auth.tgs_expire = LONG_MAX;
375         end_nt_time = gensec_expire_time(auth_generic_state->gensec_security);
376         if (end_nt_time != GENSEC_EXPIRE_TIME_INFINITY) {
377                 struct timeval tv;
378                 nttime_to_timeval(&tv, end_nt_time);
379                 ads->auth.tgs_expire = tv.tv_sec;
380         }
381
382         if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
383                 size_t max_wrapped =
384                         gensec_max_wrapped_size(auth_generic_state->gensec_security);
385                 wrap->out.max_unwrapped =
386                         gensec_max_input_size(auth_generic_state->gensec_security);
387
388                 wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped;
389                 /*
390                  * Note that we have to truncate this to 0x2C
391                  * (taken from a capture with LDAP unbind), as the
392                  * signature size is not constant for Kerberos with
393                  * arcfour-hmac-md5.
394                  */
395                 wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C);
396                 wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
397                 status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld,
398                                                  &ads_sasl_gensec_ops,
399                                                  auth_generic_state->gensec_security);
400                 if (!ADS_ERR_OK(status)) {
401                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
402                                 ads_errstr(status)));
403                         TALLOC_FREE(auth_generic_state);
404                         return status;
405                 }
406                 /* Only keep the gensec_security element around long-term */
407                 talloc_steal(NULL, auth_generic_state->gensec_security);
408         }
409         TALLOC_FREE(auth_generic_state);
410
411         return ADS_ERROR(rc);
412 }
413
414 #ifdef HAVE_KRB5
415 struct ads_service_principal {
416         char *service;
417         char *hostname;
418         char *string;
419 #ifdef HAVE_KRB5
420         gss_name_t name;
421 #endif
422 };
423
424 static void ads_free_service_principal(struct ads_service_principal *p)
425 {
426         SAFE_FREE(p->service);
427         SAFE_FREE(p->hostname);
428         SAFE_FREE(p->string);
429
430 #ifdef HAVE_KRB5
431         if (p->name) {
432                 uint32_t minor_status;
433                 gss_release_name(&minor_status, &p->name);
434         }
435 #endif
436         ZERO_STRUCTP(p);
437 }
438
439 static ADS_STATUS ads_guess_target(ADS_STRUCT *ads,
440                                    char **service,
441                                    char **hostname,
442                                    char **principal)
443 {
444         ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
445         char *princ = NULL;
446         TALLOC_CTX *frame;
447         char *server = NULL;
448         char *realm = NULL;
449         int rc;
450
451         frame = talloc_stackframe();
452         if (frame == NULL) {
453                 return ADS_ERROR(LDAP_NO_MEMORY);
454         }
455
456         if (ads->server.realm && ads->server.ldap_server) {
457                 server = strlower_talloc(frame, ads->server.ldap_server);
458                 if (server == NULL) {
459                         goto out;
460                 }
461
462                 realm = strupper_talloc(frame, ads->server.realm);
463                 if (realm == NULL) {
464                         goto out;
465                 }
466
467                 /*
468                  * If we got a name which is bigger than a NetBIOS name,
469                  * but isn't a FQDN, create one.
470                  */
471                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
472                         char *dnsdomain;
473
474                         dnsdomain = strlower_talloc(frame, ads->server.realm);
475                         if (dnsdomain == NULL) {
476                                 goto out;
477                         }
478
479                         server = talloc_asprintf(frame,
480                                                  "%s.%s",
481                                                  server, dnsdomain);
482                         if (server == NULL) {
483                                 goto out;
484                         }
485                 }
486         } else if (ads->config.realm && ads->config.ldap_server_name) {
487                 server = strlower_talloc(frame, ads->config.ldap_server_name);
488                 if (server == NULL) {
489                         goto out;
490                 }
491
492                 realm = strupper_talloc(frame, ads->config.realm);
493                 if (realm == NULL) {
494                         goto out;
495                 }
496
497                 /*
498                  * If we got a name which is bigger than a NetBIOS name,
499                  * but isn't a FQDN, create one.
500                  */
501                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
502                         char *dnsdomain;
503
504                         dnsdomain = strlower_talloc(frame, ads->server.realm);
505                         if (dnsdomain == NULL) {
506                                 goto out;
507                         }
508
509                         server = talloc_asprintf(frame,
510                                                  "%s.%s",
511                                                  server, dnsdomain);
512                         if (server == NULL) {
513                                 goto out;
514                         }
515                 }
516         }
517
518         if (server == NULL || realm == NULL) {
519                 goto out;
520         }
521
522         *service = SMB_STRDUP("ldap");
523         if (*service == NULL) {
524                 status = ADS_ERROR(LDAP_PARAM_ERROR);
525                 goto out;
526         }
527         *hostname = SMB_STRDUP(server);
528         if (*hostname == NULL) {
529                 SAFE_FREE(*service);
530                 status = ADS_ERROR(LDAP_PARAM_ERROR);
531                 goto out;
532         }
533         rc = asprintf(&princ, "ldap/%s@%s", server, realm);
534         if (rc == -1 || princ == NULL) {
535                 SAFE_FREE(*service);
536                 SAFE_FREE(*hostname);
537                 status = ADS_ERROR(LDAP_PARAM_ERROR);
538                 goto out;
539         }
540
541         *principal = princ;
542
543         status = ADS_SUCCESS;
544 out:
545         TALLOC_FREE(frame);
546         return status;
547 }
548
549 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
550                                                  struct ads_service_principal *p)
551 {
552         ADS_STATUS status;
553 #ifdef HAVE_KRB5
554         gss_buffer_desc input_name;
555         /* GSS_KRB5_NT_PRINCIPAL_NAME */
556         gss_OID_desc nt_principal =
557         {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
558         uint32_t minor_status;
559         int gss_rc;
560 #endif
561
562         ZERO_STRUCTP(p);
563
564         status = ads_guess_target(ads,
565                                   &p->service,
566                                   &p->hostname,
567                                   &p->string);
568         if (!ADS_ERR_OK(status)) {
569                 return status;
570         }
571
572 #ifdef HAVE_KRB5
573         input_name.value = p->string;
574         input_name.length = strlen(p->string);
575
576         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
577         if (gss_rc) {
578                 ads_free_service_principal(p);
579                 return ADS_ERROR_GSS(gss_rc, minor_status);
580         }
581 #endif
582
583         return ADS_SUCCESS;
584 }
585
586 #endif /* HAVE_KRB5 */
587
588 /* 
589    this performs a SASL/SPNEGO bind
590 */
591 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
592 {
593         TALLOC_CTX *frame = talloc_stackframe();
594         struct ads_service_principal p = {0};
595         ADS_STATUS status;
596         enum credentials_use_kerberos krb5_state;
597         struct cli_credentials *creds;
598
599         if (ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) {
600                 krb5_state = CRED_DONT_USE_KERBEROS;
601         } else if (ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP) {
602                 krb5_state = CRED_AUTO_USE_KERBEROS;
603         } else {
604                 krb5_state = CRED_MUST_USE_KERBEROS;
605         }
606
607         status = ads_generate_service_principal(ads, &p);
608         if (!ADS_ERR_OK(status)) {
609                 goto done;
610         }
611
612         creds = ads_sasl_creds_init(frame,
613                                     ads->auth.user_name,
614                                     ads->auth.password,
615                                     ads->auth.realm,
616                                     ads->auth.realm,
617                                     krb5_state);
618         if (creds == NULL) {
619                 status = ADS_ERROR_SYSTEM(ENOMEM);
620                 goto done;
621         }
622
623         status = ads_sasl_spnego_gensec_bind(ads,
624                                              "GSS-SPNEGO",
625                                              creds,
626                                              p.service,
627                                              p.hostname,
628                                              data_blob_null);
629
630 done:
631         if (!ADS_ERR_OK(status)) {
632                 DEBUG(1,("ads_sasl_spnego_gensec_bind(GSS-SPNEGO) failed "
633                          "for %s/%s with user[%s] realm=[%s]: %s\n",
634                           p.service, p.hostname,
635                           ads->auth.user_name,
636                           ads->auth.realm,
637                           ads_errstr(status)));
638         }
639         ads_free_service_principal(&p);
640         TALLOC_FREE(frame);
641         return status;
642 }
643
644 #ifdef HAVE_KRB5
645
646 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
647 {
648         TALLOC_CTX *frame = talloc_stackframe();
649         ADS_STATUS status;
650         struct ads_service_principal p;
651         struct cli_credentials *creds;
652
653         status = ads_generate_service_principal(ads, &p);
654         if (!ADS_ERR_OK(status)) {
655                 goto done;
656         }
657
658         creds = ads_sasl_creds_init(frame,
659                                     ads->auth.user_name,
660                                     ads->auth.password,
661                                     ads->auth.realm,
662                                     ads->auth.realm,
663                                     CRED_MUST_USE_KERBEROS);
664         if (creds == NULL) {
665                 status = ADS_ERROR_SYSTEM(ENOMEM);
666                 goto done;
667         }
668
669         status = ads_sasl_spnego_gensec_bind(ads,
670                                              "GSSAPI",
671                                              creds,
672                                              p.service,
673                                              p.hostname,
674                                              data_blob_null);
675
676 done:
677         ads_free_service_principal(&p);
678         TALLOC_FREE(frame);
679         return status;
680 }
681
682 #endif /* HAVE_KRB5 */
683
684 /* mapping between SASL mechanisms and functions */
685 static struct {
686         const char *name;
687         ADS_STATUS (*fn)(ADS_STRUCT *);
688 } sasl_mechanisms[] = {
689         {"GSS-SPNEGO", ads_sasl_spnego_bind},
690 #ifdef HAVE_KRB5
691         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
692 #endif
693         {NULL, NULL}
694 };
695
696 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
697 {
698         const char *attrs[] = {"supportedSASLMechanisms", NULL};
699         char **values;
700         ADS_STATUS status;
701         int i, j;
702         LDAPMessage *res;
703         struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
704
705         /* get a list of supported SASL mechanisms */
706         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
707         if (!ADS_ERR_OK(status)) return status;
708
709         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
710
711         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
712                 wrap->wrap_type = ADS_SASLWRAP_TYPE_SEAL;
713         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
714                 wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
715         } else {
716                 wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
717         }
718
719         /* try our supported mechanisms in order */
720         for (i=0;sasl_mechanisms[i].name;i++) {
721                 /* see if the server supports it */
722                 for (j=0;values && values[j];j++) {
723                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
724                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
725 retry:
726                                 status = sasl_mechanisms[i].fn(ads);
727                                 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
728                                     status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
729                                     wrap->wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
730                                 {
731                                         DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
732                                                  "retrying with signing enabled\n"));
733                                         wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
734                                         goto retry;
735                                 }
736                                 ldap_value_free(values);
737                                 ldap_msgfree(res);
738                                 return status;
739                         }
740                 }
741         }
742
743         ldap_value_free(values);
744         ldap_msgfree(res);
745         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
746 }
747
748 #endif /* HAVE_LDAP */
749