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