s3: Slightly simplify the logic of check_password_complexity()
[Samba/ekacnet.git] / source3 / libads / sasl.c
blobd6e4f68544ab275d23e274cfeef9ae9622fea65d
1 /*
2 Unix SMB/CIFS implementation.
3 ads sasl code
4 Copyright (C) Andrew Tridgell 2001
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.
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.
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/>.
20 #include "includes.h"
21 #include "../libcli/auth/spnego.h"
22 #include "ntlmssp.h"
24 #ifdef HAVE_LDAP
26 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
28 struct ntlmssp_state *ntlmssp_state =
29 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
30 ADS_STATUS status;
31 NTSTATUS nt_status;
32 DATA_BLOB sig;
33 uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
35 /* copy the data to the right location */
36 memcpy(dptr, buf, len);
38 /* create the signature and may encrypt the data */
39 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
40 nt_status = ntlmssp_seal_packet(ntlmssp_state,
41 dptr, len,
42 dptr, len,
43 &sig);
44 } else {
45 nt_status = ntlmssp_sign_packet(ntlmssp_state,
46 dptr, len,
47 dptr, len,
48 &sig);
50 status = ADS_ERROR_NT(nt_status);
51 if (!ADS_ERR_OK(status)) return status;
53 /* copy the signature to the right location */
54 memcpy(ads->ldap.out.buf + 4,
55 sig.data, NTLMSSP_SIG_SIZE);
57 data_blob_free(&sig);
59 /* set how many bytes must be written to the underlying socket */
60 ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
62 return ADS_SUCCESS;
65 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
67 struct ntlmssp_state *ntlmssp_state =
68 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
69 ADS_STATUS status;
70 NTSTATUS nt_status;
71 DATA_BLOB sig;
72 uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
73 uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
75 /* wrap the signature into a DATA_BLOB */
76 sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
78 /* verify the signature and maybe decrypt the data */
79 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
80 nt_status = ntlmssp_unseal_packet(ntlmssp_state,
81 dptr, dlen,
82 dptr, dlen,
83 &sig);
84 } else {
85 nt_status = ntlmssp_check_packet(ntlmssp_state,
86 dptr, dlen,
87 dptr, dlen,
88 &sig);
90 status = ADS_ERROR_NT(nt_status);
91 if (!ADS_ERR_OK(status)) return status;
93 /* set the amount of bytes for the upper layer and set the ofs to the data */
94 ads->ldap.in.left = dlen;
95 ads->ldap.in.ofs = 4 + NTLMSSP_SIG_SIZE;
97 return ADS_SUCCESS;
100 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
102 struct ntlmssp_state *ntlmssp_state =
103 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
105 ntlmssp_end(&ntlmssp_state);
107 ads->ldap.wrap_ops = NULL;
108 ads->ldap.wrap_private_data = NULL;
111 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
112 .name = "ntlmssp",
113 .wrap = ads_sasl_ntlmssp_wrap,
114 .unwrap = ads_sasl_ntlmssp_unwrap,
115 .disconnect = ads_sasl_ntlmssp_disconnect
119 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
120 we fit on one socket??)
122 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
124 DATA_BLOB msg1 = data_blob_null;
125 DATA_BLOB blob = data_blob_null;
126 DATA_BLOB blob_in = data_blob_null;
127 DATA_BLOB blob_out = data_blob_null;
128 struct berval cred, *scred = NULL;
129 int rc;
130 NTSTATUS nt_status;
131 ADS_STATUS status;
132 int turn = 1;
133 uint32 features = 0;
135 struct ntlmssp_state *ntlmssp_state;
137 nt_status = ntlmssp_client_start(NULL,
138 global_myname(),
139 lp_workgroup(),
140 lp_client_ntlmv2_auth(),
141 &ntlmssp_state);
142 if (!NT_STATUS_IS_OK(nt_status)) {
143 return ADS_ERROR_NT(nt_status);
145 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
147 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
148 return ADS_ERROR_NT(nt_status);
150 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
151 return ADS_ERROR_NT(nt_status);
153 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
154 return ADS_ERROR_NT(nt_status);
157 switch (ads->ldap.wrap_type) {
158 case ADS_SASLWRAP_TYPE_SEAL:
159 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
160 break;
161 case ADS_SASLWRAP_TYPE_SIGN:
162 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
163 features = NTLMSSP_FEATURE_SIGN;
164 } else {
166 * windows servers are broken with sign only,
167 * so we need to use seal here too
169 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
170 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
172 break;
173 case ADS_SASLWRAP_TYPE_PLAIN:
174 break;
177 ntlmssp_want_feature(ntlmssp_state, features);
179 blob_in = data_blob_null;
181 do {
182 nt_status = ntlmssp_update(ntlmssp_state,
183 blob_in, &blob_out);
184 data_blob_free(&blob_in);
185 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
186 || NT_STATUS_IS_OK(nt_status))
187 && blob_out.length) {
188 if (turn == 1) {
189 /* and wrap it in a SPNEGO wrapper */
190 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
191 } else {
192 /* wrap it in SPNEGO */
193 msg1 = spnego_gen_auth(blob_out);
196 data_blob_free(&blob_out);
198 cred.bv_val = (char *)msg1.data;
199 cred.bv_len = msg1.length;
200 scred = NULL;
201 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
202 data_blob_free(&msg1);
203 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
204 if (scred) {
205 ber_bvfree(scred);
208 ntlmssp_end(&ntlmssp_state);
209 return ADS_ERROR(rc);
211 if (scred) {
212 blob = data_blob(scred->bv_val, scred->bv_len);
213 ber_bvfree(scred);
214 } else {
215 blob = data_blob_null;
218 } else {
220 ntlmssp_end(&ntlmssp_state);
221 data_blob_free(&blob_out);
222 return ADS_ERROR_NT(nt_status);
225 if ((turn == 1) &&
226 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
227 DATA_BLOB tmp_blob = data_blob_null;
228 /* the server might give us back two challenges */
229 if (!spnego_parse_challenge(blob, &blob_in,
230 &tmp_blob)) {
232 ntlmssp_end(&ntlmssp_state);
233 data_blob_free(&blob);
234 DEBUG(3,("Failed to parse challenges\n"));
235 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
237 data_blob_free(&tmp_blob);
238 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
239 if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
240 &blob_in)) {
242 ntlmssp_end(&ntlmssp_state);
243 data_blob_free(&blob);
244 DEBUG(3,("Failed to parse auth response\n"));
245 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
248 data_blob_free(&blob);
249 data_blob_free(&blob_out);
250 turn++;
251 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
253 /* we have a reference conter on ntlmssp_state, if we are signing
254 then the state will be kept by the signing engine */
256 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
257 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
258 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
259 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
260 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
261 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
262 if (!ADS_ERR_OK(status)) {
263 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
264 ads_errstr(status)));
265 ntlmssp_end(&ntlmssp_state);
266 return status;
268 } else {
269 ntlmssp_end(&ntlmssp_state);
272 return ADS_ERROR(rc);
275 #ifdef HAVE_GSSAPI
276 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
278 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
279 ADS_STATUS status;
280 int gss_rc;
281 uint32 minor_status;
282 gss_buffer_desc unwrapped, wrapped;
283 int conf_req_flag, conf_state;
285 unwrapped.value = buf;
286 unwrapped.length = len;
288 /* for now request sign and seal */
289 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
291 gss_rc = gss_wrap(&minor_status, context_handle,
292 conf_req_flag, GSS_C_QOP_DEFAULT,
293 &unwrapped, &conf_state,
294 &wrapped);
295 status = ADS_ERROR_GSS(gss_rc, minor_status);
296 if (!ADS_ERR_OK(status)) return status;
298 if (conf_req_flag && conf_state == 0) {
299 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
302 if ((ads->ldap.out.size - 4) < wrapped.length) {
303 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
306 /* copy the wrapped blob to the right location */
307 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
309 /* set how many bytes must be written to the underlying socket */
310 ads->ldap.out.left = 4 + wrapped.length;
312 gss_release_buffer(&minor_status, &wrapped);
314 return ADS_SUCCESS;
317 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
319 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
320 ADS_STATUS status;
321 int gss_rc;
322 uint32 minor_status;
323 gss_buffer_desc unwrapped, wrapped;
324 int conf_state;
326 wrapped.value = ads->ldap.in.buf + 4;
327 wrapped.length = ads->ldap.in.ofs - 4;
329 gss_rc = gss_unwrap(&minor_status, context_handle,
330 &wrapped, &unwrapped,
331 &conf_state, GSS_C_QOP_DEFAULT);
332 status = ADS_ERROR_GSS(gss_rc, minor_status);
333 if (!ADS_ERR_OK(status)) return status;
335 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
336 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
339 if (wrapped.length < unwrapped.length) {
340 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
343 /* copy the wrapped blob to the right location */
344 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
346 /* set how many bytes must be written to the underlying socket */
347 ads->ldap.in.left = unwrapped.length;
348 ads->ldap.in.ofs = 4;
350 gss_release_buffer(&minor_status, &unwrapped);
352 return ADS_SUCCESS;
355 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
357 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
358 uint32 minor_status;
360 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
362 ads->ldap.wrap_ops = NULL;
363 ads->ldap.wrap_private_data = NULL;
366 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
367 .name = "gssapi",
368 .wrap = ads_sasl_gssapi_wrap,
369 .unwrap = ads_sasl_gssapi_unwrap,
370 .disconnect = ads_sasl_gssapi_disconnect
374 perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
376 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
378 ADS_STATUS status;
379 bool ok;
380 uint32 minor_status;
381 int gss_rc, rc;
382 gss_OID_desc krb5_mech_type =
383 {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
384 gss_OID mech_type = &krb5_mech_type;
385 gss_OID actual_mech_type = GSS_C_NULL_OID;
386 const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
387 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
388 gss_buffer_desc input_token, output_token;
389 uint32 req_flags, ret_flags;
390 uint32 req_tmp, ret_tmp;
391 DATA_BLOB unwrapped;
392 DATA_BLOB wrapped;
393 struct berval cred, *scred = NULL;
395 input_token.value = NULL;
396 input_token.length = 0;
398 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
399 switch (ads->ldap.wrap_type) {
400 case ADS_SASLWRAP_TYPE_SEAL:
401 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
402 break;
403 case ADS_SASLWRAP_TYPE_SIGN:
404 req_flags |= GSS_C_INTEG_FLAG;
405 break;
406 case ADS_SASLWRAP_TYPE_PLAIN:
407 break;
410 /* Note: here we explicit ask for the krb5 mech_type */
411 gss_rc = gss_init_sec_context(&minor_status,
412 GSS_C_NO_CREDENTIAL,
413 &context_handle,
414 serv_name,
415 mech_type,
416 req_flags,
418 NULL,
419 &input_token,
420 &actual_mech_type,
421 &output_token,
422 &ret_flags,
423 NULL);
424 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
425 status = ADS_ERROR_GSS(gss_rc, minor_status);
426 goto failed;
430 * As some gssapi krb5 mech implementations
431 * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
432 * to req_flags internaly, it's not possible to
433 * use plain or signing only connection via
434 * the gssapi interface.
436 * Because of this we need to check it the ret_flags
437 * has more flags as req_flags and correct the value
438 * of ads->ldap.wrap_type.
440 * I ads->auth.flags has ADS_AUTH_SASL_FORCE
441 * we need to give an error.
443 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
444 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
446 if (req_tmp == ret_tmp) {
447 /* everythings fine... */
449 } else if (req_flags & GSS_C_CONF_FLAG) {
451 * here we wanted sealing but didn't got it
452 * from the gssapi library
454 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
455 goto failed;
457 } else if ((req_flags & GSS_C_INTEG_FLAG) &&
458 !(ret_flags & GSS_C_INTEG_FLAG)) {
460 * here we wanted siging but didn't got it
461 * from the gssapi library
463 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
464 goto failed;
466 } else if (ret_flags & GSS_C_CONF_FLAG) {
468 * here we didn't want sealing
469 * but the gssapi library forces it
470 * so correct the needed wrap_type if
471 * the caller didn't forced siging only
473 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
474 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
475 goto failed;
478 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
479 req_flags = ret_flags;
481 } else if (ret_flags & GSS_C_INTEG_FLAG) {
483 * here we didn't want signing
484 * but the gssapi library forces it
485 * so correct the needed wrap_type if
486 * the caller didn't forced plain
488 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
489 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
490 goto failed;
493 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
494 req_flags = ret_flags;
495 } else {
497 * This could (should?) not happen
499 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
500 goto failed;
504 /* and wrap that in a shiny SPNEGO wrapper */
505 unwrapped = data_blob_const(output_token.value, output_token.length);
506 wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
507 gss_release_buffer(&minor_status, &output_token);
508 if (unwrapped.length > wrapped.length) {
509 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
510 goto failed;
513 cred.bv_val = (char *)wrapped.data;
514 cred.bv_len = wrapped.length;
516 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
517 &scred);
518 data_blob_free(&wrapped);
519 if (rc != LDAP_SUCCESS) {
520 status = ADS_ERROR(rc);
521 goto failed;
524 if (scred) {
525 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
526 } else {
527 wrapped = data_blob_null;
530 ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
531 OID_KERBEROS5_OLD,
532 &unwrapped);
533 if (scred) ber_bvfree(scred);
534 if (!ok) {
535 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
536 goto failed;
539 input_token.value = unwrapped.data;
540 input_token.length = unwrapped.length;
543 * As we asked for mutal authentication
544 * we need to pass the servers response
545 * to gssapi
547 gss_rc = gss_init_sec_context(&minor_status,
548 GSS_C_NO_CREDENTIAL,
549 &context_handle,
550 serv_name,
551 mech_type,
552 req_flags,
554 NULL,
555 &input_token,
556 &actual_mech_type,
557 &output_token,
558 &ret_flags,
559 NULL);
560 data_blob_free(&unwrapped);
561 if (gss_rc) {
562 status = ADS_ERROR_GSS(gss_rc, minor_status);
563 goto failed;
566 gss_release_buffer(&minor_status, &output_token);
569 * If we the sign and seal options
570 * doesn't match after getting the response
571 * from the server, we don't want to use the connection
573 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
574 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
576 if (req_tmp != ret_tmp) {
577 /* everythings fine... */
578 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
579 goto failed;
582 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
583 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
585 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
586 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
587 GSS_C_QOP_DEFAULT,
588 max_msg_size, &ads->ldap.out.max_unwrapped);
589 if (gss_rc) {
590 status = ADS_ERROR_GSS(gss_rc, minor_status);
591 goto failed;
594 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
595 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
596 ads->ldap.in.max_wrapped = max_msg_size;
597 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
598 if (!ADS_ERR_OK(status)) {
599 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
600 ads_errstr(status)));
601 goto failed;
603 /* make sure we don't free context_handle */
604 context_handle = GSS_C_NO_CONTEXT;
607 status = ADS_SUCCESS;
609 failed:
610 if (context_handle != GSS_C_NO_CONTEXT)
611 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
612 return status;
615 #endif /* HAVE_GSSAPI */
617 #ifdef HAVE_KRB5
618 struct ads_service_principal {
619 char *string;
620 #ifdef HAVE_GSSAPI
621 gss_name_t name;
622 #endif
625 static void ads_free_service_principal(struct ads_service_principal *p)
627 SAFE_FREE(p->string);
629 #ifdef HAVE_GSSAPI
630 if (p->name) {
631 uint32 minor_status;
632 gss_release_name(&minor_status, &p->name);
634 #endif
635 ZERO_STRUCTP(p);
638 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
639 const char *given_principal,
640 struct ads_service_principal *p)
642 ADS_STATUS status;
643 #ifdef HAVE_GSSAPI
644 gss_buffer_desc input_name;
645 /* GSS_KRB5_NT_PRINCIPAL_NAME */
646 gss_OID_desc nt_principal =
647 {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
648 uint32 minor_status;
649 int gss_rc;
650 #endif
652 ZERO_STRUCTP(p);
654 /* I've seen a child Windows 2000 domain not send
655 the principal name back in the first round of
656 the SASL bind reply. So we guess based on server
657 name and realm. --jerry */
658 /* Also try best guess when we get the w2k8 ignore
659 principal back - gd */
661 if (!given_principal ||
662 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
664 status = ads_guess_service_principal(ads, &p->string);
665 if (!ADS_ERR_OK(status)) {
666 return status;
668 } else {
669 p->string = SMB_STRDUP(given_principal);
670 if (!p->string) {
671 return ADS_ERROR(LDAP_NO_MEMORY);
675 #ifdef HAVE_GSSAPI
676 input_name.value = p->string;
677 input_name.length = strlen(p->string);
679 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
680 if (gss_rc) {
681 ads_free_service_principal(p);
682 return ADS_ERROR_GSS(gss_rc, minor_status);
684 #endif
686 return ADS_SUCCESS;
690 perform a LDAP/SASL/SPNEGO/KRB5 bind
692 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
694 DATA_BLOB blob = data_blob_null;
695 struct berval cred, *scred = NULL;
696 DATA_BLOB session_key = data_blob_null;
697 int rc;
699 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
700 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
703 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
704 &ads->auth.tgs_expire);
706 if (rc) {
707 return ADS_ERROR_KRB5(rc);
710 /* now send the auth packet and we should be done */
711 cred.bv_val = (char *)blob.data;
712 cred.bv_len = blob.length;
714 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
716 data_blob_free(&blob);
717 data_blob_free(&session_key);
718 if(scred)
719 ber_bvfree(scred);
721 return ADS_ERROR(rc);
724 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
725 struct ads_service_principal *p)
727 #ifdef HAVE_GSSAPI
729 * we only use the gsskrb5 based implementation
730 * when sasl sign or seal is requested.
732 * This has the following reasons:
733 * - it's likely that the gssapi krb5 mech implementation
734 * doesn't support to negotiate plain connections
735 * - the ads_sasl_spnego_rawkrb5_bind is more robust
736 * against clock skew errors
738 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
739 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
741 #endif
742 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
744 #endif /* HAVE_KRB5 */
747 this performs a SASL/SPNEGO bind
749 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
751 struct berval *scred=NULL;
752 int rc, i;
753 ADS_STATUS status;
754 DATA_BLOB blob;
755 char *given_principal = NULL;
756 char *OIDs[ASN1_MAX_OIDS];
757 #ifdef HAVE_KRB5
758 bool got_kerberos_mechanism = False;
759 #endif
761 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
763 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
764 status = ADS_ERROR(rc);
765 goto failed;
768 blob = data_blob(scred->bv_val, scred->bv_len);
770 ber_bvfree(scred);
772 #if 0
773 file_save("sasl_spnego.dat", blob.data, blob.length);
774 #endif
776 /* the server sent us the first part of the SPNEGO exchange in the negprot
777 reply */
778 if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) {
779 data_blob_free(&blob);
780 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
781 goto failed;
783 data_blob_free(&blob);
785 /* make sure the server understands kerberos */
786 for (i=0;OIDs[i];i++) {
787 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
788 #ifdef HAVE_KRB5
789 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
790 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
791 got_kerberos_mechanism = True;
793 #endif
794 talloc_free(OIDs[i]);
796 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
798 #ifdef HAVE_KRB5
799 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
800 got_kerberos_mechanism)
802 struct ads_service_principal p;
804 status = ads_generate_service_principal(ads, given_principal, &p);
805 TALLOC_FREE(given_principal);
806 if (!ADS_ERR_OK(status)) {
807 return status;
810 status = ads_sasl_spnego_krb5_bind(ads, &p);
811 if (ADS_ERR_OK(status)) {
812 ads_free_service_principal(&p);
813 return status;
816 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
817 "calling kinit\n", ads_errstr(status)));
819 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
821 if (ADS_ERR_OK(status)) {
822 status = ads_sasl_spnego_krb5_bind(ads, &p);
823 if (!ADS_ERR_OK(status)) {
824 DEBUG(0,("kinit succeeded but "
825 "ads_sasl_spnego_krb5_bind failed: %s\n",
826 ads_errstr(status)));
830 ads_free_service_principal(&p);
832 /* only fallback to NTLMSSP if allowed */
833 if (ADS_ERR_OK(status) ||
834 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
835 return status;
837 } else
838 #endif
840 TALLOC_FREE(given_principal);
843 /* lets do NTLMSSP ... this has the big advantage that we don't need
844 to sync clocks, and we don't rely on special versions of the krb5
845 library for HMAC_MD4 encryption */
846 return ads_sasl_spnego_ntlmssp_bind(ads);
848 failed:
849 return status;
852 #ifdef HAVE_GSSAPI
853 #define MAX_GSS_PASSES 3
855 /* this performs a SASL/gssapi bind
856 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
857 is very dependent on correctly configured DNS whereas
858 this routine is much less fragile
859 see RFC2078 and RFC2222 for details
861 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
863 uint32 minor_status;
864 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
865 gss_OID mech_type = GSS_C_NULL_OID;
866 gss_buffer_desc output_token, input_token;
867 uint32 req_flags, ret_flags;
868 int conf_state;
869 struct berval cred;
870 struct berval *scred = NULL;
871 int i=0;
872 int gss_rc, rc;
873 uint8 *p;
874 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
875 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
876 ADS_STATUS status;
878 input_token.value = NULL;
879 input_token.length = 0;
882 * Note: here we always ask the gssapi for sign and seal
883 * as this is negotiated later after the mutal
884 * authentication
886 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
888 for (i=0; i < MAX_GSS_PASSES; i++) {
889 gss_rc = gss_init_sec_context(&minor_status,
890 GSS_C_NO_CREDENTIAL,
891 &context_handle,
892 serv_name,
893 mech_type,
894 req_flags,
896 NULL,
897 &input_token,
898 NULL,
899 &output_token,
900 &ret_flags,
901 NULL);
902 if (scred) {
903 ber_bvfree(scred);
904 scred = NULL;
906 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
907 status = ADS_ERROR_GSS(gss_rc, minor_status);
908 goto failed;
911 cred.bv_val = (char *)output_token.value;
912 cred.bv_len = output_token.length;
914 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
915 &scred);
916 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
917 status = ADS_ERROR(rc);
918 goto failed;
921 if (output_token.value) {
922 gss_release_buffer(&minor_status, &output_token);
925 if (scred) {
926 input_token.value = scred->bv_val;
927 input_token.length = scred->bv_len;
928 } else {
929 input_token.value = NULL;
930 input_token.length = 0;
933 if (gss_rc == 0) break;
936 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
937 &conf_state,NULL);
938 if (scred) {
939 ber_bvfree(scred);
940 scred = NULL;
942 if (gss_rc) {
943 status = ADS_ERROR_GSS(gss_rc, minor_status);
944 goto failed;
947 p = (uint8 *)output_token.value;
949 #if 0
950 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
951 #endif
953 if (p) {
954 wrap_type = CVAL(p,0);
955 SCVAL(p,0,0);
956 max_msg_size = RIVAL(p,0);
959 gss_release_buffer(&minor_status, &output_token);
961 if (!(wrap_type & ads->ldap.wrap_type)) {
963 * the server doesn't supports the wrap
964 * type we want :-(
966 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
967 ads->ldap.wrap_type, wrap_type));
968 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
969 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
970 goto failed;
973 /* 0x58 is the minimum windows accepts */
974 if (max_msg_size < 0x58) {
975 max_msg_size = 0x58;
978 output_token.length = 4;
979 output_token.value = SMB_MALLOC(output_token.length);
980 p = (uint8 *)output_token.value;
982 RSIVAL(p,0,max_msg_size);
983 SCVAL(p,0,ads->ldap.wrap_type);
986 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
987 * but using ads->config.bind_path is the wrong! It should be
988 * the DN of the user object!
990 * w2k3 gives an error when we send an incorrect DN, but sending nothing
991 * is ok and matches the information flow used in GSS-SPNEGO.
994 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
995 &output_token, &conf_state,
996 &input_token);
997 if (gss_rc) {
998 status = ADS_ERROR_GSS(gss_rc, minor_status);
999 goto failed;
1002 free(output_token.value);
1004 cred.bv_val = (char *)input_token.value;
1005 cred.bv_len = input_token.length;
1007 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1008 &scred);
1009 gss_release_buffer(&minor_status, &input_token);
1010 status = ADS_ERROR(rc);
1011 if (!ADS_ERR_OK(status)) {
1012 goto failed;
1015 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1016 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1017 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1018 GSS_C_QOP_DEFAULT,
1019 max_msg_size, &ads->ldap.out.max_unwrapped);
1020 if (gss_rc) {
1021 status = ADS_ERROR_GSS(gss_rc, minor_status);
1022 goto failed;
1025 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1026 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1027 ads->ldap.in.max_wrapped = max_msg_size;
1028 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1029 if (!ADS_ERR_OK(status)) {
1030 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1031 ads_errstr(status)));
1032 goto failed;
1034 /* make sure we don't free context_handle */
1035 context_handle = GSS_C_NO_CONTEXT;
1038 failed:
1040 if (context_handle != GSS_C_NO_CONTEXT)
1041 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1043 if(scred)
1044 ber_bvfree(scred);
1045 return status;
1048 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1050 ADS_STATUS status;
1051 struct ads_service_principal p;
1053 status = ads_generate_service_principal(ads, NULL, &p);
1054 if (!ADS_ERR_OK(status)) {
1055 return status;
1058 status = ads_sasl_gssapi_do_bind(ads, p.name);
1059 if (ADS_ERR_OK(status)) {
1060 ads_free_service_principal(&p);
1061 return status;
1064 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1065 "calling kinit\n", ads_errstr(status)));
1067 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1069 if (ADS_ERR_OK(status)) {
1070 status = ads_sasl_gssapi_do_bind(ads, p.name);
1073 ads_free_service_principal(&p);
1075 return status;
1078 #endif /* HAVE_GSSAPI */
1080 /* mapping between SASL mechanisms and functions */
1081 static struct {
1082 const char *name;
1083 ADS_STATUS (*fn)(ADS_STRUCT *);
1084 } sasl_mechanisms[] = {
1085 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1086 #ifdef HAVE_GSSAPI
1087 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1088 #endif
1089 {NULL, NULL}
1092 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1094 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1095 char **values;
1096 ADS_STATUS status;
1097 int i, j;
1098 LDAPMessage *res;
1100 /* get a list of supported SASL mechanisms */
1101 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1102 if (!ADS_ERR_OK(status)) return status;
1104 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1106 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1107 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1108 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1109 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1110 } else {
1111 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1114 /* try our supported mechanisms in order */
1115 for (i=0;sasl_mechanisms[i].name;i++) {
1116 /* see if the server supports it */
1117 for (j=0;values && values[j];j++) {
1118 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1119 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1120 retry:
1121 status = sasl_mechanisms[i].fn(ads);
1122 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1123 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1124 ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1126 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1127 "retrying with signing enabled\n"));
1128 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1129 goto retry;
1131 ldap_value_free(values);
1132 ldap_msgfree(res);
1133 return status;
1138 ldap_value_free(values);
1139 ldap_msgfree(res);
1140 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1143 #endif /* HAVE_LDAP */