s3:winbindd: try to use the trust account with kerberos if possible
[obnox/samba/samba-obnox.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/gensec/gensec.h"
23 #include "auth_generic.h"
24 #include "ads.h"
25 #include "smb_krb5.h"
26 #include "system/gssapi.h"
27 #include "lib/param/loadparm.h"
28
29 #ifdef HAVE_LDAP
30
31 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
32 {
33         struct gensec_security *gensec_security =
34                 talloc_get_type_abort(ads->ldap.wrap_private_data,
35                 struct gensec_security);
36         NTSTATUS nt_status;
37         DATA_BLOB unwrapped, wrapped;
38         TALLOC_CTX *frame = talloc_stackframe();
39
40         unwrapped = data_blob_const(buf, len);
41
42         nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
43         if (!NT_STATUS_IS_OK(nt_status)) {
44                 TALLOC_FREE(frame);
45                 return ADS_ERROR_NT(nt_status);
46         }
47
48         if ((ads->ldap.out.size - 4) < wrapped.length) {
49                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
50         }
51
52         /* copy the wrapped blob to the right location */
53         memcpy(ads->ldap.out.buf + 4, wrapped.data, wrapped.length);
54
55         /* set how many bytes must be written to the underlying socket */
56         ads->ldap.out.left = 4 + wrapped.length;
57
58         TALLOC_FREE(frame);
59
60         return ADS_SUCCESS;
61 }
62
63 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
64 {
65         struct gensec_security *gensec_security =
66                 talloc_get_type_abort(ads->ldap.wrap_private_data,
67                 struct gensec_security);
68         NTSTATUS nt_status;
69         DATA_BLOB unwrapped, wrapped;
70         TALLOC_CTX *frame = talloc_stackframe();
71
72         wrapped = data_blob_const(ads->ldap.in.buf + 4, ads->ldap.in.ofs - 4);
73
74         nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
75         if (!NT_STATUS_IS_OK(nt_status)) {
76                 TALLOC_FREE(frame);
77                 return ADS_ERROR_NT(nt_status);
78         }
79
80         if (wrapped.length < unwrapped.length) {
81                 TALLOC_FREE(frame);
82                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
83         }
84
85         /* copy the wrapped blob to the right location */
86         memcpy(ads->ldap.in.buf + 4, unwrapped.data, unwrapped.length);
87
88         /* set how many bytes must be written to the underlying socket */
89         ads->ldap.in.left       = unwrapped.length;
90         ads->ldap.in.ofs        = 4;
91
92         TALLOC_FREE(frame);
93
94         return ADS_SUCCESS;
95 }
96
97 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
98 {
99         struct gensec_security *gensec_security =
100                 talloc_get_type_abort(ads->ldap.wrap_private_data,
101                 struct gensec_security);
102
103         TALLOC_FREE(gensec_security);
104
105         ads->ldap.wrap_ops = NULL;
106         ads->ldap.wrap_private_data = NULL;
107 }
108
109 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
110         .name           = "ntlmssp",
111         .wrap           = ads_sasl_ntlmssp_wrap,
112         .unwrap         = ads_sasl_ntlmssp_unwrap,
113         .disconnect     = ads_sasl_ntlmssp_disconnect
114 };
115
116 /* 
117    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
118    we fit on one socket??)
119 */
120 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
121 {
122         DATA_BLOB msg1 = data_blob_null;
123         DATA_BLOB blob = data_blob_null;
124         DATA_BLOB blob_in = data_blob_null;
125         DATA_BLOB blob_out = data_blob_null;
126         struct berval cred, *scred = NULL;
127         int rc;
128         NTSTATUS nt_status;
129         ADS_STATUS status;
130         int turn = 1;
131
132         struct auth_generic_state *auth_generic_state;
133
134         nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
135         if (!NT_STATUS_IS_OK(nt_status)) {
136                 return ADS_ERROR_NT(nt_status);
137         }
138
139         if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) {
140                 return ADS_ERROR_NT(nt_status);
141         }
142         if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
143                 return ADS_ERROR_NT(nt_status);
144         }
145         if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
146                 return ADS_ERROR_NT(nt_status);
147         }
148
149         switch (ads->ldap.wrap_type) {
150         case ADS_SASLWRAP_TYPE_SEAL:
151                 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
152                 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
153                 break;
154         case ADS_SASLWRAP_TYPE_SIGN:
155                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
156                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
157                 } else {
158                         /*
159                          * windows servers are broken with sign only,
160                          * so we need to use seal here too
161                          */
162                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
163                         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
164                         ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
165                 }
166                 break;
167         case ADS_SASLWRAP_TYPE_PLAIN:
168                 break;
169         }
170
171         nt_status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
172         if (!NT_STATUS_IS_OK(nt_status)) {
173                 return ADS_ERROR_NT(nt_status);
174         }
175
176         blob_in = data_blob_null;
177
178         do {
179                 nt_status = gensec_update(auth_generic_state->gensec_security,
180                                           talloc_tos(), blob_in, &blob_out);
181                 data_blob_free(&blob_in);
182                 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
183                      || NT_STATUS_IS_OK(nt_status))
184                     && blob_out.length) {
185                         if (turn == 1) {
186                                 const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
187                                 /* and wrap it in a SPNEGO wrapper */
188                                 msg1 = spnego_gen_negTokenInit(talloc_tos(),
189                                                 OIDs_ntlm, &blob_out, NULL);
190                         } else {
191                                 /* wrap it in SPNEGO */
192                                 msg1 = spnego_gen_auth(talloc_tos(), blob_out);
193                         }
194
195                         data_blob_free(&blob_out);
196
197                         cred.bv_val = (char *)msg1.data;
198                         cred.bv_len = msg1.length;
199                         scred = NULL;
200                         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
201                         data_blob_free(&msg1);
202                         if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
203                                 if (scred) {
204                                         ber_bvfree(scred);
205                                 }
206
207                                 TALLOC_FREE(auth_generic_state);
208                                 return ADS_ERROR(rc);
209                         }
210                         if (scred) {
211                                 blob = data_blob(scred->bv_val, scred->bv_len);
212                                 ber_bvfree(scred);
213                         } else {
214                                 blob = data_blob_null;
215                         }
216
217                 } else {
218
219                         TALLOC_FREE(auth_generic_state);
220                         data_blob_free(&blob_out);
221                         return ADS_ERROR_NT(nt_status);
222                 }
223                 
224                 if ((turn == 1) && 
225                     (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
226                         DATA_BLOB tmp_blob = data_blob_null;
227                         /* the server might give us back two challenges */
228                         if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in, 
229                                                     &tmp_blob)) {
230
231                                 TALLOC_FREE(auth_generic_state);
232                                 data_blob_free(&blob);
233                                 DEBUG(3,("Failed to parse challenges\n"));
234                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
235                         }
236                         data_blob_free(&tmp_blob);
237                 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
238                         if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP, 
239                                                         &blob_in)) {
240
241                                 TALLOC_FREE(auth_generic_state);
242                                 data_blob_free(&blob);
243                                 DEBUG(3,("Failed to parse auth response\n"));
244                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
245                         }
246                 }
247                 data_blob_free(&blob);
248                 data_blob_free(&blob_out);
249                 turn++;
250         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
251         
252         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
253                 uint32_t sig_size = gensec_sig_size(auth_generic_state->gensec_security, 0);
254                 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size;
255                 ads->ldap.out.sig_size = sig_size;
256                 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
257                 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
258                 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, auth_generic_state->gensec_security);
259                 if (!ADS_ERR_OK(status)) {
260                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
261                                 ads_errstr(status)));
262                         TALLOC_FREE(auth_generic_state);
263                         return status;
264                 }
265                 /* Only keep the gensec_security element around long-term */
266                 talloc_steal(NULL, auth_generic_state->gensec_security);
267         }
268         TALLOC_FREE(auth_generic_state);
269
270         return ADS_ERROR(rc);
271 }
272
273 #ifdef HAVE_KRB5
274 static ADS_STATUS ads_init_gssapi_cred(ADS_STRUCT *ads, gss_cred_id_t *cred)
275 {
276         ADS_STATUS status;
277         krb5_context kctx;
278         krb5_error_code kerr;
279         krb5_ccache kccache = NULL;
280         uint32_t maj, min;
281
282         *cred = GSS_C_NO_CREDENTIAL;
283
284         if (!ads->auth.ccache_name) {
285                 return ADS_SUCCESS;
286         }
287
288         kerr = krb5_init_context(&kctx);
289         if (kerr) {
290                 return ADS_ERROR_KRB5(kerr);
291         }
292
293 #ifdef HAVE_GSS_KRB5_IMPORT_CRED
294         kerr = krb5_cc_resolve(kctx, ads->auth.ccache_name, &kccache);
295         if (kerr) {
296                 status = ADS_ERROR_KRB5(kerr);
297                 goto done;
298         }
299
300         maj = gss_krb5_import_cred(&min, kccache, NULL, NULL, cred);
301         if (maj != GSS_S_COMPLETE) {
302                 status = ADS_ERROR_GSS(maj, min);
303                 goto done;
304         }
305 #else
306         /* We need to fallback to overriding the default creds.
307          * This operation is not thread safe as it changes the process
308          * environment variable, but we do not have any better option
309          * with older kerberos libraries */
310         {
311                 const char *oldccname = NULL;
312
313                 oldccname = getenv("KRB5CCNAME");
314                 setenv("KRB5CCNAME", ads->auth.ccache_name, 1);
315
316                 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
317                                        NULL, GSS_C_INITIATE, cred, NULL, NULL);
318
319                 if (oldccname) {
320                         setenv("KRB5CCNAME", oldccname, 1);
321                 } else {
322                         unsetenv("KRB5CCNAME");
323                 }
324
325                 if (maj != GSS_S_COMPLETE) {
326                         status = ADS_ERROR_GSS(maj, min);
327                         goto done;
328                 }
329         }
330 #endif
331
332         status = ADS_SUCCESS;
333
334 done:
335         if (!ADS_ERR_OK(status) && kccache != NULL) {
336                 krb5_cc_close(kctx, kccache);
337         }
338         krb5_free_context(kctx);
339         return status;
340 }
341
342 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
343 {
344         gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
345         ADS_STATUS status;
346         int gss_rc;
347         uint32 minor_status;
348         gss_buffer_desc unwrapped, wrapped;
349         int conf_req_flag, conf_state;
350
351         unwrapped.value         = buf;
352         unwrapped.length        = len;
353
354         /* for now request sign and seal */
355         conf_req_flag   = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
356
357         gss_rc = gss_wrap(&minor_status, context_handle,
358                           conf_req_flag, GSS_C_QOP_DEFAULT,
359                           &unwrapped, &conf_state,
360                           &wrapped);
361         status = ADS_ERROR_GSS(gss_rc, minor_status);
362         if (!ADS_ERR_OK(status)) return status;
363
364         if (conf_req_flag && conf_state == 0) {
365                 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
366         }
367
368         if ((ads->ldap.out.size - 4) < wrapped.length) {
369                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
370         }
371
372         /* copy the wrapped blob to the right location */
373         memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
374
375         /* set how many bytes must be written to the underlying socket */
376         ads->ldap.out.left = 4 + wrapped.length;
377
378         gss_release_buffer(&minor_status, &wrapped);
379
380         return ADS_SUCCESS;
381 }
382
383 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
384 {
385         gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
386         ADS_STATUS status;
387         int gss_rc;
388         uint32 minor_status;
389         gss_buffer_desc unwrapped, wrapped;
390         int conf_state;
391
392         wrapped.value   = ads->ldap.in.buf + 4;
393         wrapped.length  = ads->ldap.in.ofs - 4;
394
395         gss_rc = gss_unwrap(&minor_status, context_handle,
396                             &wrapped, &unwrapped,
397                             &conf_state, GSS_C_QOP_DEFAULT);
398         status = ADS_ERROR_GSS(gss_rc, minor_status);
399         if (!ADS_ERR_OK(status)) return status;
400
401         if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
402                 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
403         }
404
405         if (wrapped.length < unwrapped.length) {
406                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
407         }
408
409         /* copy the wrapped blob to the right location */
410         memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
411
412         /* set how many bytes must be written to the underlying socket */
413         ads->ldap.in.left       = unwrapped.length;
414         ads->ldap.in.ofs        = 4;
415
416         gss_release_buffer(&minor_status, &unwrapped);
417
418         return ADS_SUCCESS;
419 }
420
421 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
422 {
423         gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
424         uint32 minor_status;
425
426         gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
427
428         ads->ldap.wrap_ops = NULL;
429         ads->ldap.wrap_private_data = NULL;
430 }
431
432 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
433         .name           = "gssapi",
434         .wrap           = ads_sasl_gssapi_wrap,
435         .unwrap         = ads_sasl_gssapi_unwrap,
436         .disconnect     = ads_sasl_gssapi_disconnect
437 };
438
439 /* 
440    perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
441 */
442 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
443 {
444         ADS_STATUS status;
445         bool ok;
446         uint32 minor_status;
447         int gss_rc, rc;
448         gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
449         gss_OID_desc krb5_mech_type =
450         {9, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
451         gss_OID mech_type = &krb5_mech_type;
452         gss_OID actual_mech_type = GSS_C_NULL_OID;
453         const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
454         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
455         gss_buffer_desc input_token, output_token;
456         uint32 req_flags, ret_flags;
457         uint32 req_tmp, ret_tmp;
458         DATA_BLOB unwrapped;
459         DATA_BLOB wrapped;
460         struct berval cred, *scred = NULL;
461
462         status = ads_init_gssapi_cred(ads, &gss_cred);
463         if (!ADS_ERR_OK(status)) {
464                 goto failed;
465         }
466
467         input_token.value = NULL;
468         input_token.length = 0;
469
470         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
471         switch (ads->ldap.wrap_type) {
472         case ADS_SASLWRAP_TYPE_SEAL:
473                 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
474                 break;
475         case ADS_SASLWRAP_TYPE_SIGN:
476                 req_flags |= GSS_C_INTEG_FLAG;
477                 break;
478         case ADS_SASLWRAP_TYPE_PLAIN:
479                 break;
480         }
481
482         /* Note: here we explicit ask for the krb5 mech_type */
483         gss_rc = gss_init_sec_context(&minor_status,
484                                       gss_cred,
485                                       &context_handle,
486                                       serv_name,
487                                       mech_type,
488                                       req_flags,
489                                       0,
490                                       NULL,
491                                       &input_token,
492                                       &actual_mech_type,
493                                       &output_token,
494                                       &ret_flags,
495                                       NULL);
496         if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
497                 status = ADS_ERROR_GSS(gss_rc, minor_status);
498                 goto failed;
499         }
500
501         /*
502          * As some gssapi krb5 mech implementations
503          * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
504          * to req_flags internaly, it's not possible to
505          * use plain or signing only connection via
506          * the gssapi interface.
507          *
508          * Because of this we need to check it the ret_flags
509          * has more flags as req_flags and correct the value
510          * of ads->ldap.wrap_type.
511          *
512          * I ads->auth.flags has ADS_AUTH_SASL_FORCE
513          * we need to give an error.
514          */
515         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
516         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
517
518         if (req_tmp == ret_tmp) {
519                 /* everythings fine... */
520
521         } else if (req_flags & GSS_C_CONF_FLAG) {
522                 /*
523                  * here we wanted sealing but didn't got it
524                  * from the gssapi library
525                  */
526                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
527                 goto failed;
528
529         } else if ((req_flags & GSS_C_INTEG_FLAG) &&
530                    !(ret_flags & GSS_C_INTEG_FLAG)) {
531                 /*
532                  * here we wanted siging but didn't got it
533                  * from the gssapi library
534                  */
535                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
536                 goto failed;
537
538         } else if (ret_flags & GSS_C_CONF_FLAG) {
539                 /*
540                  * here we didn't want sealing
541                  * but the gssapi library forces it
542                  * so correct the needed wrap_type if
543                  * the caller didn't forced siging only
544                  */
545                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
546                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
547                         goto failed;
548                 }
549
550                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
551                 req_flags = ret_flags;
552
553         } else if (ret_flags & GSS_C_INTEG_FLAG) {
554                 /*
555                  * here we didn't want signing
556                  * but the gssapi library forces it
557                  * so correct the needed wrap_type if
558                  * the caller didn't forced plain
559                  */
560                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
561                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
562                         goto failed;
563                 }
564
565                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
566                 req_flags = ret_flags;
567         } else {
568                 /*
569                  * This could (should?) not happen
570                  */
571                 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
572                 goto failed;
573         
574         }
575
576         /* and wrap that in a shiny SPNEGO wrapper */
577         unwrapped = data_blob_const(output_token.value, output_token.length);
578         wrapped = spnego_gen_negTokenInit(talloc_tos(),
579                         spnego_mechs, &unwrapped, NULL);
580         gss_release_buffer(&minor_status, &output_token);
581         if (unwrapped.length > wrapped.length) {
582                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
583                 goto failed;
584         }
585
586         cred.bv_val = (char *)wrapped.data;
587         cred.bv_len = wrapped.length;
588
589         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, 
590                               &scred);
591         data_blob_free(&wrapped);
592         if (rc != LDAP_SUCCESS) {
593                 status = ADS_ERROR(rc);
594                 goto failed;
595         }
596
597         if (scred) {
598                 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
599         } else {
600                 wrapped = data_blob_null;
601         }
602
603         ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
604                                         OID_KERBEROS5_OLD,
605                                         &unwrapped);
606         if (scred) ber_bvfree(scred);
607         if (!ok) {
608                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
609                 goto failed;
610         }
611
612         input_token.value       = unwrapped.data;
613         input_token.length      = unwrapped.length;
614
615         /* 
616          * As we asked for mutal authentication
617          * we need to pass the servers response
618          * to gssapi
619          */
620         gss_rc = gss_init_sec_context(&minor_status,
621                                       gss_cred,
622                                       &context_handle,
623                                       serv_name,
624                                       mech_type,
625                                       req_flags,
626                                       0,
627                                       NULL,
628                                       &input_token,
629                                       &actual_mech_type,
630                                       &output_token,
631                                       &ret_flags,
632                                       NULL);
633         data_blob_free(&unwrapped);
634         if (gss_rc) {
635                 status = ADS_ERROR_GSS(gss_rc, minor_status);
636                 goto failed;
637         }
638
639         gss_release_buffer(&minor_status, &output_token);
640
641         /*
642          * If we the sign and seal options
643          * doesn't match after getting the response
644          * from the server, we don't want to use the connection
645          */
646         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
647         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
648
649         if (req_tmp != ret_tmp) {
650                 /* everythings fine... */
651                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
652                 goto failed;
653         }
654
655         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
656                 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
657
658                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
659                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
660                                              GSS_C_QOP_DEFAULT,
661                                              max_msg_size, &ads->ldap.out.max_unwrapped);
662                 if (gss_rc) {
663                         status = ADS_ERROR_GSS(gss_rc, minor_status);
664                         goto failed;
665                 }
666
667                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
668                 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
669                 ads->ldap.in.max_wrapped = max_msg_size;
670                 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
671                 if (!ADS_ERR_OK(status)) {
672                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
673                                 ads_errstr(status)));
674                         goto failed;
675                 }
676                 /* make sure we don't free context_handle */
677                 context_handle = GSS_C_NO_CONTEXT;
678         }
679
680         status = ADS_SUCCESS;
681
682 failed:
683         if (gss_cred != GSS_C_NO_CREDENTIAL)
684                 gss_release_cred(&minor_status, &gss_cred);
685         if (context_handle != GSS_C_NO_CONTEXT)
686                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
687         return status;
688 }
689
690 #endif /* HAVE_KRB5 */
691
692 #ifdef HAVE_KRB5
693 struct ads_service_principal {
694          char *string;
695 #ifdef HAVE_KRB5
696          gss_name_t name;
697 #endif
698 };
699
700 static void ads_free_service_principal(struct ads_service_principal *p)
701 {
702         SAFE_FREE(p->string);
703
704 #ifdef HAVE_KRB5
705         if (p->name) {
706                 uint32 minor_status;
707                 gss_release_name(&minor_status, &p->name);
708         }
709 #endif
710         ZERO_STRUCTP(p);
711 }
712
713
714 static ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads,
715                                               char **returned_principal)
716 {
717         ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
718         char *princ = NULL;
719         TALLOC_CTX *frame;
720         char *server = NULL;
721         char *realm = NULL;
722         int rc;
723
724         frame = talloc_stackframe();
725         if (frame == NULL) {
726                 return ADS_ERROR(LDAP_NO_MEMORY);
727         }
728
729         if (ads->server.realm && ads->server.ldap_server) {
730                 server = strlower_talloc(frame, ads->server.ldap_server);
731                 if (server == NULL) {
732                         goto out;
733                 }
734
735                 realm = strupper_talloc(frame, ads->server.realm);
736                 if (realm == NULL) {
737                         goto out;
738                 }
739
740                 /*
741                  * If we got a name which is bigger than a NetBIOS name,
742                  * but isn't a FQDN, create one.
743                  */
744                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
745                         char *dnsdomain;
746
747                         dnsdomain = strlower_talloc(frame, ads->server.realm);
748                         if (dnsdomain == NULL) {
749                                 goto out;
750                         }
751
752                         server = talloc_asprintf(frame,
753                                                  "%s.%s",
754                                                  server, dnsdomain);
755                         if (server == NULL) {
756                                 goto out;
757                         }
758                 }
759         } else if (ads->config.realm && ads->config.ldap_server_name) {
760                 server = strlower_talloc(frame, ads->config.ldap_server_name);
761                 if (server == NULL) {
762                         goto out;
763                 }
764
765                 realm = strupper_talloc(frame, ads->config.realm);
766                 if (realm == NULL) {
767                         goto out;
768                 }
769
770                 /*
771                  * If we got a name which is bigger than a NetBIOS name,
772                  * but isn't a FQDN, create one.
773                  */
774                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
775                         char *dnsdomain;
776
777                         dnsdomain = strlower_talloc(frame, ads->server.realm);
778                         if (dnsdomain == NULL) {
779                                 goto out;
780                         }
781
782                         server = talloc_asprintf(frame,
783                                                  "%s.%s",
784                                                  server, dnsdomain);
785                         if (server == NULL) {
786                                 goto out;
787                         }
788                 }
789         }
790
791         if (server == NULL || realm == NULL) {
792                 goto out;
793         }
794
795         rc = asprintf(&princ, "ldap/%s@%s", server, realm);
796         if (rc == -1 || princ == NULL) {
797                 status = ADS_ERROR(LDAP_PARAM_ERROR);
798                 goto out;
799         }
800
801         *returned_principal = princ;
802
803         status = ADS_SUCCESS;
804 out:
805         TALLOC_FREE(frame);
806         return status;
807 }
808
809 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
810                                                  const char *given_principal,
811                                                  struct ads_service_principal *p)
812 {
813         ADS_STATUS status;
814 #ifdef HAVE_KRB5
815         gss_buffer_desc input_name;
816         /* GSS_KRB5_NT_PRINCIPAL_NAME */
817         gss_OID_desc nt_principal =
818         {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
819         uint32 minor_status;
820         int gss_rc;
821 #endif
822
823         ZERO_STRUCTP(p);
824
825         /* I've seen a child Windows 2000 domain not send
826            the principal name back in the first round of
827            the SASL bind reply.  So we guess based on server
828            name and realm.  --jerry  */
829         /* Also try best guess when we get the w2k8 ignore principal
830            back, or when we are configured to ignore it - gd,
831            abartlet */
832
833         if (!lp_client_use_spnego_principal() ||
834             !given_principal ||
835             strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
836
837                 status = ads_guess_service_principal(ads, &p->string);
838                 if (!ADS_ERR_OK(status)) {
839                         return status;
840                 }
841         } else {
842                 p->string = SMB_STRDUP(given_principal);
843                 if (!p->string) {
844                         return ADS_ERROR(LDAP_NO_MEMORY);
845                 }
846         }
847
848 #ifdef HAVE_KRB5
849         input_name.value = p->string;
850         input_name.length = strlen(p->string);
851
852         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
853         if (gss_rc) {
854                 ads_free_service_principal(p);
855                 return ADS_ERROR_GSS(gss_rc, minor_status);
856         }
857 #endif
858
859         return ADS_SUCCESS;
860 }
861
862 /* 
863    perform a LDAP/SASL/SPNEGO/KRB5 bind
864 */
865 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
866 {
867         DATA_BLOB blob = data_blob_null;
868         struct berval cred, *scred = NULL;
869         DATA_BLOB session_key = data_blob_null;
870         int rc;
871
872         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
873                 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
874         }
875
876         rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
877                                      ads->auth.time_offset, &blob, &session_key, 0,
878                                      ads->auth.ccache_name,
879                                      &ads->auth.tgs_expire);
880
881         if (rc) {
882                 return ADS_ERROR_KRB5(rc);
883         }
884
885         /* now send the auth packet and we should be done */
886         cred.bv_val = (char *)blob.data;
887         cred.bv_len = blob.length;
888
889         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
890
891         data_blob_free(&blob);
892         data_blob_free(&session_key);
893         if(scred)
894                 ber_bvfree(scred);
895
896         return ADS_ERROR(rc);
897 }
898
899 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
900                                             struct ads_service_principal *p)
901 {
902 #ifdef HAVE_KRB5
903         /*
904          * we only use the gsskrb5 based implementation
905          * when sasl sign or seal is requested.
906          *
907          * This has the following reasons:
908          * - it's likely that the gssapi krb5 mech implementation
909          *   doesn't support to negotiate plain connections
910          * - the ads_sasl_spnego_rawkrb5_bind is more robust
911          *   against clock skew errors
912          */
913         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
914                 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
915         }
916 #endif
917         return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
918 }
919 #endif /* HAVE_KRB5 */
920
921 /* 
922    this performs a SASL/SPNEGO bind
923 */
924 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
925 {
926         struct berval *scred=NULL;
927         int rc, i;
928         ADS_STATUS status;
929         DATA_BLOB blob;
930         char *given_principal = NULL;
931         char *OIDs[ASN1_MAX_OIDS];
932 #ifdef HAVE_KRB5
933         bool got_kerberos_mechanism = False;
934 #endif
935
936         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
937
938         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
939                 status = ADS_ERROR(rc);
940                 goto failed;
941         }
942
943         blob = data_blob(scred->bv_val, scred->bv_len);
944
945         ber_bvfree(scred);
946
947 #if 0
948         file_save("sasl_spnego.dat", blob.data, blob.length);
949 #endif
950
951         /* the server sent us the first part of the SPNEGO exchange in the negprot 
952            reply */
953         if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
954                         OIDs[0] == NULL) {
955                 data_blob_free(&blob);
956                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
957                 goto failed;
958         }
959         data_blob_free(&blob);
960
961         /* make sure the server understands kerberos */
962         for (i=0;OIDs[i];i++) {
963                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
964 #ifdef HAVE_KRB5
965                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
966                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
967                         got_kerberos_mechanism = True;
968                 }
969 #endif
970                 talloc_free(OIDs[i]);
971         }
972         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
973
974 #ifdef HAVE_KRB5
975         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
976             got_kerberos_mechanism) 
977         {
978                 struct ads_service_principal p;
979
980                 status = ads_generate_service_principal(ads, given_principal, &p);
981                 TALLOC_FREE(given_principal);
982                 if (!ADS_ERR_OK(status)) {
983                         return status;
984                 }
985
986                 status = ads_sasl_spnego_krb5_bind(ads, &p);
987                 if (ADS_ERR_OK(status)) {
988                         ads_free_service_principal(&p);
989                         return status;
990                 }
991
992                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
993                           "calling kinit\n", ads_errstr(status)));
994
995                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
996
997                 if (ADS_ERR_OK(status)) {
998                         status = ads_sasl_spnego_krb5_bind(ads, &p);
999                         if (!ADS_ERR_OK(status)) {
1000                                 DEBUG(0,("kinit succeeded but "
1001                                         "ads_sasl_spnego_krb5_bind failed: %s\n",
1002                                         ads_errstr(status)));
1003                         }
1004                 }
1005
1006                 ads_free_service_principal(&p);
1007
1008                 /* only fallback to NTLMSSP if allowed */
1009                 if (ADS_ERR_OK(status) || 
1010                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
1011                         return status;
1012                 }
1013         } else
1014 #endif
1015         {
1016                 TALLOC_FREE(given_principal);
1017         }
1018
1019         /* lets do NTLMSSP ... this has the big advantage that we don't need
1020            to sync clocks, and we don't rely on special versions of the krb5 
1021            library for HMAC_MD4 encryption */
1022         return ads_sasl_spnego_ntlmssp_bind(ads);
1023
1024 failed:
1025         return status;
1026 }
1027
1028 #ifdef HAVE_KRB5
1029 #define MAX_GSS_PASSES 3
1030
1031 /* this performs a SASL/gssapi bind
1032    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
1033    is very dependent on correctly configured DNS whereas
1034    this routine is much less fragile
1035    see RFC2078 and RFC2222 for details
1036 */
1037 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
1038 {
1039         uint32 minor_status;
1040         gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
1041         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
1042         gss_OID mech_type = GSS_C_NULL_OID;
1043         gss_buffer_desc output_token, input_token;
1044         uint32 req_flags, ret_flags;
1045         int conf_state;
1046         struct berval cred;
1047         struct berval *scred = NULL;
1048         int i=0;
1049         int gss_rc, rc;
1050         uint8 *p;
1051         uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
1052         uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1053         ADS_STATUS status;
1054
1055         input_token.value = NULL;
1056         input_token.length = 0;
1057
1058         status = ads_init_gssapi_cred(ads, &gss_cred);
1059         if (!ADS_ERR_OK(status)) {
1060                 goto failed;
1061         }
1062
1063         /*
1064          * Note: here we always ask the gssapi for sign and seal
1065          *       as this is negotiated later after the mutal
1066          *       authentication
1067          */
1068         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
1069
1070         for (i=0; i < MAX_GSS_PASSES; i++) {
1071                 gss_rc = gss_init_sec_context(&minor_status,
1072                                           gss_cred,
1073                                           &context_handle,
1074                                           serv_name,
1075                                           mech_type,
1076                                           req_flags,
1077                                           0,
1078                                           NULL,
1079                                           &input_token,
1080                                           NULL,
1081                                           &output_token,
1082                                           &ret_flags,
1083                                           NULL);
1084                 if (scred) {
1085                         ber_bvfree(scred);
1086                         scred = NULL;
1087                 }
1088                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
1089                         status = ADS_ERROR_GSS(gss_rc, minor_status);
1090                         goto failed;
1091                 }
1092
1093                 cred.bv_val = (char *)output_token.value;
1094                 cred.bv_len = output_token.length;
1095
1096                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
1097                                       &scred);
1098                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
1099                         status = ADS_ERROR(rc);
1100                         goto failed;
1101                 }
1102
1103                 if (output_token.value) {
1104                         gss_release_buffer(&minor_status, &output_token);
1105                 }
1106
1107                 if (scred) {
1108                         input_token.value = scred->bv_val;
1109                         input_token.length = scred->bv_len;
1110                 } else {
1111                         input_token.value = NULL;
1112                         input_token.length = 0;
1113                 }
1114
1115                 if (gss_rc == 0) break;
1116         }
1117
1118         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
1119                             &conf_state,NULL);
1120         if (scred) {
1121                 ber_bvfree(scred);
1122                 scred = NULL;
1123         }
1124         if (gss_rc) {
1125                 status = ADS_ERROR_GSS(gss_rc, minor_status);
1126                 goto failed;
1127         }
1128
1129         p = (uint8 *)output_token.value;
1130
1131 #if 0
1132         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
1133 #endif
1134
1135         if (p) {
1136                 wrap_type = CVAL(p,0);
1137                 SCVAL(p,0,0);
1138                 max_msg_size = RIVAL(p,0);
1139         }
1140
1141         gss_release_buffer(&minor_status, &output_token);
1142
1143         if (!(wrap_type & ads->ldap.wrap_type)) {
1144                 /*
1145                  * the server doesn't supports the wrap
1146                  * type we want :-(
1147                  */
1148                 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
1149                         ads->ldap.wrap_type, wrap_type));
1150                 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
1151                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
1152                 goto failed;
1153         }
1154
1155         /* 0x58 is the minimum windows accepts */
1156         if (max_msg_size < 0x58) {
1157                 max_msg_size = 0x58;
1158         }
1159
1160         output_token.length = 4;
1161         output_token.value = SMB_MALLOC(output_token.length);
1162         if (!output_token.value) {
1163                 output_token.length = 0;
1164                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1165                 goto failed;
1166         }
1167         p = (uint8 *)output_token.value;
1168
1169         RSIVAL(p,0,max_msg_size);
1170         SCVAL(p,0,ads->ldap.wrap_type);
1171
1172         /*
1173          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
1174          * but using ads->config.bind_path is the wrong! It should be
1175          * the DN of the user object!
1176          *
1177          * w2k3 gives an error when we send an incorrect DN, but sending nothing
1178          * is ok and matches the information flow used in GSS-SPNEGO.
1179          */
1180
1181         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1182                         &output_token, /* used as *input* here. */
1183                         &conf_state,
1184                         &input_token); /* Used as *output* here. */
1185         if (gss_rc) {
1186                 status = ADS_ERROR_GSS(gss_rc, minor_status);
1187                 output_token.length = 0;
1188                 SAFE_FREE(output_token.value);
1189                 goto failed;
1190         }
1191
1192         /* We've finished with output_token. */
1193         SAFE_FREE(output_token.value);
1194         output_token.length = 0;
1195
1196         cred.bv_val = (char *)input_token.value;
1197         cred.bv_len = input_token.length;
1198
1199         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
1200                               &scred);
1201         gss_release_buffer(&minor_status, &input_token);
1202         status = ADS_ERROR(rc);
1203         if (!ADS_ERR_OK(status)) {
1204                 goto failed;
1205         }
1206
1207         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1208                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1209                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1210                                              GSS_C_QOP_DEFAULT,
1211                                              max_msg_size, &ads->ldap.out.max_unwrapped);
1212                 if (gss_rc) {
1213                         status = ADS_ERROR_GSS(gss_rc, minor_status);
1214                         goto failed;
1215                 }
1216
1217                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1218                 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1219                 ads->ldap.in.max_wrapped = max_msg_size;
1220                 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1221                 if (!ADS_ERR_OK(status)) {
1222                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1223                                 ads_errstr(status)));
1224                         goto failed;
1225                 }
1226                 /* make sure we don't free context_handle */
1227                 context_handle = GSS_C_NO_CONTEXT;
1228         }
1229
1230 failed:
1231         if (gss_cred != GSS_C_NO_CREDENTIAL)
1232                 gss_release_cred(&minor_status, &gss_cred);
1233         if (context_handle != GSS_C_NO_CONTEXT)
1234                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1235
1236         if(scred)
1237                 ber_bvfree(scred);
1238         return status;
1239 }
1240
1241 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1242 {
1243         ADS_STATUS status;
1244         struct ads_service_principal p;
1245
1246         status = ads_generate_service_principal(ads, NULL, &p);
1247         if (!ADS_ERR_OK(status)) {
1248                 return status;
1249         }
1250
1251         status = ads_sasl_gssapi_do_bind(ads, p.name);
1252         if (ADS_ERR_OK(status)) {
1253                 ads_free_service_principal(&p);
1254                 return status;
1255         }
1256
1257         DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1258                   "calling kinit\n", ads_errstr(status)));
1259
1260         status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1261
1262         if (ADS_ERR_OK(status)) {
1263                 status = ads_sasl_gssapi_do_bind(ads, p.name);
1264         }
1265
1266         ads_free_service_principal(&p);
1267
1268         return status;
1269 }
1270
1271 #endif /* HAVE_KRB5 */
1272
1273 /* mapping between SASL mechanisms and functions */
1274 static struct {
1275         const char *name;
1276         ADS_STATUS (*fn)(ADS_STRUCT *);
1277 } sasl_mechanisms[] = {
1278         {"GSS-SPNEGO", ads_sasl_spnego_bind},
1279 #ifdef HAVE_KRB5
1280         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1281 #endif
1282         {NULL, NULL}
1283 };
1284
1285 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1286 {
1287         const char *attrs[] = {"supportedSASLMechanisms", NULL};
1288         char **values;
1289         ADS_STATUS status;
1290         int i, j;
1291         LDAPMessage *res;
1292
1293         /* get a list of supported SASL mechanisms */
1294         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1295         if (!ADS_ERR_OK(status)) return status;
1296
1297         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1298
1299         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1300                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1301         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1302                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1303         } else {
1304                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1305         }
1306
1307         /* try our supported mechanisms in order */
1308         for (i=0;sasl_mechanisms[i].name;i++) {
1309                 /* see if the server supports it */
1310                 for (j=0;values && values[j];j++) {
1311                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1312                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1313 retry:
1314                                 status = sasl_mechanisms[i].fn(ads);
1315                                 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1316                                     status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1317                                     ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1318                                 {
1319                                         DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1320                                                  "retrying with signing enabled\n"));
1321                                         ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1322                                         goto retry;
1323                                 }
1324                                 ldap_value_free(values);
1325                                 ldap_msgfree(res);
1326                                 return status;
1327                         }
1328                 }
1329         }
1330
1331         ldap_value_free(values);
1332         ldap_msgfree(res);
1333         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1334 }
1335
1336 #endif /* HAVE_LDAP */
1337