source: trunk/server/source3/libads/sasl.c@ 429

Last change on this file since 429 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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