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