r23948: add gsskrb5 sign and seal support for LDAP connections
[vlendec/samba-autobuild/.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
22 #ifdef HAVE_LDAP
23
24 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
25 {
26         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
27         ADS_STATUS status;
28         NTSTATUS nt_status;
29         DATA_BLOB sig;
30         uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
31
32         /* copy the data to the right location */
33         memcpy(dptr, buf, len);
34
35         /* create the signature and may encrypt the data */
36         if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
37                 nt_status = ntlmssp_seal_packet(ntlmssp_state,
38                                                 dptr, len,
39                                                 dptr, len,
40                                                 &sig);
41         } else {
42                 nt_status = ntlmssp_sign_packet(ntlmssp_state,
43                                                 dptr, len,
44                                                 dptr, len,
45                                                 &sig);
46         }
47         status = ADS_ERROR_NT(nt_status);
48         if (!ADS_ERR_OK(status)) return status;
49
50         /* copy the signature to the right location */
51         memcpy(ads->ldap.out.buf + 4,
52                sig.data, NTLMSSP_SIG_SIZE);
53
54         data_blob_free(&sig);
55
56         /* set how many bytes must be written to the underlying socket */
57         ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
58
59         return ADS_SUCCESS;
60 }
61
62 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
63 {
64         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
65         ADS_STATUS status;
66         NTSTATUS nt_status;
67         DATA_BLOB sig;
68         uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
69         uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
70
71         /* wrap the signature into a DATA_BLOB */
72         sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
73
74         /* verify the signature and maybe decrypt the data */
75         if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
76                 nt_status = ntlmssp_unseal_packet(ntlmssp_state,
77                                                   dptr, dlen,
78                                                   dptr, dlen,
79                                                   &sig);
80         } else {
81                 nt_status = ntlmssp_check_packet(ntlmssp_state,
82                                                  dptr, dlen,
83                                                  dptr, dlen,
84                                                  &sig);
85         }
86         status = ADS_ERROR_NT(nt_status);
87         if (!ADS_ERR_OK(status)) return status;
88
89         /* set the amount of bytes for the upper layer and set the ofs to the data */
90         ads->ldap.in.left       = dlen;
91         ads->ldap.in.ofs        = 4 + NTLMSSP_SIG_SIZE;
92
93         return ADS_SUCCESS;
94 }
95
96 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
97 {
98         struct ntlmssp_state *ntlmssp_state = ads->ldap.wrap_private_data;
99
100         ntlmssp_end(&ntlmssp_state);
101
102         ads->ldap.wrap_ops = NULL;
103         ads->ldap.wrap_private_data = NULL;
104 }
105
106 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
107         .name           = "ntlmssp",
108         .wrap           = ads_sasl_ntlmssp_wrap,
109         .unwrap         = ads_sasl_ntlmssp_unwrap,
110         .disconnect     = ads_sasl_ntlmssp_disconnect
111 };
112
113 /* 
114    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
115    we fit on one socket??)
116 */
117 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
118 {
119         DATA_BLOB msg1 = data_blob_null;
120         DATA_BLOB blob = data_blob_null;
121         DATA_BLOB blob_in = data_blob_null;
122         DATA_BLOB blob_out = data_blob_null;
123         struct berval cred, *scred = NULL;
124         int rc;
125         NTSTATUS nt_status;
126         int turn = 1;
127         uint32 features = 0;
128
129         struct ntlmssp_state *ntlmssp_state;
130
131         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
132                 return ADS_ERROR_NT(nt_status);
133         }
134         ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
135
136         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
137                 return ADS_ERROR_NT(nt_status);
138         }
139         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
140                 return ADS_ERROR_NT(nt_status);
141         }
142         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
143                 return ADS_ERROR_NT(nt_status);
144         }
145
146         switch (ads->ldap.wrap_type) {
147         case ADS_SASLWRAP_TYPE_SEAL:
148                 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
149                 break;
150         case ADS_SASLWRAP_TYPE_SIGN:
151                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
152                         features = NTLMSSP_FEATURE_SIGN;
153                 } else {
154                         /*
155                          * windows servers are broken with sign only,
156                          * so we need to use seal here too
157                          */
158                         features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
159                         ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
160                 }
161                 break;
162         case ADS_SASLWRAP_TYPE_PLAIN:
163                 break;
164         }
165
166         ntlmssp_want_feature(ntlmssp_state, features);
167
168         blob_in = data_blob_null;
169
170         do {
171                 nt_status = ntlmssp_update(ntlmssp_state, 
172                                            blob_in, &blob_out);
173                 data_blob_free(&blob_in);
174                 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
175                      || NT_STATUS_IS_OK(nt_status))
176                     && blob_out.length) {
177                         if (turn == 1) {
178                                 /* and wrap it in a SPNEGO wrapper */
179                                 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
180                         } else {
181                                 /* wrap it in SPNEGO */
182                                 msg1 = spnego_gen_auth(blob_out);
183                         }
184
185                         data_blob_free(&blob_out);
186
187                         cred.bv_val = (char *)msg1.data;
188                         cred.bv_len = msg1.length;
189                         scred = NULL;
190                         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
191                         data_blob_free(&msg1);
192                         if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
193                                 if (scred) {
194                                         ber_bvfree(scred);
195                                 }
196
197                                 ntlmssp_end(&ntlmssp_state);
198                                 return ADS_ERROR(rc);
199                         }
200                         if (scred) {
201                                 blob = data_blob(scred->bv_val, scred->bv_len);
202                                 ber_bvfree(scred);
203                         } else {
204                                 blob = data_blob_null;
205                         }
206
207                 } else {
208
209                         ntlmssp_end(&ntlmssp_state);
210                         data_blob_free(&blob_out);
211                         return ADS_ERROR_NT(nt_status);
212                 }
213                 
214                 if ((turn == 1) && 
215                     (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
216                         DATA_BLOB tmp_blob = data_blob_null;
217                         /* the server might give us back two challenges */
218                         if (!spnego_parse_challenge(blob, &blob_in, 
219                                                     &tmp_blob)) {
220
221                                 ntlmssp_end(&ntlmssp_state);
222                                 data_blob_free(&blob);
223                                 DEBUG(3,("Failed to parse challenges\n"));
224                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
225                         }
226                         data_blob_free(&tmp_blob);
227                 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
228                         if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
229                                                         &blob_in)) {
230
231                                 ntlmssp_end(&ntlmssp_state);
232                                 data_blob_free(&blob);
233                                 DEBUG(3,("Failed to parse auth response\n"));
234                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
235                         }
236                 }
237                 data_blob_free(&blob);
238                 data_blob_free(&blob_out);
239                 turn++;
240         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
241         
242         /* we have a reference conter on ntlmssp_state, if we are signing
243            then the state will be kept by the signing engine */
244
245         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
246                 ads->ldap.out.min = 4;
247                 ads->ldap.out.max = 0x0FFFFFFF - NTLMSSP_SIG_SIZE;
248                 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
249                 ads->ldap.in.min = 4;
250                 ads->ldap.in.max = 0x0FFFFFFF;
251                 ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
252         } else {
253                 ntlmssp_end(&ntlmssp_state);
254         }
255
256         return ADS_ERROR(rc);
257 }
258
259 #ifdef HAVE_GSSAPI
260 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
261 {
262         gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
263         ADS_STATUS status;
264         int gss_rc;
265         uint32 minor_status;
266         gss_buffer_desc unwrapped, wrapped;
267         int conf_req_flag, conf_state;
268
269         unwrapped.value         = buf;
270         unwrapped.length        = len;
271
272         /* for now request sign and seal */
273         conf_req_flag   = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
274
275         gss_rc = gss_wrap(&minor_status, context_handle,
276                           conf_req_flag, GSS_C_QOP_DEFAULT,
277                           &unwrapped, &conf_state,
278                           &wrapped);
279         status = ADS_ERROR_GSS(gss_rc, minor_status);
280         if (!ADS_ERR_OK(status)) return status;
281
282         if (conf_req_flag && conf_state == 0) {
283                 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
284         }
285
286         if ((ads->ldap.out.size - 4) < wrapped.length) {
287                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
288         }
289
290         /* copy the wrapped blob to the right location */
291         memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
292
293         /* set how many bytes must be written to the underlying socket */
294         ads->ldap.out.left = 4 + wrapped.length;
295
296         gss_release_buffer(&minor_status, &wrapped);
297
298         return ADS_SUCCESS;
299 }
300
301 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
302 {
303         gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
304         ADS_STATUS status;
305         int gss_rc;
306         uint32 minor_status;
307         gss_buffer_desc unwrapped, wrapped;
308         int conf_state;
309
310         wrapped.value   = ads->ldap.in.buf + 4;
311         wrapped.length  = ads->ldap.in.ofs - 4;
312
313         gss_rc = gss_unwrap(&minor_status, context_handle,
314                             &wrapped, &unwrapped,
315                             &conf_state, GSS_C_QOP_DEFAULT);
316         status = ADS_ERROR_GSS(gss_rc, minor_status);
317         if (!ADS_ERR_OK(status)) return status;
318
319         if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
320                 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
321         }
322
323         if (wrapped.length < wrapped.length) {
324                 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
325         }
326
327         /* copy the wrapped blob to the right location */
328         memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
329
330         /* set how many bytes must be written to the underlying socket */
331         ads->ldap.in.left       = unwrapped.length;
332         ads->ldap.in.ofs        = 4;
333
334         gss_release_buffer(&minor_status, &unwrapped);
335
336         return ADS_SUCCESS;
337 }
338
339 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
340 {
341         gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
342         uint32 minor_status;
343
344         gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
345
346         ads->ldap.wrap_ops = NULL;
347         ads->ldap.wrap_private_data = NULL;
348 }
349
350 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
351         .name           = "gssapi",
352         .wrap           = ads_sasl_gssapi_wrap,
353         .unwrap         = ads_sasl_gssapi_unwrap,
354         .disconnect     = ads_sasl_gssapi_disconnect
355 };
356 #endif
357
358 #ifdef HAVE_KRB5
359 /* 
360    perform a LDAP/SASL/SPNEGO/KRB5 bind
361 */
362 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
363 {
364         DATA_BLOB blob = data_blob_null;
365         struct berval cred, *scred = NULL;
366         DATA_BLOB session_key = data_blob_null;
367         int rc;
368
369         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
370                                      &ads->auth.tgs_expire);
371
372         if (rc) {
373                 return ADS_ERROR_KRB5(rc);
374         }
375
376         /* now send the auth packet and we should be done */
377         cred.bv_val = (char *)blob.data;
378         cred.bv_len = blob.length;
379
380         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
381
382         data_blob_free(&blob);
383         data_blob_free(&session_key);
384         if(scred)
385                 ber_bvfree(scred);
386
387         return ADS_ERROR(rc);
388 }
389 #endif
390
391 /* 
392    this performs a SASL/SPNEGO bind
393 */
394 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
395 {
396         struct berval *scred=NULL;
397         int rc, i;
398         ADS_STATUS status;
399         DATA_BLOB blob;
400         char *principal = NULL;
401         char *OIDs[ASN1_MAX_OIDS];
402 #ifdef HAVE_KRB5
403         BOOL got_kerberos_mechanism = False;
404 #endif
405
406         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
407
408         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
409                 status = ADS_ERROR(rc);
410                 goto failed;
411         }
412
413         blob = data_blob(scred->bv_val, scred->bv_len);
414
415         ber_bvfree(scred);
416
417 #if 0
418         file_save("sasl_spnego.dat", blob.data, blob.length);
419 #endif
420
421         /* the server sent us the first part of the SPNEGO exchange in the negprot 
422            reply */
423         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
424                 data_blob_free(&blob);
425                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
426                 goto failed;
427         }
428         data_blob_free(&blob);
429
430         /* make sure the server understands kerberos */
431         for (i=0;OIDs[i];i++) {
432                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
433 #ifdef HAVE_KRB5
434                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
435                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
436                         got_kerberos_mechanism = True;
437                 }
438 #endif
439                 free(OIDs[i]);
440         }
441         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
442
443 #ifdef HAVE_KRB5
444         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
445             got_kerberos_mechanism) 
446         {
447                 /* I've seen a child Windows 2000 domain not send 
448                    the principal name back in the first round of 
449                    the SASL bind reply.  So we guess based on server
450                    name and realm.  --jerry  */
451                 if ( !principal ) {
452                         if ( ads->server.realm && ads->server.ldap_server ) {
453                                 char *server, *server_realm;
454                                 
455                                 server = SMB_STRDUP( ads->server.ldap_server );
456                                 server_realm = SMB_STRDUP( ads->server.realm );
457                                 
458                                 if ( !server || !server_realm )
459                                         return ADS_ERROR(LDAP_NO_MEMORY);
460
461                                 strlower_m( server );
462                                 strupper_m( server_realm );                             
463                                 asprintf( &principal, "ldap/%s@%s", server, server_realm );
464
465                                 SAFE_FREE( server );
466                                 SAFE_FREE( server_realm );
467
468                                 if ( !principal )
469                                         return ADS_ERROR(LDAP_NO_MEMORY);                               
470                         }
471                         
472                 }
473                 
474                 status = ads_sasl_spnego_krb5_bind(ads, principal);
475                 if (ADS_ERR_OK(status)) {
476                         SAFE_FREE(principal);
477                         return status;
478                 }
479
480                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
481                           "calling kinit\n", ads_errstr(status)));
482
483                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
484
485                 if (ADS_ERR_OK(status)) {
486                         status = ads_sasl_spnego_krb5_bind(ads, principal);
487                 }
488
489                 /* only fallback to NTLMSSP if allowed */
490                 if (ADS_ERR_OK(status) || 
491                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
492                         SAFE_FREE(principal);
493                         return status;
494                 }
495         }
496 #endif
497
498         SAFE_FREE(principal);
499
500         /* lets do NTLMSSP ... this has the big advantage that we don't need
501            to sync clocks, and we don't rely on special versions of the krb5 
502            library for HMAC_MD4 encryption */
503         return ads_sasl_spnego_ntlmssp_bind(ads);
504
505 failed:
506         return status;
507 }
508
509 #ifdef HAVE_GSSAPI
510 #define MAX_GSS_PASSES 3
511
512 /* this performs a SASL/gssapi bind
513    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
514    is very dependent on correctly configured DNS whereas
515    this routine is much less fragile
516    see RFC2078 and RFC2222 for details
517 */
518 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
519 {
520         uint32 minor_status;
521         gss_name_t serv_name;
522         gss_buffer_desc input_name;
523         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
524         gss_OID mech_type = GSS_C_NULL_OID;
525         gss_buffer_desc output_token, input_token;
526         uint32 req_flags, ret_flags;
527         int conf_state;
528         struct berval cred;
529         struct berval *scred = NULL;
530         int i=0;
531         int gss_rc, rc;
532         uint8 *p;
533         uint32 max_msg_size = 0;
534         char *sname = NULL;
535         ADS_STATUS status;
536         krb5_principal principal = NULL;
537         krb5_context ctx = NULL;
538         krb5_enctype enc_types[] = {
539 #ifdef ENCTYPE_ARCFOUR_HMAC
540                         ENCTYPE_ARCFOUR_HMAC,
541 #endif
542                         ENCTYPE_DES_CBC_MD5,
543                         ENCTYPE_NULL};
544         gss_OID_desc nt_principal = 
545         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
546
547         /* we need to fetch a service ticket as the ldap user in the
548            servers realm, regardless of our realm */
549         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
550
551         initialize_krb5_error_table();
552         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
553         if (!ADS_ERR_OK(status)) {
554                 SAFE_FREE(sname);
555                 return status;
556         }
557         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
558         if (!ADS_ERR_OK(status)) {
559                 SAFE_FREE(sname);
560                 krb5_free_context(ctx); 
561                 return status;
562         }
563         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
564         if (!ADS_ERR_OK(status)) {
565                 SAFE_FREE(sname);
566                 krb5_free_context(ctx); 
567                 return status;
568         }
569
570         input_name.value = &principal;
571         input_name.length = sizeof(principal);
572
573         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
574
575         /*
576          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
577          * to point to the *address* of the krb5_principal, and the gss libraries
578          * to a shallow copy of the krb5_principal pointer - so we need to keep
579          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
580          * Just one more way in which MIT engineers screwed me over.... JRA.
581          */
582
583         SAFE_FREE(sname);
584
585         if (gss_rc) {
586                 krb5_free_principal(ctx, principal);
587                 krb5_free_context(ctx); 
588                 return ADS_ERROR_GSS(gss_rc, minor_status);
589         }
590
591         input_token.value = NULL;
592         input_token.length = 0;
593
594         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
595         switch (ads->ldap.wrap_type) {
596         case ADS_SASLWRAP_TYPE_SEAL:
597                 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
598                 break;
599         case ADS_SASLWRAP_TYPE_SIGN:
600                 req_flags |= GSS_C_INTEG_FLAG;
601                 break;
602         case ADS_SASLWRAP_TYPE_PLAIN:
603                 break;
604         }
605
606         for (i=0; i < MAX_GSS_PASSES; i++) {
607                 gss_rc = gss_init_sec_context(&minor_status,
608                                           GSS_C_NO_CREDENTIAL,
609                                           &context_handle,
610                                           serv_name,
611                                           mech_type,
612                                           req_flags,
613                                           0,
614                                           NULL,
615                                           &input_token,
616                                           NULL,
617                                           &output_token,
618                                           &ret_flags,
619                                           NULL);
620
621                 if (input_token.value) {
622                         gss_release_buffer(&minor_status, &input_token);
623                 }
624
625                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
626                         status = ADS_ERROR_GSS(gss_rc, minor_status);
627                         goto failed;
628                 }
629
630                 cred.bv_val = (char *)output_token.value;
631                 cred.bv_len = output_token.length;
632
633                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
634                                       &scred);
635                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
636                         status = ADS_ERROR(rc);
637                         goto failed;
638                 }
639
640                 if (output_token.value) {
641                         gss_release_buffer(&minor_status, &output_token);
642                 }
643
644                 if (scred) {
645                         input_token.value = scred->bv_val;
646                         input_token.length = scred->bv_len;
647                 } else {
648                         input_token.value = NULL;
649                         input_token.length = 0;
650                 }
651
652                 if (gss_rc == 0) break;
653         }
654
655         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
656                             &conf_state,NULL);
657         if (gss_rc) {
658                 status = ADS_ERROR_GSS(gss_rc, minor_status);
659                 goto failed;
660         }
661
662         gss_release_buffer(&minor_status, &input_token);
663
664         p = (uint8 *)output_token.value;
665
666 #if 0
667         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
668 #endif
669
670         if (p) {
671                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
672         }
673
674         gss_release_buffer(&minor_status, &output_token);
675
676         output_token.length = 4;
677         output_token.value = SMB_MALLOC(output_token.length);
678         p = (uint8 *)output_token.value;
679
680         *p++ = ads->ldap.wrap_type;
681         /* choose the same size as the server gave us */
682         *p++ = max_msg_size>>16;
683         *p++ = max_msg_size>>8;
684         *p++ = max_msg_size;
685         /*
686          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
687          * but using ads->config.bind_path is the wrong! It should be
688          * the DN of the user object!
689          *
690          * w2k3 gives an error when we send an incorrect DN, but sending nothing
691          * is ok and matches the information flow used in GSS-SPNEGO.
692          */
693
694         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
695                           &output_token, &conf_state,
696                           &input_token);
697         if (gss_rc) {
698                 status = ADS_ERROR_GSS(gss_rc, minor_status);
699                 goto failed;
700         }
701
702         free(output_token.value);
703
704         cred.bv_val = (char *)input_token.value;
705         cred.bv_len = input_token.length;
706
707         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
708                               &scred);
709         status = ADS_ERROR(rc);
710
711         gss_release_buffer(&minor_status, &input_token);
712
713         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
714                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
715                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
716                                              GSS_C_QOP_DEFAULT,
717                                              max_msg_size, &ads->ldap.out.max);
718                 if (gss_rc) {
719                         status = ADS_ERROR_GSS(gss_rc, minor_status);
720                         goto failed;
721                 }
722
723                 ads->ldap.out.min = 4;
724                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
725                 ads->ldap.in.min = 4;
726                 ads->ldap.in.max = max_msg_size;
727                 ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
728                 /* make sure we don't free context_handle */
729                 context_handle = GSS_C_NO_CONTEXT;
730         }
731 failed:
732
733         gss_release_name(&minor_status, &serv_name);
734         if (context_handle != GSS_C_NO_CONTEXT)
735                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
736         krb5_free_principal(ctx, principal);
737         krb5_free_context(ctx); 
738
739         if(scred)
740                 ber_bvfree(scred);
741         return status;
742 }
743 #endif /* HAVE_GGSAPI */
744
745 /* mapping between SASL mechanisms and functions */
746 static struct {
747         const char *name;
748         ADS_STATUS (*fn)(ADS_STRUCT *);
749 } sasl_mechanisms[] = {
750         {"GSS-SPNEGO", ads_sasl_spnego_bind},
751 #ifdef HAVE_GSSAPI
752         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
753 #endif
754         {NULL, NULL}
755 };
756
757 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
758 {
759         const char *attrs[] = {"supportedSASLMechanisms", NULL};
760         char **values;
761         ADS_STATUS status;
762         int i, j;
763         LDAPMessage *res;
764
765         /* get a list of supported SASL mechanisms */
766         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
767         if (!ADS_ERR_OK(status)) return status;
768
769         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
770
771         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
772                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
773         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
774                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
775         } else {
776                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
777         }
778
779         /* try our supported mechanisms in order */
780         for (i=0;sasl_mechanisms[i].name;i++) {
781                 /* see if the server supports it */
782                 for (j=0;values && values[j];j++) {
783                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
784                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
785                                 status = sasl_mechanisms[i].fn(ads);
786                                 ldap_value_free(values);
787                                 ldap_msgfree(res);
788                                 return status;
789                         }
790                 }
791         }
792
793         ldap_value_free(values);
794         ldap_msgfree(res);
795         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
796 }
797
798 #endif /* HAVE_LDAP */
799