r24062: fix logic for broken krb5 libs which always force
[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 =
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
360 /* 
361    perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
362 */
363 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const char *sname)
364 {
365         ADS_STATUS status;
366         BOOL ok;
367         uint32 minor_status;
368         int gss_rc, rc;
369         gss_OID_desc krb5_mech_type =
370         {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
371         gss_OID mech_type = &krb5_mech_type;
372         gss_OID actual_mech_type = GSS_C_NULL_OID;
373         const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
374         gss_name_t serv_name;
375         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
376         gss_buffer_desc input_token, output_token;
377         uint32 req_flags, ret_flags;
378         uint32 req_tmp, ret_tmp;
379         DATA_BLOB unwrapped;
380         DATA_BLOB wrapped;
381         struct berval cred, *scred = NULL;
382         krb5_principal principal = NULL;
383         gss_buffer_desc input_name;
384         krb5_context ctx = NULL;
385         krb5_enctype enc_types[] = {
386 #ifdef ENCTYPE_ARCFOUR_HMAC
387                         ENCTYPE_ARCFOUR_HMAC,
388 #endif
389                         ENCTYPE_DES_CBC_MD5,
390                         ENCTYPE_NULL};
391         gss_OID_desc nt_principal = 
392         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
393
394         initialize_krb5_error_table();
395         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
396         if (!ADS_ERR_OK(status)) {
397                 return status;
398         }
399         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
400         if (!ADS_ERR_OK(status)) {
401                 krb5_free_context(ctx); 
402                 return status;
403         }
404         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
405         if (!ADS_ERR_OK(status)) {
406                 krb5_free_context(ctx); 
407                 return status;
408         }
409
410         /*
411          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
412          * to point to the *address* of the krb5_principal, and the gss libraries
413          * to a shallow copy of the krb5_principal pointer - so we need to keep
414          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
415          * Just one more way in which MIT engineers screwed me over.... JRA.
416          */
417         input_name.value = &principal;
418         input_name.length = sizeof(principal);
419
420         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
421         if (gss_rc) {
422                 krb5_free_principal(ctx, principal);
423                 krb5_free_context(ctx); 
424                 return ADS_ERROR_GSS(gss_rc, minor_status);
425         }
426
427         input_token.value = NULL;
428         input_token.length = 0;
429
430         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
431         switch (ads->ldap.wrap_type) {
432         case ADS_SASLWRAP_TYPE_SEAL:
433                 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
434                 break;
435         case ADS_SASLWRAP_TYPE_SIGN:
436                 req_flags |= GSS_C_INTEG_FLAG;
437                 break;
438         case ADS_SASLWRAP_TYPE_PLAIN:
439                 break;
440         }
441
442         /* Note: here we explicit ask for the krb5 mech_type */
443         gss_rc = gss_init_sec_context(&minor_status,
444                                       GSS_C_NO_CREDENTIAL,
445                                       &context_handle,
446                                       serv_name,
447                                       mech_type,
448                                       req_flags,
449                                       0,
450                                       NULL,
451                                       &input_token,
452                                       &actual_mech_type,
453                                       &output_token,
454                                       &ret_flags,
455                                       NULL);
456         if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
457                 status = ADS_ERROR_GSS(gss_rc, minor_status);
458                 goto failed;
459         }
460
461         /*
462          * As some gssapi krb5 mech implementations
463          * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
464          * to req_flags internaly, it's not possible to
465          * use plain or signing only connection via
466          * the gssapi interface.
467          *
468          * Because of this we need to check it the ret_flags
469          * has more flags as req_flags and correct the value
470          * of ads->ldap.wrap_type.
471          *
472          * I ads->auth.flags has ADS_AUTH_SASL_FORCE
473          * we need to give an error.
474          */
475         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
476         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
477
478         if (req_tmp == ret_tmp) {
479                 /* everythings fine... */
480
481         } else if (req_flags & GSS_C_CONF_FLAG) {
482                 /*
483                  * here we wanted sealing but didn't got it
484                  * from the gssapi library
485                  */
486                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
487                 goto failed;
488
489         } else if ((req_flags & GSS_C_INTEG_FLAG) &&
490                    !(ret_flags & GSS_C_INTEG_FLAG)) {
491                 /*
492                  * here we wanted siging but didn't got it
493                  * from the gssapi library
494                  */
495                 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
496                 goto failed;
497
498         } else if (ret_flags & GSS_C_CONF_FLAG) {
499                 /*
500                  * here we didn't want sealing
501                  * but the gssapi library forces it
502                  * so correct the needed wrap_type if
503                  * the caller didn't forced siging only
504                  */
505                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
506                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
507                         goto failed;
508                 }
509
510                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
511                 req_flags = ret_flags;
512
513         } else if (ret_flags & GSS_C_INTEG_FLAG) {
514                 /*
515                  * here we didn't want signing
516                  * but the gssapi library forces it
517                  * so correct the needed wrap_type if
518                  * the caller didn't forced plain
519                  */
520                 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
521                         status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
522                         goto failed;
523                 }
524
525                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
526                 req_flags = ret_flags;
527         } else {
528                 /*
529                  * This could (should?) not happen
530                  */
531                 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
532                 goto failed;
533         
534         }
535
536         /* and wrap that in a shiny SPNEGO wrapper */
537         unwrapped = data_blob_const(output_token.value, output_token.length);
538         wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
539         gss_release_buffer(&minor_status, &output_token);
540         if (unwrapped.length > wrapped.length) {
541                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
542                 goto failed;
543         }
544
545         cred.bv_val = (char *)wrapped.data;
546         cred.bv_len = wrapped.length;
547
548         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, 
549                               &scred);
550         data_blob_free(&wrapped);
551         if (rc != LDAP_SUCCESS) {
552                 status = ADS_ERROR(rc);
553                 goto failed;
554         }
555
556         if (scred) {
557                 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
558         } else {
559                 wrapped = data_blob_null;
560         }
561
562         ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
563                                         OID_KERBEROS5_OLD,
564                                         &unwrapped);
565         if (scred) ber_bvfree(scred);
566         if (!ok) {
567                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
568                 goto failed;
569         }
570
571         input_token.value       = unwrapped.data;
572         input_token.length      = unwrapped.length;
573
574         /* 
575          * As we asked for mutal authentication
576          * we need to pass the servers response
577          * to gssapi
578          */
579         gss_rc = gss_init_sec_context(&minor_status,
580                                       GSS_C_NO_CREDENTIAL,
581                                       &context_handle,
582                                       serv_name,
583                                       mech_type,
584                                       req_flags,
585                                       0,
586                                       NULL,
587                                       &input_token,
588                                       &actual_mech_type,
589                                       &output_token,
590                                       &ret_flags,
591                                       NULL);
592         data_blob_free(&unwrapped);
593         if (gss_rc) {
594                 status = ADS_ERROR_GSS(gss_rc, minor_status);
595                 goto failed;
596         }
597
598         gss_release_buffer(&minor_status, &output_token);
599
600         /*
601          * If we the sign and seal options
602          * doesn't match after getting the response
603          * from the server, we don't want to use the connection
604          */
605         req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
606         ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
607
608         if (req_tmp != ret_tmp) {
609                 /* everythings fine... */
610                 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
611                 goto failed;
612         }
613
614         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
615                 uint32 max_msg_size = 0x0A000000;
616
617                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
618                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
619                                              GSS_C_QOP_DEFAULT,
620                                              max_msg_size, &ads->ldap.out.max);
621                 if (gss_rc) {
622                         status = ADS_ERROR_GSS(gss_rc, minor_status);
623                         goto failed;
624                 }
625
626                 ads->ldap.out.min = 4;
627                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
628                 ads->ldap.in.min = 4;
629                 ads->ldap.in.max = max_msg_size;
630                 ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
631                 /* make sure we don't free context_handle */
632                 context_handle = GSS_C_NO_CONTEXT;
633         }
634
635 failed:
636         gss_release_name(&minor_status, &serv_name);
637         if (context_handle != GSS_C_NO_CONTEXT)
638                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
639         krb5_free_principal(ctx, principal);
640         krb5_free_context(ctx); 
641         return status;
642 }
643
644 #endif
645
646 #ifdef HAVE_KRB5
647 /* 
648    perform a LDAP/SASL/SPNEGO/KRB5 bind
649 */
650 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
651 {
652         DATA_BLOB blob = data_blob_null;
653         struct berval cred, *scred = NULL;
654         DATA_BLOB session_key = data_blob_null;
655         int rc;
656
657         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
658                 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
659         }
660
661         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
662                                      &ads->auth.tgs_expire);
663
664         if (rc) {
665                 return ADS_ERROR_KRB5(rc);
666         }
667
668         /* now send the auth packet and we should be done */
669         cred.bv_val = (char *)blob.data;
670         cred.bv_len = blob.length;
671
672         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
673
674         data_blob_free(&blob);
675         data_blob_free(&session_key);
676         if(scred)
677                 ber_bvfree(scred);
678
679         return ADS_ERROR(rc);
680 }
681
682 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
683 {
684 #ifdef HAVE_GSSAPI
685         /*
686          * we only use the gsskrb5 based implementation
687          * when sasl sign or seal is requested.
688          *
689          * This has the following reasons:
690          * - it's likely that the gssapi krb5 mech implementation
691          *   doesn't support to negotiate plain connections
692          * - the ads_sasl_spnego_rawkrb5_bind is more robust
693          *   against clock skew errors
694          */
695         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
696                 return ads_sasl_spnego_gsskrb5_bind(ads, principal);
697         }
698 #endif
699         return ads_sasl_spnego_rawkrb5_bind(ads, principal);
700 }
701 #endif
702
703 /* 
704    this performs a SASL/SPNEGO bind
705 */
706 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
707 {
708         struct berval *scred=NULL;
709         int rc, i;
710         ADS_STATUS status;
711         DATA_BLOB blob;
712         char *principal = NULL;
713         char *OIDs[ASN1_MAX_OIDS];
714 #ifdef HAVE_KRB5
715         BOOL got_kerberos_mechanism = False;
716 #endif
717
718         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
719
720         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
721                 status = ADS_ERROR(rc);
722                 goto failed;
723         }
724
725         blob = data_blob(scred->bv_val, scred->bv_len);
726
727         ber_bvfree(scred);
728
729 #if 0
730         file_save("sasl_spnego.dat", blob.data, blob.length);
731 #endif
732
733         /* the server sent us the first part of the SPNEGO exchange in the negprot 
734            reply */
735         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
736                 data_blob_free(&blob);
737                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
738                 goto failed;
739         }
740         data_blob_free(&blob);
741
742         /* make sure the server understands kerberos */
743         for (i=0;OIDs[i];i++) {
744                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
745 #ifdef HAVE_KRB5
746                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
747                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
748                         got_kerberos_mechanism = True;
749                 }
750 #endif
751                 free(OIDs[i]);
752         }
753         DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
754
755 #ifdef HAVE_KRB5
756         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
757             got_kerberos_mechanism) 
758         {
759                 /* I've seen a child Windows 2000 domain not send 
760                    the principal name back in the first round of 
761                    the SASL bind reply.  So we guess based on server
762                    name and realm.  --jerry  */
763                 if ( !principal ) {
764                         if ( ads->server.realm && ads->server.ldap_server ) {
765                                 char *server, *server_realm;
766                                 
767                                 server = SMB_STRDUP( ads->server.ldap_server );
768                                 server_realm = SMB_STRDUP( ads->server.realm );
769                                 
770                                 if ( !server || !server_realm )
771                                         return ADS_ERROR(LDAP_NO_MEMORY);
772
773                                 strlower_m( server );
774                                 strupper_m( server_realm );                             
775                                 asprintf( &principal, "ldap/%s@%s", server, server_realm );
776
777                                 SAFE_FREE( server );
778                                 SAFE_FREE( server_realm );
779
780                                 if ( !principal )
781                                         return ADS_ERROR(LDAP_NO_MEMORY);                               
782                         }
783                         
784                 }
785                 
786                 status = ads_sasl_spnego_krb5_bind(ads, principal);
787                 if (ADS_ERR_OK(status)) {
788                         SAFE_FREE(principal);
789                         return status;
790                 }
791
792                 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
793                           "calling kinit\n", ads_errstr(status)));
794
795                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
796
797                 if (ADS_ERR_OK(status)) {
798                         status = ads_sasl_spnego_krb5_bind(ads, principal);
799                 }
800
801                 /* only fallback to NTLMSSP if allowed */
802                 if (ADS_ERR_OK(status) || 
803                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
804                         SAFE_FREE(principal);
805                         return status;
806                 }
807         }
808 #endif
809
810         SAFE_FREE(principal);
811
812         /* lets do NTLMSSP ... this has the big advantage that we don't need
813            to sync clocks, and we don't rely on special versions of the krb5 
814            library for HMAC_MD4 encryption */
815         return ads_sasl_spnego_ntlmssp_bind(ads);
816
817 failed:
818         return status;
819 }
820
821 #ifdef HAVE_GSSAPI
822 #define MAX_GSS_PASSES 3
823
824 /* this performs a SASL/gssapi bind
825    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
826    is very dependent on correctly configured DNS whereas
827    this routine is much less fragile
828    see RFC2078 and RFC2222 for details
829 */
830 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
831 {
832         uint32 minor_status;
833         gss_name_t serv_name;
834         gss_buffer_desc input_name;
835         gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
836         gss_OID mech_type = GSS_C_NULL_OID;
837         gss_buffer_desc output_token, input_token;
838         uint32 req_flags, ret_flags;
839         int conf_state;
840         struct berval cred;
841         struct berval *scred = NULL;
842         int i=0;
843         int gss_rc, rc;
844         uint8 *p;
845         uint32 max_msg_size = 0;
846         char *sname = NULL;
847         ADS_STATUS status;
848         krb5_principal principal = NULL;
849         krb5_context ctx = NULL;
850         krb5_enctype enc_types[] = {
851 #ifdef ENCTYPE_ARCFOUR_HMAC
852                         ENCTYPE_ARCFOUR_HMAC,
853 #endif
854                         ENCTYPE_DES_CBC_MD5,
855                         ENCTYPE_NULL};
856         gss_OID_desc nt_principal = 
857         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
858
859         /* we need to fetch a service ticket as the ldap user in the
860            servers realm, regardless of our realm */
861         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
862
863         initialize_krb5_error_table();
864         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
865         if (!ADS_ERR_OK(status)) {
866                 SAFE_FREE(sname);
867                 return status;
868         }
869         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
870         if (!ADS_ERR_OK(status)) {
871                 SAFE_FREE(sname);
872                 krb5_free_context(ctx); 
873                 return status;
874         }
875         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
876         if (!ADS_ERR_OK(status)) {
877                 SAFE_FREE(sname);
878                 krb5_free_context(ctx); 
879                 return status;
880         }
881
882         input_name.value = &principal;
883         input_name.length = sizeof(principal);
884
885         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
886
887         /*
888          * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
889          * to point to the *address* of the krb5_principal, and the gss libraries
890          * to a shallow copy of the krb5_principal pointer - so we need to keep
891          * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
892          * Just one more way in which MIT engineers screwed me over.... JRA.
893          */
894
895         SAFE_FREE(sname);
896
897         if (gss_rc) {
898                 krb5_free_principal(ctx, principal);
899                 krb5_free_context(ctx); 
900                 return ADS_ERROR_GSS(gss_rc, minor_status);
901         }
902
903         input_token.value = NULL;
904         input_token.length = 0;
905
906         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
907         switch (ads->ldap.wrap_type) {
908         case ADS_SASLWRAP_TYPE_SEAL:
909                 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
910                 break;
911         case ADS_SASLWRAP_TYPE_SIGN:
912                 req_flags |= GSS_C_INTEG_FLAG;
913                 break;
914         case ADS_SASLWRAP_TYPE_PLAIN:
915                 break;
916         }
917
918         for (i=0; i < MAX_GSS_PASSES; i++) {
919                 gss_rc = gss_init_sec_context(&minor_status,
920                                           GSS_C_NO_CREDENTIAL,
921                                           &context_handle,
922                                           serv_name,
923                                           mech_type,
924                                           req_flags,
925                                           0,
926                                           NULL,
927                                           &input_token,
928                                           NULL,
929                                           &output_token,
930                                           &ret_flags,
931                                           NULL);
932
933                 if (input_token.value) {
934                         gss_release_buffer(&minor_status, &input_token);
935                 }
936
937                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
938                         status = ADS_ERROR_GSS(gss_rc, minor_status);
939                         goto failed;
940                 }
941
942                 cred.bv_val = (char *)output_token.value;
943                 cred.bv_len = output_token.length;
944
945                 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
946                                       &scred);
947                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
948                         status = ADS_ERROR(rc);
949                         goto failed;
950                 }
951
952                 if (output_token.value) {
953                         gss_release_buffer(&minor_status, &output_token);
954                 }
955
956                 if (scred) {
957                         input_token.value = scred->bv_val;
958                         input_token.length = scred->bv_len;
959                 } else {
960                         input_token.value = NULL;
961                         input_token.length = 0;
962                 }
963
964                 if (gss_rc == 0) break;
965         }
966
967         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
968                             &conf_state,NULL);
969         if (gss_rc) {
970                 status = ADS_ERROR_GSS(gss_rc, minor_status);
971                 goto failed;
972         }
973
974         gss_release_buffer(&minor_status, &input_token);
975
976         p = (uint8 *)output_token.value;
977
978 #if 0
979         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
980 #endif
981
982         if (p) {
983                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
984         }
985
986         gss_release_buffer(&minor_status, &output_token);
987
988         output_token.length = 4;
989         output_token.value = SMB_MALLOC(output_token.length);
990         p = (uint8 *)output_token.value;
991
992         *p++ = ads->ldap.wrap_type;
993         /* choose the same size as the server gave us */
994         *p++ = max_msg_size>>16;
995         *p++ = max_msg_size>>8;
996         *p++ = max_msg_size;
997         /*
998          * we used to add sprintf("dn:%s", ads->config.bind_path) here.
999          * but using ads->config.bind_path is the wrong! It should be
1000          * the DN of the user object!
1001          *
1002          * w2k3 gives an error when we send an incorrect DN, but sending nothing
1003          * is ok and matches the information flow used in GSS-SPNEGO.
1004          */
1005
1006         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1007                           &output_token, &conf_state,
1008                           &input_token);
1009         if (gss_rc) {
1010                 status = ADS_ERROR_GSS(gss_rc, minor_status);
1011                 goto failed;
1012         }
1013
1014         free(output_token.value);
1015
1016         cred.bv_val = (char *)input_token.value;
1017         cred.bv_len = input_token.length;
1018
1019         rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
1020                               &scred);
1021         gss_release_buffer(&minor_status, &input_token);
1022         status = ADS_ERROR(rc);
1023         if (!ADS_ERR_OK(status)) {
1024                 goto failed;
1025         }
1026
1027         if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1028                 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1029                                              (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1030                                              GSS_C_QOP_DEFAULT,
1031                                              max_msg_size, &ads->ldap.out.max);
1032                 if (gss_rc) {
1033                         status = ADS_ERROR_GSS(gss_rc, minor_status);
1034                         goto failed;
1035                 }
1036
1037                 ads->ldap.out.min = 4;
1038                 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
1039                 ads->ldap.in.min = 4;
1040                 ads->ldap.in.max = max_msg_size;
1041                 ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1042                 /* make sure we don't free context_handle */
1043                 context_handle = GSS_C_NO_CONTEXT;
1044         }
1045 failed:
1046
1047         gss_release_name(&minor_status, &serv_name);
1048         if (context_handle != GSS_C_NO_CONTEXT)
1049                 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1050         krb5_free_principal(ctx, principal);
1051         krb5_free_context(ctx); 
1052
1053         if(scred)
1054                 ber_bvfree(scred);
1055         return status;
1056 }
1057 #endif /* HAVE_GGSAPI */
1058
1059 /* mapping between SASL mechanisms and functions */
1060 static struct {
1061         const char *name;
1062         ADS_STATUS (*fn)(ADS_STRUCT *);
1063 } sasl_mechanisms[] = {
1064         {"GSS-SPNEGO", ads_sasl_spnego_bind},
1065 #ifdef HAVE_GSSAPI
1066         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1067 #endif
1068         {NULL, NULL}
1069 };
1070
1071 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1072 {
1073         const char *attrs[] = {"supportedSASLMechanisms", NULL};
1074         char **values;
1075         ADS_STATUS status;
1076         int i, j;
1077         LDAPMessage *res;
1078
1079         /* get a list of supported SASL mechanisms */
1080         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1081         if (!ADS_ERR_OK(status)) return status;
1082
1083         values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1084
1085         if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1086                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1087         } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1088                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1089         } else {
1090                 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1091         }
1092
1093         /* try our supported mechanisms in order */
1094         for (i=0;sasl_mechanisms[i].name;i++) {
1095                 /* see if the server supports it */
1096                 for (j=0;values && values[j];j++) {
1097                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1098                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1099                                 status = sasl_mechanisms[i].fn(ads);
1100                                 ldap_value_free(values);
1101                                 ldap_msgfree(res);
1102                                 return status;
1103                         }
1104                 }
1105         }
1106
1107         ldap_value_free(values);
1108         ldap_msgfree(res);
1109         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1110 }
1111
1112 #endif /* HAVE_LDAP */
1113