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