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