source: branches/samba-3.3.x/source/libads/sasl.c@ 206

Last change on this file since 206 was 206, checked in by Herwig Bauernfeind, 16 years ago

Import Samba 3.3 branch at 3.0.0 level (psmedley's port)

File size: 29.3 KB
Line 
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
24static 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
63static 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
98static 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
109static 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*/
120static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
121{
122 DATA_BLOB msg1 = data_blob_null;
123 DATA_BLOB blob = data_blob_null;
124 DATA_BLOB blob_in = data_blob_null;
125 DATA_BLOB blob_out = data_blob_null;
126 struct berval cred, *scred = NULL;
127 int rc;
128 NTSTATUS nt_status;
129 ADS_STATUS status;
130 int turn = 1;
131 uint32 features = 0;
132
133 struct ntlmssp_state *ntlmssp_state;
134
135 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
136 return ADS_ERROR_NT(nt_status);
137 }
138 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
139
140 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
141 return ADS_ERROR_NT(nt_status);
142 }
143 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
144 return ADS_ERROR_NT(nt_status);
145 }
146 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
147 return ADS_ERROR_NT(nt_status);
148 }
149
150 switch (ads->ldap.wrap_type) {
151 case ADS_SASLWRAP_TYPE_SEAL:
152 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
153 break;
154 case ADS_SASLWRAP_TYPE_SIGN:
155 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
156 features = NTLMSSP_FEATURE_SIGN;
157 } else {
158 /*
159 * windows servers are broken with sign only,
160 * so we need to use seal here too
161 */
162 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
163 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
164 }
165 break;
166 case ADS_SASLWRAP_TYPE_PLAIN:
167 break;
168 }
169
170 ntlmssp_want_feature(ntlmssp_state, features);
171
172 blob_in = data_blob_null;
173
174 do {
175 nt_status = ntlmssp_update(ntlmssp_state,
176 blob_in, &blob_out);
177 data_blob_free(&blob_in);
178 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
179 || NT_STATUS_IS_OK(nt_status))
180 && blob_out.length) {
181 if (turn == 1) {
182 /* and wrap it in a SPNEGO wrapper */
183 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
184 } else {
185 /* wrap it in SPNEGO */
186 msg1 = spnego_gen_auth(blob_out);
187 }
188
189 data_blob_free(&blob_out);
190
191 cred.bv_val = (char *)msg1.data;
192 cred.bv_len = msg1.length;
193 scred = NULL;
194 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
195 data_blob_free(&msg1);
196 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
197 if (scred) {
198 ber_bvfree(scred);
199 }
200
201 ntlmssp_end(&ntlmssp_state);
202 return ADS_ERROR(rc);
203 }
204 if (scred) {
205 blob = data_blob(scred->bv_val, scred->bv_len);
206 ber_bvfree(scred);
207 } else {
208 blob = data_blob_null;
209 }
210
211 } else {
212
213 ntlmssp_end(&ntlmssp_state);
214 data_blob_free(&blob_out);
215 return ADS_ERROR_NT(nt_status);
216 }
217
218 if ((turn == 1) &&
219 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
220 DATA_BLOB tmp_blob = data_blob_null;
221 /* the server might give us back two challenges */
222 if (!spnego_parse_challenge(blob, &blob_in,
223 &tmp_blob)) {
224
225 ntlmssp_end(&ntlmssp_state);
226 data_blob_free(&blob);
227 DEBUG(3,("Failed to parse challenges\n"));
228 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
229 }
230 data_blob_free(&tmp_blob);
231 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
232 if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
233 &blob_in)) {
234
235 ntlmssp_end(&ntlmssp_state);
236 data_blob_free(&blob);
237 DEBUG(3,("Failed to parse auth response\n"));
238 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
239 }
240 }
241 data_blob_free(&blob);
242 data_blob_free(&blob_out);
243 turn++;
244 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
245
246 /* we have a reference conter on ntlmssp_state, if we are signing
247 then the state will be kept by the signing engine */
248
249 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
250 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
251 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
252 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
253 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
254 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
255 if (!ADS_ERR_OK(status)) {
256 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
257 ads_errstr(status)));
258 ntlmssp_end(&ntlmssp_state);
259 return status;
260 }
261 } else {
262 ntlmssp_end(&ntlmssp_state);
263 }
264
265 return ADS_ERROR(rc);
266}
267
268#ifdef HAVE_GSSAPI
269static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
270{
271 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
272 ADS_STATUS status;
273 int gss_rc;
274 uint32 minor_status;
275 gss_buffer_desc unwrapped, wrapped;
276 int conf_req_flag, conf_state;
277
278 unwrapped.value = buf;
279 unwrapped.length = len;
280
281 /* for now request sign and seal */
282 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
283
284 gss_rc = gss_wrap(&minor_status, context_handle,
285 conf_req_flag, GSS_C_QOP_DEFAULT,
286 &unwrapped, &conf_state,
287 &wrapped);
288 status = ADS_ERROR_GSS(gss_rc, minor_status);
289 if (!ADS_ERR_OK(status)) return status;
290
291 if (conf_req_flag && conf_state == 0) {
292 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
293 }
294
295 if ((ads->ldap.out.size - 4) < wrapped.length) {
296 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
297 }
298
299 /* copy the wrapped blob to the right location */
300 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
301
302 /* set how many bytes must be written to the underlying socket */
303 ads->ldap.out.left = 4 + wrapped.length;
304
305 gss_release_buffer(&minor_status, &wrapped);
306
307 return ADS_SUCCESS;
308}
309
310static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
311{
312 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
313 ADS_STATUS status;
314 int gss_rc;
315 uint32 minor_status;
316 gss_buffer_desc unwrapped, wrapped;
317 int conf_state;
318
319 wrapped.value = ads->ldap.in.buf + 4;
320 wrapped.length = ads->ldap.in.ofs - 4;
321
322 gss_rc = gss_unwrap(&minor_status, context_handle,
323 &wrapped, &unwrapped,
324 &conf_state, GSS_C_QOP_DEFAULT);
325 status = ADS_ERROR_GSS(gss_rc, minor_status);
326 if (!ADS_ERR_OK(status)) return status;
327
328 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
329 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
330 }
331
332 if (wrapped.length < unwrapped.length) {
333 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
334 }
335
336 /* copy the wrapped blob to the right location */
337 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
338
339 /* set how many bytes must be written to the underlying socket */
340 ads->ldap.in.left = unwrapped.length;
341 ads->ldap.in.ofs = 4;
342
343 gss_release_buffer(&minor_status, &unwrapped);
344
345 return ADS_SUCCESS;
346}
347
348static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
349{
350 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
351 uint32 minor_status;
352
353 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
354
355 ads->ldap.wrap_ops = NULL;
356 ads->ldap.wrap_private_data = NULL;
357}
358
359static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
360 .name = "gssapi",
361 .wrap = ads_sasl_gssapi_wrap,
362 .unwrap = ads_sasl_gssapi_unwrap,
363 .disconnect = ads_sasl_gssapi_disconnect
364};
365
366/*
367 perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
368*/
369static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
370{
371 ADS_STATUS status;
372 bool ok;
373 uint32 minor_status;
374 int gss_rc, rc;
375 gss_OID_desc krb5_mech_type =
376 {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
377 gss_OID mech_type = &krb5_mech_type;
378 gss_OID actual_mech_type = GSS_C_NULL_OID;
379 const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
380 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
381 gss_buffer_desc input_token, output_token;
382 uint32 req_flags, ret_flags;
383 uint32 req_tmp, ret_tmp;
384 DATA_BLOB unwrapped;
385 DATA_BLOB wrapped;
386 struct berval cred, *scred = NULL;
387
388 input_token.value = NULL;
389 input_token.length = 0;
390
391 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
392 switch (ads->ldap.wrap_type) {
393 case ADS_SASLWRAP_TYPE_SEAL:
394 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
395 break;
396 case ADS_SASLWRAP_TYPE_SIGN:
397 req_flags |= GSS_C_INTEG_FLAG;
398 break;
399 case ADS_SASLWRAP_TYPE_PLAIN:
400 break;
401 }
402
403 /* Note: here we explicit ask for the krb5 mech_type */
404 gss_rc = gss_init_sec_context(&minor_status,
405 GSS_C_NO_CREDENTIAL,
406 &context_handle,
407 serv_name,
408 mech_type,
409 req_flags,
410 0,
411 NULL,
412 &input_token,
413 &actual_mech_type,
414 &output_token,
415 &ret_flags,
416 NULL);
417 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
418 status = ADS_ERROR_GSS(gss_rc, minor_status);
419 goto failed;
420 }
421
422 /*
423 * As some gssapi krb5 mech implementations
424 * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
425 * to req_flags internaly, it's not possible to
426 * use plain or signing only connection via
427 * the gssapi interface.
428 *
429 * Because of this we need to check it the ret_flags
430 * has more flags as req_flags and correct the value
431 * of ads->ldap.wrap_type.
432 *
433 * I ads->auth.flags has ADS_AUTH_SASL_FORCE
434 * we need to give an error.
435 */
436 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
437 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
438
439 if (req_tmp == ret_tmp) {
440 /* everythings fine... */
441
442 } else if (req_flags & GSS_C_CONF_FLAG) {
443 /*
444 * here we wanted sealing but didn't got it
445 * from the gssapi library
446 */
447 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
448 goto failed;
449
450 } else if ((req_flags & GSS_C_INTEG_FLAG) &&
451 !(ret_flags & GSS_C_INTEG_FLAG)) {
452 /*
453 * here we wanted siging but didn't got it
454 * from the gssapi library
455 */
456 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
457 goto failed;
458
459 } else if (ret_flags & GSS_C_CONF_FLAG) {
460 /*
461 * here we didn't want sealing
462 * but the gssapi library forces it
463 * so correct the needed wrap_type if
464 * the caller didn't forced siging only
465 */
466 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
467 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
468 goto failed;
469 }
470
471 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
472 req_flags = ret_flags;
473
474 } else if (ret_flags & GSS_C_INTEG_FLAG) {
475 /*
476 * here we didn't want signing
477 * but the gssapi library forces it
478 * so correct the needed wrap_type if
479 * the caller didn't forced plain
480 */
481 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
482 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
483 goto failed;
484 }
485
486 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
487 req_flags = ret_flags;
488 } else {
489 /*
490 * This could (should?) not happen
491 */
492 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
493 goto failed;
494
495 }
496
497 /* and wrap that in a shiny SPNEGO wrapper */
498 unwrapped = data_blob_const(output_token.value, output_token.length);
499 wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
500 gss_release_buffer(&minor_status, &output_token);
501 if (unwrapped.length > wrapped.length) {
502 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
503 goto failed;
504 }
505
506 cred.bv_val = (char *)wrapped.data;
507 cred.bv_len = wrapped.length;
508
509 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
510 &scred);
511 data_blob_free(&wrapped);
512 if (rc != LDAP_SUCCESS) {
513 status = ADS_ERROR(rc);
514 goto failed;
515 }
516
517 if (scred) {
518 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
519 } else {
520 wrapped = data_blob_null;
521 }
522
523 ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
524 OID_KERBEROS5_OLD,
525 &unwrapped);
526 if (scred) ber_bvfree(scred);
527 if (!ok) {
528 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
529 goto failed;
530 }
531
532 input_token.value = unwrapped.data;
533 input_token.length = unwrapped.length;
534
535 /*
536 * As we asked for mutal authentication
537 * we need to pass the servers response
538 * to gssapi
539 */
540 gss_rc = gss_init_sec_context(&minor_status,
541 GSS_C_NO_CREDENTIAL,
542 &context_handle,
543 serv_name,
544 mech_type,
545 req_flags,
546 0,
547 NULL,
548 &input_token,
549 &actual_mech_type,
550 &output_token,
551 &ret_flags,
552 NULL);
553 data_blob_free(&unwrapped);
554 if (gss_rc) {
555 status = ADS_ERROR_GSS(gss_rc, minor_status);
556 goto failed;
557 }
558
559 gss_release_buffer(&minor_status, &output_token);
560
561 /*
562 * If we the sign and seal options
563 * doesn't match after getting the response
564 * from the server, we don't want to use the connection
565 */
566 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
567 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
568
569 if (req_tmp != ret_tmp) {
570 /* everythings fine... */
571 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
572 goto failed;
573 }
574
575 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
576 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
577
578 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
579 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
580 GSS_C_QOP_DEFAULT,
581 max_msg_size, &ads->ldap.out.max_unwrapped);
582 if (gss_rc) {
583 status = ADS_ERROR_GSS(gss_rc, minor_status);
584 goto failed;
585 }
586
587 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
588 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
589 ads->ldap.in.max_wrapped = max_msg_size;
590 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
591 if (!ADS_ERR_OK(status)) {
592 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
593 ads_errstr(status)));
594 goto failed;
595 }
596 /* make sure we don't free context_handle */
597 context_handle = GSS_C_NO_CONTEXT;
598 }
599
600 status = ADS_SUCCESS;
601
602failed:
603 if (context_handle != GSS_C_NO_CONTEXT)
604 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
605 return status;
606}
607
608#endif /* HAVE_GSSAPI */
609
610#ifdef HAVE_KRB5
611struct ads_service_principal {
612 char *string;
613#ifdef HAVE_GSSAPI
614 gss_name_t name;
615#endif
616};
617
618static void ads_free_service_principal(struct ads_service_principal *p)
619{
620 SAFE_FREE(p->string);
621
622#ifdef HAVE_GSSAPI
623 if (p->name) {
624 uint32 minor_status;
625 gss_release_name(&minor_status, &p->name);
626 }
627#endif
628 ZERO_STRUCTP(p);
629}
630
631static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
632 const char *given_principal,
633 struct ads_service_principal *p)
634{
635 ADS_STATUS status;
636#ifdef HAVE_GSSAPI
637 gss_buffer_desc input_name;
638 /* GSS_KRB5_NT_PRINCIPAL_NAME */
639 gss_OID_desc nt_principal =
640 {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
641 uint32 minor_status;
642 int gss_rc;
643#endif
644
645 ZERO_STRUCTP(p);
646
647 /* I've seen a child Windows 2000 domain not send
648 the principal name back in the first round of
649 the SASL bind reply. So we guess based on server
650 name and realm. --jerry */
651 /* Also try best guess when we get the w2k8 ignore
652 principal back - gd */
653
654 if (!given_principal ||
655 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
656
657 status = ads_guess_service_principal(ads, &p->string);
658 if (!ADS_ERR_OK(status)) {
659 return status;
660 }
661 } else {
662 p->string = SMB_STRDUP(given_principal);
663 if (!p->string) {
664 return ADS_ERROR(LDAP_NO_MEMORY);
665 }
666 }
667
668#ifdef HAVE_GSSAPI
669 input_name.value = p->string;
670 input_name.length = strlen(p->string);
671
672 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
673 if (gss_rc) {
674 ads_free_service_principal(p);
675 return ADS_ERROR_GSS(gss_rc, minor_status);
676 }
677#endif
678
679 return ADS_SUCCESS;
680}
681
682/*
683 perform a LDAP/SASL/SPNEGO/KRB5 bind
684*/
685static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
686{
687 DATA_BLOB blob = data_blob_null;
688 struct berval cred, *scred = NULL;
689 DATA_BLOB session_key = data_blob_null;
690 int rc;
691
692 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
693 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
694 }
695
696 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
697 &ads->auth.tgs_expire);
698
699 if (rc) {
700 return ADS_ERROR_KRB5(rc);
701 }
702
703 /* now send the auth packet and we should be done */
704 cred.bv_val = (char *)blob.data;
705 cred.bv_len = blob.length;
706
707 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
708
709 data_blob_free(&blob);
710 data_blob_free(&session_key);
711 if(scred)
712 ber_bvfree(scred);
713
714 return ADS_ERROR(rc);
715}
716
717static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
718 struct ads_service_principal *p)
719{
720#ifdef HAVE_GSSAPI
721 /*
722 * we only use the gsskrb5 based implementation
723 * when sasl sign or seal is requested.
724 *
725 * This has the following reasons:
726 * - it's likely that the gssapi krb5 mech implementation
727 * doesn't support to negotiate plain connections
728 * - the ads_sasl_spnego_rawkrb5_bind is more robust
729 * against clock skew errors
730 */
731 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
732 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
733 }
734#endif
735 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
736}
737#endif /* HAVE_KRB5 */
738
739/*
740 this performs a SASL/SPNEGO bind
741*/
742static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
743{
744 struct berval *scred=NULL;
745 int rc, i;
746 ADS_STATUS status;
747 DATA_BLOB blob;
748 char *given_principal = NULL;
749 char *OIDs[ASN1_MAX_OIDS];
750#ifdef HAVE_KRB5
751 bool got_kerberos_mechanism = False;
752#endif
753
754 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
755
756 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
757 status = ADS_ERROR(rc);
758 goto failed;
759 }
760
761 blob = data_blob(scred->bv_val, scred->bv_len);
762
763 ber_bvfree(scred);
764
765#if 0
766 file_save("sasl_spnego.dat", blob.data, blob.length);
767#endif
768
769 /* the server sent us the first part of the SPNEGO exchange in the negprot
770 reply */
771 if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) {
772 data_blob_free(&blob);
773 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
774 goto failed;
775 }
776 data_blob_free(&blob);
777
778 /* make sure the server understands kerberos */
779 for (i=0;OIDs[i];i++) {
780 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
781#ifdef HAVE_KRB5
782 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
783 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
784 got_kerberos_mechanism = True;
785 }
786#endif
787 free(OIDs[i]);
788 }
789 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
790
791#ifdef HAVE_KRB5
792 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
793 got_kerberos_mechanism)
794 {
795 struct ads_service_principal p;
796
797 status = ads_generate_service_principal(ads, given_principal, &p);
798 SAFE_FREE(given_principal);
799 if (!ADS_ERR_OK(status)) {
800 return status;
801 }
802
803 status = ads_sasl_spnego_krb5_bind(ads, &p);
804 if (ADS_ERR_OK(status)) {
805 ads_free_service_principal(&p);
806 return status;
807 }
808
809 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
810 "calling kinit\n", ads_errstr(status)));
811
812 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
813
814 if (ADS_ERR_OK(status)) {
815 status = ads_sasl_spnego_krb5_bind(ads, &p);
816 if (!ADS_ERR_OK(status)) {
817 DEBUG(0,("kinit succeeded but "
818 "ads_sasl_spnego_krb5_bind failed: %s\n",
819 ads_errstr(status)));
820 }
821 }
822
823 ads_free_service_principal(&p);
824
825 /* only fallback to NTLMSSP if allowed */
826 if (ADS_ERR_OK(status) ||
827 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
828 return status;
829 }
830 } else
831#endif
832 {
833 SAFE_FREE(given_principal);
834 }
835
836 /* lets do NTLMSSP ... this has the big advantage that we don't need
837 to sync clocks, and we don't rely on special versions of the krb5
838 library for HMAC_MD4 encryption */
839 return ads_sasl_spnego_ntlmssp_bind(ads);
840
841failed:
842 return status;
843}
844
845#ifdef HAVE_GSSAPI
846#define MAX_GSS_PASSES 3
847
848/* this performs a SASL/gssapi bind
849 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
850 is very dependent on correctly configured DNS whereas
851 this routine is much less fragile
852 see RFC2078 and RFC2222 for details
853*/
854static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
855{
856 uint32 minor_status;
857 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
858 gss_OID mech_type = GSS_C_NULL_OID;
859 gss_buffer_desc output_token, input_token;
860 uint32 req_flags, ret_flags;
861 int conf_state;
862 struct berval cred;
863 struct berval *scred = NULL;
864 int i=0;
865 int gss_rc, rc;
866 uint8 *p;
867 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
868 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
869 ADS_STATUS status;
870
871 input_token.value = NULL;
872 input_token.length = 0;
873
874 /*
875 * Note: here we always ask the gssapi for sign and seal
876 * as this is negotiated later after the mutal
877 * authentication
878 */
879 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
880
881 for (i=0; i < MAX_GSS_PASSES; i++) {
882 gss_rc = gss_init_sec_context(&minor_status,
883 GSS_C_NO_CREDENTIAL,
884 &context_handle,
885 serv_name,
886 mech_type,
887 req_flags,
888 0,
889 NULL,
890 &input_token,
891 NULL,
892 &output_token,
893 &ret_flags,
894 NULL);
895 if (scred) {
896 ber_bvfree(scred);
897 scred = NULL;
898 }
899 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
900 status = ADS_ERROR_GSS(gss_rc, minor_status);
901 goto failed;
902 }
903
904 cred.bv_val = (char *)output_token.value;
905 cred.bv_len = output_token.length;
906
907 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
908 &scred);
909 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
910 status = ADS_ERROR(rc);
911 goto failed;
912 }
913
914 if (output_token.value) {
915 gss_release_buffer(&minor_status, &output_token);
916 }
917
918 if (scred) {
919 input_token.value = scred->bv_val;
920 input_token.length = scred->bv_len;
921 } else {
922 input_token.value = NULL;
923 input_token.length = 0;
924 }
925
926 if (gss_rc == 0) break;
927 }
928
929 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
930 &conf_state,NULL);
931 if (scred) {
932 ber_bvfree(scred);
933 scred = NULL;
934 }
935 if (gss_rc) {
936 status = ADS_ERROR_GSS(gss_rc, minor_status);
937 goto failed;
938 }
939
940 p = (uint8 *)output_token.value;
941
942#if 0
943 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
944#endif
945
946 if (p) {
947 wrap_type = CVAL(p,0);
948 SCVAL(p,0,0);
949 max_msg_size = RIVAL(p,0);
950 }
951
952 gss_release_buffer(&minor_status, &output_token);
953
954 if (!(wrap_type & ads->ldap.wrap_type)) {
955 /*
956 * the server doesn't supports the wrap
957 * type we want :-(
958 */
959 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
960 ads->ldap.wrap_type, wrap_type));
961 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
962 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
963 goto failed;
964 }
965
966 /* 0x58 is the minimum windows accepts */
967 if (max_msg_size < 0x58) {
968 max_msg_size = 0x58;
969 }
970
971 output_token.length = 4;
972 output_token.value = SMB_MALLOC(output_token.length);
973 p = (uint8 *)output_token.value;
974
975 RSIVAL(p,0,max_msg_size);
976 SCVAL(p,0,ads->ldap.wrap_type);
977
978 /*
979 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
980 * but using ads->config.bind_path is the wrong! It should be
981 * the DN of the user object!
982 *
983 * w2k3 gives an error when we send an incorrect DN, but sending nothing
984 * is ok and matches the information flow used in GSS-SPNEGO.
985 */
986
987 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
988 &output_token, &conf_state,
989 &input_token);
990 if (gss_rc) {
991 status = ADS_ERROR_GSS(gss_rc, minor_status);
992 goto failed;
993 }
994
995 free(output_token.value);
996
997 cred.bv_val = (char *)input_token.value;
998 cred.bv_len = input_token.length;
999
1000 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1001 &scred);
1002 gss_release_buffer(&minor_status, &input_token);
1003 status = ADS_ERROR(rc);
1004 if (!ADS_ERR_OK(status)) {
1005 goto failed;
1006 }
1007
1008 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1009 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1010 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1011 GSS_C_QOP_DEFAULT,
1012 max_msg_size, &ads->ldap.out.max_unwrapped);
1013 if (gss_rc) {
1014 status = ADS_ERROR_GSS(gss_rc, minor_status);
1015 goto failed;
1016 }
1017
1018 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1019 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1020 ads->ldap.in.max_wrapped = max_msg_size;
1021 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1022 if (!ADS_ERR_OK(status)) {
1023 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1024 ads_errstr(status)));
1025 goto failed;
1026 }
1027 /* make sure we don't free context_handle */
1028 context_handle = GSS_C_NO_CONTEXT;
1029 }
1030
1031failed:
1032
1033 if (context_handle != GSS_C_NO_CONTEXT)
1034 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1035
1036 if(scred)
1037 ber_bvfree(scred);
1038 return status;
1039}
1040
1041static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1042{
1043 ADS_STATUS status;
1044 struct ads_service_principal p;
1045
1046 status = ads_generate_service_principal(ads, NULL, &p);
1047 if (!ADS_ERR_OK(status)) {
1048 return status;
1049 }
1050
1051 status = ads_sasl_gssapi_do_bind(ads, p.name);
1052 if (ADS_ERR_OK(status)) {
1053 ads_free_service_principal(&p);
1054 return status;
1055 }
1056
1057 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1058 "calling kinit\n", ads_errstr(status)));
1059
1060 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1061
1062 if (ADS_ERR_OK(status)) {
1063 status = ads_sasl_gssapi_do_bind(ads, p.name);
1064 }
1065
1066 ads_free_service_principal(&p);
1067
1068 return status;
1069}
1070
1071#endif /* HAVE_GSSAPI */
1072
1073/* mapping between SASL mechanisms and functions */
1074static struct {
1075 const char *name;
1076 ADS_STATUS (*fn)(ADS_STRUCT *);
1077} sasl_mechanisms[] = {
1078 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1079#ifdef HAVE_GSSAPI
1080 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1081#endif
1082 {NULL, NULL}
1083};
1084
1085ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1086{
1087 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1088 char **values;
1089 ADS_STATUS status;
1090 int i, j;
1091 LDAPMessage *res;
1092
1093 /* get a list of supported SASL mechanisms */
1094 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1095 if (!ADS_ERR_OK(status)) return status;
1096
1097 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1098
1099 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1100 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1101 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1102 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1103 } else {
1104 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1105 }
1106
1107 /* try our supported mechanisms in order */
1108 for (i=0;sasl_mechanisms[i].name;i++) {
1109 /* see if the server supports it */
1110 for (j=0;values && values[j];j++) {
1111 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1112 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1113 status = sasl_mechanisms[i].fn(ads);
1114 ldap_value_free(values);
1115 ldap_msgfree(res);
1116 return status;
1117 }
1118 }
1119 }
1120
1121 ldap_value_free(values);
1122 ldap_msgfree(res);
1123 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1124}
1125
1126#endif /* HAVE_LDAP */
1127
Note: See TracBrowser for help on using the repository browser.