libads: record service ticket endtime for sealed ldap connections
[mat/samba.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_t *buf, uint32_t 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_t *buf, uint32_t 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_t 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_t 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_t 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_t 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_t req_flags, ret_flags;
457         uint32_t req_tmp, ret_tmp;
458         DATA_BLOB unwrapped;
459         DATA_BLOB wrapped;
460         struct berval cred, *scred = NULL;
461         uint32_t context_validity = 0;
462         time_t context_endtime = 0;
463
464         status = ads_init_gssapi_cred(ads, &gss_cred);
465         if (!ADS_ERR_OK(status)) {
466                 goto failed;
467         }
468
469         input_token.value = NULL;
470         input_token.length = 0;
471
472         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
473         switch (ads->ldap.wrap_type) {
474         case ADS_SASLWRAP_TYPE_SEAL:
475                 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
476                 break;
477         case ADS_SASLWRAP_TYPE_SIGN:
478                 req_flags |= GSS_C_INTEG_FLAG;
479                 break;
480         case ADS_SASLWRAP_TYPE_PLAIN:
481                 break;
482         }
483
484         /* Note: here we explicit ask for the krb5 mech_type */
485         gss_rc = gss_init_sec_context(&minor_status,
486                                       gss_cred,
487                                       &context_handle,
488                                       serv_name,
489                                       mech_type,
490                                       req_flags,
491                                       0,
492                                       NULL,
493                                       &input_token,
494                                       &actual_mech_type,
495                                       &output_token,
496                                       &ret_flags,
497                                       NULL);
498         if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
499                 status = ADS_ERROR_GSS(gss_rc, minor_status);
500                 goto failed;
501         }
502
503         /*
504          * As some gssapi krb5 mech implementations
505          * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
506          * to req_flags internaly, it's not possible to
507          * use plain or signing only connection via
508          * the gssapi interface.
509          *
510          * Because of this we need to check it the ret_flags
511          * has more flags as req_flags and correct the value
512          * of ads->ldap.wrap_type.
513          *
514          * I ads->auth.flags has ADS_AUTH_SASL_FORCE
515          * we need to give an error.
516          */
517         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
518         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
519
520         if (req_tmp == ret_tmp) {
521                 /* everythings fine... */
522
523         } else if (req_flags & GSS_C_CONF_FLAG) {
524                 /*
525                  * here we wanted sealing but didn't got it
526                  * from the gssapi library
527                  */
528                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
529                 goto failed;
530
531         } else if ((req_flags & GSS_C_INTEG_FLAG) &&
532                    !(ret_flags & GSS_C_INTEG_FLAG)) {
533                 /*
534                  * here we wanted siging but didn't got it
535                  * from the gssapi library
536                  */
537                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
538                 goto failed;
539
540         } else if (ret_flags & GSS_C_CONF_FLAG) {
541                 /*
542                  * here we didn't want sealing
543                  * but the gssapi library forces it
544                  * so correct the needed wrap_type if
545                  * the caller didn't forced siging only
546                  */
547                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
548                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
549                         goto failed;
550                 }
551
552                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
553                 req_flags = ret_flags;
554
555         } else if (ret_flags & GSS_C_INTEG_FLAG) {
556                 /*
557                  * here we didn't want signing
558                  * but the gssapi library forces it
559                  * so correct the needed wrap_type if
560                  * the caller didn't forced plain
561                  */
562                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
563                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
564                         goto failed;
565                 }
566
567                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
568                 req_flags = ret_flags;
569         } else {
570                 /*
571                  * This could (should?) not happen
572                  */
573                 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
574                 goto failed;
575         
576         }
577
578         /* and wrap that in a shiny SPNEGO wrapper */
579         unwrapped = data_blob_const(output_token.value, output_token.length);
580         wrapped = spnego_gen_negTokenInit(talloc_tos(),
581                         spnego_mechs, &unwrapped, NULL);
582         gss_release_buffer(&minor_status, &output_token);
583         if (unwrapped.length > wrapped.length) {
584                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
585                 goto failed;
586         }
587
588         cred.bv_val = (char *)wrapped.data;
589         cred.bv_len = wrapped.length;
590
591         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, 
592                               &scred);
593         data_blob_free(&wrapped);
594         if (rc != LDAP_SUCCESS) {
595                 status = ADS_ERROR(rc);
596                 goto failed;
597         }
598
599         if (scred) {
600                 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
601         } else {
602                 wrapped = data_blob_null;
603         }
604
605         ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
606                                         OID_KERBEROS5_OLD,
607                                         &unwrapped);
608         if (scred) ber_bvfree(scred);
609         if (!ok) {
610                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
611                 goto failed;
612         }
613
614         input_token.value       = unwrapped.data;
615         input_token.length      = unwrapped.length;
616
617         /* 
618          * As we asked for mutal authentication
619          * we need to pass the servers response
620          * to gssapi
621          */
622         gss_rc = gss_init_sec_context(&minor_status,
623                                       gss_cred,
624                                       &context_handle,
625                                       serv_name,
626                                       mech_type,
627                                       req_flags,
628                                       0,
629                                       NULL,
630                                       &input_token,
631                                       &actual_mech_type,
632                                       &output_token,
633                                       &ret_flags,
634                                       NULL);
635         data_blob_free(&unwrapped);
636         if (gss_rc) {
637                 status = ADS_ERROR_GSS(gss_rc, minor_status);
638                 goto failed;
639         }
640
641         gss_release_buffer(&minor_status, &output_token);
642
643         /*
644          * If we the sign and seal options
645          * doesn't match after getting the response
646          * from the server, we don't want to use the connection
647          */
648         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
649         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
650
651         if (req_tmp != ret_tmp) {
652                 /* everythings fine... */
653                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
654                 goto failed;
655         }
656
657         gss_rc =
658             gss_context_time(&minor_status, context_handle, &context_validity);
659         if (gss_rc == GSS_S_COMPLETE) {
660                 if (context_validity != 0) {
661                         context_endtime = time(NULL) + context_validity;
662                         DEBUG(10, ("context (service ticket) valid for "
663                                 "%u seconds\n",
664                                 context_validity));
665                 } else {
666                         DEBUG(10, ("context (service ticket) expired\n"));
667                 }
668         } else {
669                 DEBUG(1, ("gss_context_time failed (%d,%u) -"
670                         " this will be a one-time context\n",
671                         gss_rc, minor_status));
672                 if (gss_rc == GSS_S_CONTEXT_EXPIRED) {
673                         DEBUG(10, ("context (service ticket) expired\n"));
674                 }
675         }
676
677         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
678                 uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
679
680                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
681                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
682                                              GSS_C_QOP_DEFAULT,
683                                              max_msg_size, &ads->ldap.out.max_unwrapped);
684                 if (gss_rc) {
685                         status = ADS_ERROR_GSS(gss_rc, minor_status);
686                         goto failed;
687                 }
688
689                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
690                 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
691                 ads->ldap.in.max_wrapped = max_msg_size;
692                 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
693                 if (!ADS_ERR_OK(status)) {
694                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
695                                 ads_errstr(status)));
696                         goto failed;
697                 }
698                 /* make sure we don't free context_handle */
699                 context_handle = GSS_C_NO_CONTEXT;
700         }
701
702         ads->auth.tgs_expire = context_endtime;
703         status = ADS_SUCCESS;
704
705 failed:
706         if (gss_cred != GSS_C_NO_CREDENTIAL)
707                 gss_release_cred(&minor_status, &gss_cred);
708         if (context_handle != GSS_C_NO_CONTEXT)
709                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
710         return status;
711 }
712
713 #endif /* HAVE_KRB5 */
714
715 #ifdef HAVE_KRB5
716 struct ads_service_principal {
717          char *string;
718 #ifdef HAVE_KRB5
719          gss_name_t name;
720 #endif
721 };
722
723 static void ads_free_service_principal(struct ads_service_principal *p)
724 {
725         SAFE_FREE(p->string);
726
727 #ifdef HAVE_KRB5
728         if (p->name) {
729                 uint32_t minor_status;
730                 gss_release_name(&minor_status, &p->name);
731         }
732 #endif
733         ZERO_STRUCTP(p);
734 }
735
736
737 static ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads,
738                                               char **returned_principal)
739 {
740         ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
741         char *princ = NULL;
742         TALLOC_CTX *frame;
743         char *server = NULL;
744         char *realm = NULL;
745         int rc;
746
747         frame = talloc_stackframe();
748         if (frame == NULL) {
749                 return ADS_ERROR(LDAP_NO_MEMORY);
750         }
751
752         if (ads->server.realm && ads->server.ldap_server) {
753                 server = strlower_talloc(frame, ads->server.ldap_server);
754                 if (server == NULL) {
755                         goto out;
756                 }
757
758                 realm = strupper_talloc(frame, ads->server.realm);
759                 if (realm == NULL) {
760                         goto out;
761                 }
762
763                 /*
764                  * If we got a name which is bigger than a NetBIOS name,
765                  * but isn't a FQDN, create one.
766                  */
767                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
768                         char *dnsdomain;
769
770                         dnsdomain = strlower_talloc(frame, ads->server.realm);
771                         if (dnsdomain == NULL) {
772                                 goto out;
773                         }
774
775                         server = talloc_asprintf(frame,
776                                                  "%s.%s",
777                                                  server, dnsdomain);
778                         if (server == NULL) {
779                                 goto out;
780                         }
781                 }
782         } else if (ads->config.realm && ads->config.ldap_server_name) {
783                 server = strlower_talloc(frame, ads->config.ldap_server_name);
784                 if (server == NULL) {
785                         goto out;
786                 }
787
788                 realm = strupper_talloc(frame, ads->config.realm);
789                 if (realm == NULL) {
790                         goto out;
791                 }
792
793                 /*
794                  * If we got a name which is bigger than a NetBIOS name,
795                  * but isn't a FQDN, create one.
796                  */
797                 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
798                         char *dnsdomain;
799
800                         dnsdomain = strlower_talloc(frame, ads->server.realm);
801                         if (dnsdomain == NULL) {
802                                 goto out;
803                         }
804
805                         server = talloc_asprintf(frame,
806                                                  "%s.%s",
807                                                  server, dnsdomain);
808                         if (server == NULL) {
809                                 goto out;
810                         }
811                 }
812         }
813
814         if (server == NULL || realm == NULL) {
815                 goto out;
816         }
817
818         rc = asprintf(&princ, "ldap/%s@%s", server, realm);
819         if (rc == -1 || princ == NULL) {
820                 status = ADS_ERROR(LDAP_PARAM_ERROR);
821                 goto out;
822         }
823
824         *returned_principal = princ;
825
826         status = ADS_SUCCESS;
827 out:
828         TALLOC_FREE(frame);
829         return status;
830 }
831
832 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
833                                                  const char *given_principal,
834                                                  struct ads_service_principal *p)
835 {
836         ADS_STATUS status;
837 #ifdef HAVE_KRB5
838         gss_buffer_desc input_name;
839         /* GSS_KRB5_NT_PRINCIPAL_NAME */
840         gss_OID_desc nt_principal =
841         {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
842         uint32_t minor_status;
843         int gss_rc;
844 #endif
845
846         ZERO_STRUCTP(p);
847
848         /* I've seen a child Windows 2000 domain not send
849            the principal name back in the first round of
850            the SASL bind reply.  So we guess based on server
851            name and realm.  --jerry  */
852         /* Also try best guess when we get the w2k8 ignore principal
853            back, or when we are configured to ignore it - gd,
854            abartlet */
855
856         if (!lp_client_use_spnego_principal() ||
857             !given_principal ||
858             strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
859
860                 status = ads_guess_service_principal(ads, &p->string);
861                 if (!ADS_ERR_OK(status)) {
862                         return status;
863                 }
864         } else {
865                 p->string = SMB_STRDUP(given_principal);
866                 if (!p->string) {
867                         return ADS_ERROR(LDAP_NO_MEMORY);
868                 }
869         }
870
871 #ifdef HAVE_KRB5
872         input_name.value = p->string;
873         input_name.length = strlen(p->string);
874
875         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
876         if (gss_rc) {
877                 ads_free_service_principal(p);
878                 return ADS_ERROR_GSS(gss_rc, minor_status);
879         }
880 #endif
881
882         return ADS_SUCCESS;
883 }
884
885 /* 
886    perform a LDAP/SASL/SPNEGO/KRB5 bind
887 */
888 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
889 {
890         DATA_BLOB blob = data_blob_null;
891         struct berval cred, *scred = NULL;
892         DATA_BLOB session_key = data_blob_null;
893         int rc;
894
895         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
896                 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
897         }
898
899         rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
900                                      ads->auth.time_offset, &blob, &session_key, 0,
901                                      ads->auth.ccache_name,
902                                      &ads->auth.tgs_expire);
903
904         if (rc) {
905                 return ADS_ERROR_KRB5(rc);
906         }
907
908         /* now send the auth packet and we should be done */
909         cred.bv_val = (char *)blob.data;
910         cred.bv_len = blob.length;
911
912         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
913
914         data_blob_free(&blob);
915         data_blob_free(&session_key);
916         if(scred)
917                 ber_bvfree(scred);
918
919         return ADS_ERROR(rc);
920 }
921
922 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
923                                             struct ads_service_principal *p)
924 {
925 #ifdef HAVE_KRB5
926         /*
927          * we only use the gsskrb5 based implementation
928          * when sasl sign or seal is requested.
929          *
930          * This has the following reasons:
931          * - it's likely that the gssapi krb5 mech implementation
932          *   doesn't support to negotiate plain connections
933          * - the ads_sasl_spnego_rawkrb5_bind is more robust
934          *   against clock skew errors
935          */
936         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
937                 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
938         }
939 #endif
940         return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
941 }
942 #endif /* HAVE_KRB5 */
943
944 /* 
945    this performs a SASL/SPNEGO bind
946 */
947 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
948 {
949         struct berval *scred=NULL;
950         int rc, i;
951         ADS_STATUS status;
952         DATA_BLOB blob;
953         char *given_principal = NULL;
954         char *OIDs[ASN1_MAX_OIDS];
955 #ifdef HAVE_KRB5
956         bool got_kerberos_mechanism = False;
957 #endif
958
959         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
960
961         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
962                 status = ADS_ERROR(rc);
963                 goto failed;
964         }
965
966         blob = data_blob(scred->bv_val, scred->bv_len);
967
968         ber_bvfree(scred);
969
970 #if 0
971         file_save("sasl_spnego.dat", blob.data, blob.length);
972 #endif
973
974         /* the server sent us the first part of the SPNEGO exchange in the negprot 
975            reply */
976         if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
977                         OIDs[0] == NULL) {
978                 data_blob_free(&blob);
979                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
980                 goto failed;
981         }
982         data_blob_free(&blob);
983
984         /* make sure the server understands kerberos */
985         for (i=0;OIDs[i];i++) {
986                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
987 #ifdef HAVE_KRB5
988                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
989                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
990                         got_kerberos_mechanism = True;
991                 }
992 #endif
993                 talloc_free(OIDs[i]);
994         }
995         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
996
997 #ifdef HAVE_KRB5
998         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
999             got_kerberos_mechanism) 
1000         {
1001                 struct ads_service_principal p;
1002
1003                 status = ads_generate_service_principal(ads, given_principal, &p);
1004                 TALLOC_FREE(given_principal);
1005                 if (!ADS_ERR_OK(status)) {
1006                         return status;
1007                 }
1008
1009                 status = ads_sasl_spnego_krb5_bind(ads, &p);
1010                 if (ADS_ERR_OK(status)) {
1011                         ads_free_service_principal(&p);
1012                         return status;
1013                 }
1014
1015                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
1016                           "calling kinit\n", ads_errstr(status)));
1017
1018                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
1019
1020                 if (ADS_ERR_OK(status)) {
1021                         status = ads_sasl_spnego_krb5_bind(ads, &p);
1022                         if (!ADS_ERR_OK(status)) {
1023                                 DEBUG(0,("kinit succeeded but "
1024                                         "ads_sasl_spnego_krb5_bind failed: %s\n",
1025                                         ads_errstr(status)));
1026                         }
1027                 }
1028
1029                 ads_free_service_principal(&p);
1030
1031                 /* only fallback to NTLMSSP if allowed */
1032                 if (ADS_ERR_OK(status) || 
1033                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
1034                         return status;
1035                 }
1036         } else
1037 #endif
1038         {
1039                 TALLOC_FREE(given_principal);
1040         }
1041
1042         /* lets do NTLMSSP ... this has the big advantage that we don't need
1043            to sync clocks, and we don't rely on special versions of the krb5 
1044            library for HMAC_MD4 encryption */
1045         return ads_sasl_spnego_ntlmssp_bind(ads);
1046
1047 failed:
1048         return status;
1049 }
1050
1051 #ifdef HAVE_KRB5
1052 #define MAX_GSS_PASSES 3
1053
1054 /* this performs a SASL/gssapi bind
1055    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
1056    is very dependent on correctly configured DNS whereas
1057    this routine is much less fragile
1058    see RFC2078 and RFC2222 for details
1059 */
1060 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
1061 {
1062         uint32_t minor_status;
1063         gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
1064         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
1065         gss_OID mech_type = GSS_C_NULL_OID;
1066         gss_buffer_desc output_token, input_token;
1067         uint32_t req_flags, ret_flags;
1068         int conf_state;
1069         struct berval cred;
1070         struct berval *scred = NULL;
1071         int i=0;
1072         int gss_rc, rc;
1073         uint8_t *p;
1074         uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
1075         uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1076         ADS_STATUS status;
1077
1078         input_token.value = NULL;
1079         input_token.length = 0;
1080
1081         status = ads_init_gssapi_cred(ads, &gss_cred);
1082         if (!ADS_ERR_OK(status)) {
1083                 goto failed;
1084         }
1085
1086         /*
1087          * Note: here we always ask the gssapi for sign and seal
1088          *       as this is negotiated later after the mutal
1089          *       authentication
1090          */
1091         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
1092
1093         for (i=0; i < MAX_GSS_PASSES; i++) {
1094                 gss_rc = gss_init_sec_context(&minor_status,
1095                                           gss_cred,
1096                                           &context_handle,
1097                                           serv_name,
1098                                           mech_type,
1099                                           req_flags,
1100                                           0,
1101                                           NULL,
1102                                           &input_token,
1103                                           NULL,
1104                                           &output_token,
1105                                           &ret_flags,
1106                                           NULL);
1107                 if (scred) {
1108                         ber_bvfree(scred);
1109                         scred = NULL;
1110                 }
1111                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
1112                         status = ADS_ERROR_GSS(gss_rc, minor_status);
1113                         goto failed;
1114                 }
1115
1116                 cred.bv_val = (char *)output_token.value;
1117                 cred.bv_len = output_token.length;
1118
1119                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
1120                                       &scred);
1121                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
1122                         status = ADS_ERROR(rc);
1123                         goto failed;
1124                 }
1125
1126                 if (output_token.value) {
1127                         gss_release_buffer(&minor_status, &output_token);
1128                 }
1129
1130                 if (scred) {
1131                         input_token.value = scred->bv_val;
1132                         input_token.length = scred->bv_len;
1133                 } else {
1134                         input_token.value = NULL;
1135                         input_token.length = 0;
1136                 }
1137
1138                 if (gss_rc == 0) break;
1139         }
1140
1141         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
1142                             &conf_state,NULL);
1143         if (scred) {
1144                 ber_bvfree(scred);
1145                 scred = NULL;
1146         }
1147         if (gss_rc) {
1148                 status = ADS_ERROR_GSS(gss_rc, minor_status);
1149                 goto failed;
1150         }
1151
1152         p = (uint8_t *)output_token.value;
1153
1154 #if 0
1155         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
1156 #endif
1157
1158         if (p) {
1159                 wrap_type = CVAL(p,0);
1160                 SCVAL(p,0,0);
1161                 max_msg_size = RIVAL(p,0);
1162         }
1163
1164         gss_release_buffer(&minor_status, &output_token);
1165
1166         if (!(wrap_type & ads->ldap.wrap_type)) {
1167                 /*
1168                  * the server doesn't supports the wrap
1169                  * type we want :-(
1170                  */
1171                 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
1172                         ads->ldap.wrap_type, wrap_type));
1173                 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
1174                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
1175                 goto failed;
1176         }
1177
1178         /* 0x58 is the minimum windows accepts */
1179         if (max_msg_size < 0x58) {
1180                 max_msg_size = 0x58;
1181         }
1182
1183         output_token.length = 4;
1184         output_token.value = SMB_MALLOC(output_token.length);
1185         if (!output_token.value) {
1186                 output_token.length = 0;
1187                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1188                 goto failed;
1189         }
1190         p = (uint8_t *)output_token.value;
1191
1192         RSIVAL(p,0,max_msg_size);
1193         SCVAL(p,0,ads->ldap.wrap_type);
1194
1195         /*
1196          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
1197          * but using ads->config.bind_path is the wrong! It should be
1198          * the DN of the user object!
1199          *
1200          * w2k3 gives an error when we send an incorrect DN, but sending nothing
1201          * is ok and matches the information flow used in GSS-SPNEGO.
1202          */
1203
1204         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1205                         &output_token, /* used as *input* here. */
1206                         &conf_state,
1207                         &input_token); /* Used as *output* here. */
1208         if (gss_rc) {
1209                 status = ADS_ERROR_GSS(gss_rc, minor_status);
1210                 output_token.length = 0;
1211                 SAFE_FREE(output_token.value);
1212                 goto failed;
1213         }
1214
1215         /* We've finished with output_token. */
1216         SAFE_FREE(output_token.value);
1217         output_token.length = 0;
1218
1219         cred.bv_val = (char *)input_token.value;
1220         cred.bv_len = input_token.length;
1221
1222         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
1223                               &scred);
1224         gss_release_buffer(&minor_status, &input_token);
1225         status = ADS_ERROR(rc);
1226         if (!ADS_ERR_OK(status)) {
1227                 goto failed;
1228         }
1229
1230         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1231                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1232                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1233                                              GSS_C_QOP_DEFAULT,
1234                                              max_msg_size, &ads->ldap.out.max_unwrapped);
1235                 if (gss_rc) {
1236                         status = ADS_ERROR_GSS(gss_rc, minor_status);
1237                         goto failed;
1238                 }
1239
1240                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1241                 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1242                 ads->ldap.in.max_wrapped = max_msg_size;
1243                 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1244                 if (!ADS_ERR_OK(status)) {
1245                         DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1246                                 ads_errstr(status)));
1247                         goto failed;
1248                 }
1249                 /* make sure we don't free context_handle */
1250                 context_handle = GSS_C_NO_CONTEXT;
1251         }
1252
1253 failed:
1254         if (gss_cred != GSS_C_NO_CREDENTIAL)
1255                 gss_release_cred(&minor_status, &gss_cred);
1256         if (context_handle != GSS_C_NO_CONTEXT)
1257                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1258
1259         if(scred)
1260                 ber_bvfree(scred);
1261         return status;
1262 }
1263
1264 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1265 {
1266         ADS_STATUS status;
1267         struct ads_service_principal p;
1268
1269         status = ads_generate_service_principal(ads, NULL, &p);
1270         if (!ADS_ERR_OK(status)) {
1271                 return status;
1272         }
1273
1274         status = ads_sasl_gssapi_do_bind(ads, p.name);
1275         if (ADS_ERR_OK(status)) {
1276                 ads_free_service_principal(&p);
1277                 return status;
1278         }
1279
1280         DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1281                   "calling kinit\n", ads_errstr(status)));
1282
1283         status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1284
1285         if (ADS_ERR_OK(status)) {
1286                 status = ads_sasl_gssapi_do_bind(ads, p.name);
1287         }
1288
1289         ads_free_service_principal(&p);
1290
1291         return status;
1292 }
1293
1294 #endif /* HAVE_KRB5 */
1295
1296 /* mapping between SASL mechanisms and functions */
1297 static struct {
1298         const char *name;
1299         ADS_STATUS (*fn)(ADS_STRUCT *);
1300 } sasl_mechanisms[] = {
1301         {"GSS-SPNEGO", ads_sasl_spnego_bind},
1302 #ifdef HAVE_KRB5
1303         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1304 #endif
1305         {NULL, NULL}
1306 };
1307
1308 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1309 {
1310         const char *attrs[] = {"supportedSASLMechanisms", NULL};
1311         char **values;
1312         ADS_STATUS status;
1313         int i, j;
1314         LDAPMessage *res;
1315
1316         /* get a list of supported SASL mechanisms */
1317         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1318         if (!ADS_ERR_OK(status)) return status;
1319
1320         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1321
1322         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1323                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1324         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1325                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1326         } else {
1327                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1328         }
1329
1330         /* try our supported mechanisms in order */
1331         for (i=0;sasl_mechanisms[i].name;i++) {
1332                 /* see if the server supports it */
1333                 for (j=0;values && values[j];j++) {
1334                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1335                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1336 retry:
1337                                 status = sasl_mechanisms[i].fn(ads);
1338                                 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1339                                     status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1340                                     ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1341                                 {
1342                                         DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1343                                                  "retrying with signing enabled\n"));
1344                                         ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1345                                         goto retry;
1346                                 }
1347                                 ldap_value_free(values);
1348                                 ldap_msgfree(res);
1349                                 return status;
1350                         }
1351                 }
1352         }
1353
1354         ldap_value_free(values);
1355         ldap_msgfree(res);
1356         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1357 }
1358
1359 #endif /* HAVE_LDAP */
1360