pam_winbind: fix warn_pwd_expire implementation.
[Samba.git] / source3 / libads / sasl.c
blob1450ff17196a26bb0136b4555d67d6fced7700e8
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 "auth/gensec/gensec.h"
23 #include "auth_generic.h"
24 #include "ads.h"
25 #include "smb_krb5.h"
26 #include "system/gssapi.h"
27 #include "lib/param/loadparm.h"
29 #ifdef HAVE_LDAP
31 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
33 struct gensec_security *gensec_security =
34 talloc_get_type_abort(ads->ldap.wrap_private_data,
35 struct gensec_security);
36 NTSTATUS nt_status;
37 DATA_BLOB unwrapped, wrapped;
38 TALLOC_CTX *frame = talloc_stackframe();
40 unwrapped = data_blob_const(buf, len);
42 nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
43 if (!NT_STATUS_IS_OK(nt_status)) {
44 TALLOC_FREE(frame);
45 return ADS_ERROR_NT(nt_status);
48 if ((ads->ldap.out.size - 4) < wrapped.length) {
49 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
52 /* copy the wrapped blob to the right location */
53 memcpy(ads->ldap.out.buf + 4, wrapped.data, wrapped.length);
55 /* set how many bytes must be written to the underlying socket */
56 ads->ldap.out.left = 4 + wrapped.length;
58 TALLOC_FREE(frame);
60 return ADS_SUCCESS;
63 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
65 struct gensec_security *gensec_security =
66 talloc_get_type_abort(ads->ldap.wrap_private_data,
67 struct gensec_security);
68 NTSTATUS nt_status;
69 DATA_BLOB unwrapped, wrapped;
70 TALLOC_CTX *frame = talloc_stackframe();
72 wrapped = data_blob_const(ads->ldap.in.buf + 4, ads->ldap.in.ofs - 4);
74 nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
75 if (!NT_STATUS_IS_OK(nt_status)) {
76 TALLOC_FREE(frame);
77 return ADS_ERROR_NT(nt_status);
80 if (wrapped.length < unwrapped.length) {
81 TALLOC_FREE(frame);
82 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
85 /* copy the wrapped blob to the right location */
86 memcpy(ads->ldap.in.buf + 4, unwrapped.data, unwrapped.length);
88 /* set how many bytes must be written to the underlying socket */
89 ads->ldap.in.left = unwrapped.length;
90 ads->ldap.in.ofs = 4;
92 TALLOC_FREE(frame);
94 return ADS_SUCCESS;
97 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
99 struct gensec_security *gensec_security =
100 talloc_get_type_abort(ads->ldap.wrap_private_data,
101 struct gensec_security);
103 TALLOC_FREE(gensec_security);
105 ads->ldap.wrap_ops = NULL;
106 ads->ldap.wrap_private_data = NULL;
109 static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
110 .name = "ntlmssp",
111 .wrap = ads_sasl_ntlmssp_wrap,
112 .unwrap = ads_sasl_ntlmssp_unwrap,
113 .disconnect = ads_sasl_ntlmssp_disconnect
117 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
118 we fit on one socket??)
120 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
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;
132 struct auth_generic_state *auth_generic_state;
134 nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
135 if (!NT_STATUS_IS_OK(nt_status)) {
136 return ADS_ERROR_NT(nt_status);
139 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) {
140 return ADS_ERROR_NT(nt_status);
142 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
143 return ADS_ERROR_NT(nt_status);
145 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
146 return ADS_ERROR_NT(nt_status);
149 switch (ads->ldap.wrap_type) {
150 case ADS_SASLWRAP_TYPE_SEAL:
151 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
152 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
153 break;
154 case ADS_SASLWRAP_TYPE_SIGN:
155 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
156 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
157 } else {
159 * windows servers are broken with sign only,
160 * so we need to use seal here too
162 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
163 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
164 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
166 break;
167 case ADS_SASLWRAP_TYPE_PLAIN:
168 break;
171 nt_status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
172 if (!NT_STATUS_IS_OK(nt_status)) {
173 return ADS_ERROR_NT(nt_status);
176 blob_in = data_blob_null;
178 do {
179 nt_status = gensec_update(auth_generic_state->gensec_security,
180 talloc_tos(), NULL, blob_in, &blob_out);
181 data_blob_free(&blob_in);
182 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
183 || NT_STATUS_IS_OK(nt_status))
184 && blob_out.length) {
185 if (turn == 1) {
186 const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
187 /* and wrap it in a SPNEGO wrapper */
188 msg1 = spnego_gen_negTokenInit(talloc_tos(),
189 OIDs_ntlm, &blob_out, NULL);
190 } else {
191 /* wrap it in SPNEGO */
192 msg1 = spnego_gen_auth(talloc_tos(), blob_out);
195 data_blob_free(&blob_out);
197 cred.bv_val = (char *)msg1.data;
198 cred.bv_len = msg1.length;
199 scred = NULL;
200 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
201 data_blob_free(&msg1);
202 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
203 if (scred) {
204 ber_bvfree(scred);
207 TALLOC_FREE(auth_generic_state);
208 return ADS_ERROR(rc);
210 if (scred) {
211 blob = data_blob(scred->bv_val, scred->bv_len);
212 ber_bvfree(scred);
213 } else {
214 blob = data_blob_null;
217 } else {
219 TALLOC_FREE(auth_generic_state);
220 data_blob_free(&blob_out);
221 return ADS_ERROR_NT(nt_status);
224 if ((turn == 1) &&
225 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
226 DATA_BLOB tmp_blob = data_blob_null;
227 /* the server might give us back two challenges */
228 if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in,
229 &tmp_blob)) {
231 TALLOC_FREE(auth_generic_state);
232 data_blob_free(&blob);
233 DEBUG(3,("Failed to parse challenges\n"));
234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
236 data_blob_free(&tmp_blob);
237 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
238 if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP,
239 &blob_in)) {
241 TALLOC_FREE(auth_generic_state);
242 data_blob_free(&blob);
243 DEBUG(3,("Failed to parse auth response\n"));
244 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
247 data_blob_free(&blob);
248 data_blob_free(&blob_out);
249 turn++;
250 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
252 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
253 uint32_t sig_size = gensec_sig_size(auth_generic_state->gensec_security, 0);
254 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size;
255 ads->ldap.out.sig_size = sig_size;
256 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
257 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
258 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, auth_generic_state->gensec_security);
259 if (!ADS_ERR_OK(status)) {
260 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
261 ads_errstr(status)));
262 TALLOC_FREE(auth_generic_state);
263 return status;
265 /* Only keep the gensec_security element around long-term */
266 talloc_steal(NULL, auth_generic_state->gensec_security);
268 TALLOC_FREE(auth_generic_state);
270 return ADS_ERROR(rc);
273 #ifdef HAVE_KRB5
274 static ADS_STATUS ads_init_gssapi_cred(ADS_STRUCT *ads, gss_cred_id_t *cred)
276 ADS_STATUS status;
277 krb5_context kctx;
278 krb5_error_code kerr;
279 krb5_ccache kccache = NULL;
280 uint32_t maj, min;
282 *cred = GSS_C_NO_CREDENTIAL;
284 if (!ads->auth.ccache_name) {
285 return ADS_SUCCESS;
288 kerr = krb5_init_context(&kctx);
289 if (kerr) {
290 return ADS_ERROR_KRB5(kerr);
293 #ifdef HAVE_GSS_KRB5_IMPORT_CRED
294 kerr = krb5_cc_resolve(kctx, ads->auth.ccache_name, &kccache);
295 if (kerr) {
296 status = ADS_ERROR_KRB5(kerr);
297 goto done;
300 maj = gss_krb5_import_cred(&min, kccache, NULL, NULL, cred);
301 if (maj != GSS_S_COMPLETE) {
302 status = ADS_ERROR_GSS(maj, min);
303 goto done;
305 #else
306 /* We need to fallback to overriding the default creds.
307 * This operation is not thread safe as it changes the process
308 * environment variable, but we do not have any better option
309 * with older kerberos libraries */
311 const char *oldccname = NULL;
313 oldccname = getenv("KRB5CCNAME");
314 setenv("KRB5CCNAME", ads->auth.ccache_name, 1);
316 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
317 NULL, GSS_C_INITIATE, cred, NULL, NULL);
319 if (oldccname) {
320 setenv("KRB5CCNAME", oldccname, 1);
321 } else {
322 unsetenv("KRB5CCNAME");
325 if (maj != GSS_S_COMPLETE) {
326 status = ADS_ERROR_GSS(maj, min);
327 goto done;
330 #endif
332 status = ADS_SUCCESS;
334 done:
335 if (!ADS_ERR_OK(status) && kccache != NULL) {
336 krb5_cc_close(kctx, kccache);
338 krb5_free_context(kctx);
339 return status;
342 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
344 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
345 ADS_STATUS status;
346 int gss_rc;
347 uint32 minor_status;
348 gss_buffer_desc unwrapped, wrapped;
349 int conf_req_flag, conf_state;
351 unwrapped.value = buf;
352 unwrapped.length = len;
354 /* for now request sign and seal */
355 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
357 gss_rc = gss_wrap(&minor_status, context_handle,
358 conf_req_flag, GSS_C_QOP_DEFAULT,
359 &unwrapped, &conf_state,
360 &wrapped);
361 status = ADS_ERROR_GSS(gss_rc, minor_status);
362 if (!ADS_ERR_OK(status)) return status;
364 if (conf_req_flag && conf_state == 0) {
365 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
368 if ((ads->ldap.out.size - 4) < wrapped.length) {
369 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
372 /* copy the wrapped blob to the right location */
373 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
375 /* set how many bytes must be written to the underlying socket */
376 ads->ldap.out.left = 4 + wrapped.length;
378 gss_release_buffer(&minor_status, &wrapped);
380 return ADS_SUCCESS;
383 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
385 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
386 ADS_STATUS status;
387 int gss_rc;
388 uint32 minor_status;
389 gss_buffer_desc unwrapped, wrapped;
390 int conf_state;
392 wrapped.value = ads->ldap.in.buf + 4;
393 wrapped.length = ads->ldap.in.ofs - 4;
395 gss_rc = gss_unwrap(&minor_status, context_handle,
396 &wrapped, &unwrapped,
397 &conf_state, GSS_C_QOP_DEFAULT);
398 status = ADS_ERROR_GSS(gss_rc, minor_status);
399 if (!ADS_ERR_OK(status)) return status;
401 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
402 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
405 if (wrapped.length < unwrapped.length) {
406 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
409 /* copy the wrapped blob to the right location */
410 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
412 /* set how many bytes must be written to the underlying socket */
413 ads->ldap.in.left = unwrapped.length;
414 ads->ldap.in.ofs = 4;
416 gss_release_buffer(&minor_status, &unwrapped);
418 return ADS_SUCCESS;
421 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
423 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
424 uint32 minor_status;
426 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
428 ads->ldap.wrap_ops = NULL;
429 ads->ldap.wrap_private_data = NULL;
432 static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
433 .name = "gssapi",
434 .wrap = ads_sasl_gssapi_wrap,
435 .unwrap = ads_sasl_gssapi_unwrap,
436 .disconnect = ads_sasl_gssapi_disconnect
440 perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
442 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
444 ADS_STATUS status;
445 bool ok;
446 uint32 minor_status;
447 int gss_rc, rc;
448 gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
449 gss_OID_desc krb5_mech_type =
450 {9, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
451 gss_OID mech_type = &krb5_mech_type;
452 gss_OID actual_mech_type = GSS_C_NULL_OID;
453 const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
454 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
455 gss_buffer_desc input_token, output_token;
456 uint32 req_flags, ret_flags;
457 uint32 req_tmp, ret_tmp;
458 DATA_BLOB unwrapped;
459 DATA_BLOB wrapped;
460 struct berval cred, *scred = NULL;
462 status = ads_init_gssapi_cred(ads, &gss_cred);
463 if (!ADS_ERR_OK(status)) {
464 goto failed;
467 input_token.value = NULL;
468 input_token.length = 0;
470 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
471 switch (ads->ldap.wrap_type) {
472 case ADS_SASLWRAP_TYPE_SEAL:
473 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
474 break;
475 case ADS_SASLWRAP_TYPE_SIGN:
476 req_flags |= GSS_C_INTEG_FLAG;
477 break;
478 case ADS_SASLWRAP_TYPE_PLAIN:
479 break;
482 /* Note: here we explicit ask for the krb5 mech_type */
483 gss_rc = gss_init_sec_context(&minor_status,
484 gss_cred,
485 &context_handle,
486 serv_name,
487 mech_type,
488 req_flags,
490 NULL,
491 &input_token,
492 &actual_mech_type,
493 &output_token,
494 &ret_flags,
495 NULL);
496 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
497 status = ADS_ERROR_GSS(gss_rc, minor_status);
498 goto failed;
502 * As some gssapi krb5 mech implementations
503 * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
504 * to req_flags internaly, it's not possible to
505 * use plain or signing only connection via
506 * the gssapi interface.
508 * Because of this we need to check it the ret_flags
509 * has more flags as req_flags and correct the value
510 * of ads->ldap.wrap_type.
512 * I ads->auth.flags has ADS_AUTH_SASL_FORCE
513 * we need to give an error.
515 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
516 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
518 if (req_tmp == ret_tmp) {
519 /* everythings fine... */
521 } else if (req_flags & GSS_C_CONF_FLAG) {
523 * here we wanted sealing but didn't got it
524 * from the gssapi library
526 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
527 goto failed;
529 } else if ((req_flags & GSS_C_INTEG_FLAG) &&
530 !(ret_flags & GSS_C_INTEG_FLAG)) {
532 * here we wanted siging but didn't got it
533 * from the gssapi library
535 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
536 goto failed;
538 } else if (ret_flags & GSS_C_CONF_FLAG) {
540 * here we didn't want sealing
541 * but the gssapi library forces it
542 * so correct the needed wrap_type if
543 * the caller didn't forced siging only
545 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
546 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
547 goto failed;
550 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
551 req_flags = ret_flags;
553 } else if (ret_flags & GSS_C_INTEG_FLAG) {
555 * here we didn't want signing
556 * but the gssapi library forces it
557 * so correct the needed wrap_type if
558 * the caller didn't forced plain
560 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
561 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
562 goto failed;
565 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
566 req_flags = ret_flags;
567 } else {
569 * This could (should?) not happen
571 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
572 goto failed;
576 /* and wrap that in a shiny SPNEGO wrapper */
577 unwrapped = data_blob_const(output_token.value, output_token.length);
578 wrapped = spnego_gen_negTokenInit(talloc_tos(),
579 spnego_mechs, &unwrapped, NULL);
580 gss_release_buffer(&minor_status, &output_token);
581 if (unwrapped.length > wrapped.length) {
582 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
583 goto failed;
586 cred.bv_val = (char *)wrapped.data;
587 cred.bv_len = wrapped.length;
589 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
590 &scred);
591 data_blob_free(&wrapped);
592 if (rc != LDAP_SUCCESS) {
593 status = ADS_ERROR(rc);
594 goto failed;
597 if (scred) {
598 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
599 } else {
600 wrapped = data_blob_null;
603 ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
604 OID_KERBEROS5_OLD,
605 &unwrapped);
606 if (scred) ber_bvfree(scred);
607 if (!ok) {
608 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
609 goto failed;
612 input_token.value = unwrapped.data;
613 input_token.length = unwrapped.length;
616 * As we asked for mutal authentication
617 * we need to pass the servers response
618 * to gssapi
620 gss_rc = gss_init_sec_context(&minor_status,
621 gss_cred,
622 &context_handle,
623 serv_name,
624 mech_type,
625 req_flags,
627 NULL,
628 &input_token,
629 &actual_mech_type,
630 &output_token,
631 &ret_flags,
632 NULL);
633 data_blob_free(&unwrapped);
634 if (gss_rc) {
635 status = ADS_ERROR_GSS(gss_rc, minor_status);
636 goto failed;
639 gss_release_buffer(&minor_status, &output_token);
642 * If we the sign and seal options
643 * doesn't match after getting the response
644 * from the server, we don't want to use the connection
646 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
647 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
649 if (req_tmp != ret_tmp) {
650 /* everythings fine... */
651 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
652 goto failed;
655 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
656 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
658 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
659 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
660 GSS_C_QOP_DEFAULT,
661 max_msg_size, &ads->ldap.out.max_unwrapped);
662 if (gss_rc) {
663 status = ADS_ERROR_GSS(gss_rc, minor_status);
664 goto failed;
667 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
668 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
669 ads->ldap.in.max_wrapped = max_msg_size;
670 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
671 if (!ADS_ERR_OK(status)) {
672 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
673 ads_errstr(status)));
674 goto failed;
676 /* make sure we don't free context_handle */
677 context_handle = GSS_C_NO_CONTEXT;
680 status = ADS_SUCCESS;
682 failed:
683 if (gss_cred != GSS_C_NO_CREDENTIAL)
684 gss_release_cred(&minor_status, &gss_cred);
685 if (context_handle != GSS_C_NO_CONTEXT)
686 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
687 return status;
690 #endif /* HAVE_KRB5 */
692 #ifdef HAVE_KRB5
693 struct ads_service_principal {
694 char *string;
695 #ifdef HAVE_KRB5
696 gss_name_t name;
697 #endif
700 static void ads_free_service_principal(struct ads_service_principal *p)
702 SAFE_FREE(p->string);
704 #ifdef HAVE_KRB5
705 if (p->name) {
706 uint32 minor_status;
707 gss_release_name(&minor_status, &p->name);
709 #endif
710 ZERO_STRUCTP(p);
714 static ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads,
715 char **returned_principal)
717 ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
718 char *princ = NULL;
719 TALLOC_CTX *frame;
720 char *server = NULL;
721 char *realm = NULL;
722 int rc;
724 frame = talloc_stackframe();
725 if (frame == NULL) {
726 return ADS_ERROR(LDAP_NO_MEMORY);
729 if (ads->server.realm && ads->server.ldap_server) {
730 server = strlower_talloc(frame, ads->server.ldap_server);
731 if (server == NULL) {
732 goto out;
735 realm = strupper_talloc(frame, ads->server.realm);
736 if (realm == NULL) {
737 goto out;
741 * If we got a name which is bigger than a NetBIOS name,
742 * but isn't a FQDN, create one.
744 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
745 char *dnsdomain;
747 dnsdomain = strlower_talloc(frame, ads->server.realm);
748 if (dnsdomain == NULL) {
749 goto out;
752 server = talloc_asprintf(frame,
753 "%s.%s",
754 server, dnsdomain);
755 if (server == NULL) {
756 goto out;
759 } else if (ads->config.realm && ads->config.ldap_server_name) {
760 server = strlower_talloc(frame, ads->config.ldap_server_name);
761 if (server == NULL) {
762 goto out;
765 realm = strupper_talloc(frame, ads->config.realm);
766 if (realm == NULL) {
767 goto out;
771 * If we got a name which is bigger than a NetBIOS name,
772 * but isn't a FQDN, create one.
774 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
775 char *dnsdomain;
777 dnsdomain = strlower_talloc(frame, ads->server.realm);
778 if (dnsdomain == NULL) {
779 goto out;
782 server = talloc_asprintf(frame,
783 "%s.%s",
784 server, dnsdomain);
785 if (server == NULL) {
786 goto out;
791 if (server == NULL || realm == NULL) {
792 goto out;
795 rc = asprintf(&princ, "ldap/%s@%s", server, realm);
796 if (rc == -1 || princ == NULL) {
797 status = ADS_ERROR(LDAP_PARAM_ERROR);
798 goto out;
801 *returned_principal = princ;
803 status = ADS_SUCCESS;
804 out:
805 TALLOC_FREE(frame);
806 return status;
809 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
810 const char *given_principal,
811 struct ads_service_principal *p)
813 ADS_STATUS status;
814 #ifdef HAVE_KRB5
815 gss_buffer_desc input_name;
816 /* GSS_KRB5_NT_PRINCIPAL_NAME */
817 gss_OID_desc nt_principal =
818 {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
819 uint32 minor_status;
820 int gss_rc;
821 #endif
823 ZERO_STRUCTP(p);
825 /* I've seen a child Windows 2000 domain not send
826 the principal name back in the first round of
827 the SASL bind reply. So we guess based on server
828 name and realm. --jerry */
829 /* Also try best guess when we get the w2k8 ignore principal
830 back, or when we are configured to ignore it - gd,
831 abartlet */
833 if (!lp_client_use_spnego_principal() ||
834 !given_principal ||
835 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
837 status = ads_guess_service_principal(ads, &p->string);
838 if (!ADS_ERR_OK(status)) {
839 return status;
841 } else {
842 p->string = SMB_STRDUP(given_principal);
843 if (!p->string) {
844 return ADS_ERROR(LDAP_NO_MEMORY);
848 #ifdef HAVE_KRB5
849 input_name.value = p->string;
850 input_name.length = strlen(p->string);
852 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
853 if (gss_rc) {
854 ads_free_service_principal(p);
855 return ADS_ERROR_GSS(gss_rc, minor_status);
857 #endif
859 return ADS_SUCCESS;
863 perform a LDAP/SASL/SPNEGO/KRB5 bind
865 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
867 DATA_BLOB blob = data_blob_null;
868 struct berval cred, *scred = NULL;
869 DATA_BLOB session_key = data_blob_null;
870 int rc;
872 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
873 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
876 rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
877 ads->auth.time_offset, &blob, &session_key, 0,
878 ads->auth.ccache_name,
879 &ads->auth.tgs_expire);
881 if (rc) {
882 return ADS_ERROR_KRB5(rc);
885 /* now send the auth packet and we should be done */
886 cred.bv_val = (char *)blob.data;
887 cred.bv_len = blob.length;
889 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
891 data_blob_free(&blob);
892 data_blob_free(&session_key);
893 if(scred)
894 ber_bvfree(scred);
896 return ADS_ERROR(rc);
899 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
900 struct ads_service_principal *p)
902 #ifdef HAVE_KRB5
904 * we only use the gsskrb5 based implementation
905 * when sasl sign or seal is requested.
907 * This has the following reasons:
908 * - it's likely that the gssapi krb5 mech implementation
909 * doesn't support to negotiate plain connections
910 * - the ads_sasl_spnego_rawkrb5_bind is more robust
911 * against clock skew errors
913 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
914 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
916 #endif
917 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
919 #endif /* HAVE_KRB5 */
922 this performs a SASL/SPNEGO bind
924 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
926 struct berval *scred=NULL;
927 int rc, i;
928 ADS_STATUS status;
929 DATA_BLOB blob;
930 char *given_principal = NULL;
931 char *OIDs[ASN1_MAX_OIDS];
932 #ifdef HAVE_KRB5
933 bool got_kerberos_mechanism = False;
934 #endif
936 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
938 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
939 status = ADS_ERROR(rc);
940 goto failed;
943 blob = data_blob(scred->bv_val, scred->bv_len);
945 ber_bvfree(scred);
947 #if 0
948 file_save("sasl_spnego.dat", blob.data, blob.length);
949 #endif
951 /* the server sent us the first part of the SPNEGO exchange in the negprot
952 reply */
953 if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
954 OIDs[0] == NULL) {
955 data_blob_free(&blob);
956 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
957 goto failed;
959 data_blob_free(&blob);
961 /* make sure the server understands kerberos */
962 for (i=0;OIDs[i];i++) {
963 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
964 #ifdef HAVE_KRB5
965 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
966 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
967 got_kerberos_mechanism = True;
969 #endif
970 talloc_free(OIDs[i]);
972 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
974 #ifdef HAVE_KRB5
975 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
976 got_kerberos_mechanism)
978 struct ads_service_principal p;
980 status = ads_generate_service_principal(ads, given_principal, &p);
981 TALLOC_FREE(given_principal);
982 if (!ADS_ERR_OK(status)) {
983 return status;
986 status = ads_sasl_spnego_krb5_bind(ads, &p);
987 if (ADS_ERR_OK(status)) {
988 ads_free_service_principal(&p);
989 return status;
992 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
993 "calling kinit\n", ads_errstr(status)));
995 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
997 if (ADS_ERR_OK(status)) {
998 status = ads_sasl_spnego_krb5_bind(ads, &p);
999 if (!ADS_ERR_OK(status)) {
1000 DEBUG(0,("kinit succeeded but "
1001 "ads_sasl_spnego_krb5_bind failed: %s\n",
1002 ads_errstr(status)));
1006 ads_free_service_principal(&p);
1008 /* only fallback to NTLMSSP if allowed */
1009 if (ADS_ERR_OK(status) ||
1010 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
1011 return status;
1013 } else
1014 #endif
1016 TALLOC_FREE(given_principal);
1019 /* lets do NTLMSSP ... this has the big advantage that we don't need
1020 to sync clocks, and we don't rely on special versions of the krb5
1021 library for HMAC_MD4 encryption */
1022 return ads_sasl_spnego_ntlmssp_bind(ads);
1024 failed:
1025 return status;
1028 #ifdef HAVE_KRB5
1029 #define MAX_GSS_PASSES 3
1031 /* this performs a SASL/gssapi bind
1032 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
1033 is very dependent on correctly configured DNS whereas
1034 this routine is much less fragile
1035 see RFC2078 and RFC2222 for details
1037 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
1039 uint32 minor_status;
1040 gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
1041 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
1042 gss_OID mech_type = GSS_C_NULL_OID;
1043 gss_buffer_desc output_token, input_token;
1044 uint32 req_flags, ret_flags;
1045 int conf_state;
1046 struct berval cred;
1047 struct berval *scred = NULL;
1048 int i=0;
1049 int gss_rc, rc;
1050 uint8 *p;
1051 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
1052 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1053 ADS_STATUS status;
1055 input_token.value = NULL;
1056 input_token.length = 0;
1058 status = ads_init_gssapi_cred(ads, &gss_cred);
1059 if (!ADS_ERR_OK(status)) {
1060 goto failed;
1064 * Note: here we always ask the gssapi for sign and seal
1065 * as this is negotiated later after the mutal
1066 * authentication
1068 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
1070 for (i=0; i < MAX_GSS_PASSES; i++) {
1071 gss_rc = gss_init_sec_context(&minor_status,
1072 gss_cred,
1073 &context_handle,
1074 serv_name,
1075 mech_type,
1076 req_flags,
1078 NULL,
1079 &input_token,
1080 NULL,
1081 &output_token,
1082 &ret_flags,
1083 NULL);
1084 if (scred) {
1085 ber_bvfree(scred);
1086 scred = NULL;
1088 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
1089 status = ADS_ERROR_GSS(gss_rc, minor_status);
1090 goto failed;
1093 cred.bv_val = (char *)output_token.value;
1094 cred.bv_len = output_token.length;
1096 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1097 &scred);
1098 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
1099 status = ADS_ERROR(rc);
1100 goto failed;
1103 if (output_token.value) {
1104 gss_release_buffer(&minor_status, &output_token);
1107 if (scred) {
1108 input_token.value = scred->bv_val;
1109 input_token.length = scred->bv_len;
1110 } else {
1111 input_token.value = NULL;
1112 input_token.length = 0;
1115 if (gss_rc == 0) break;
1118 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
1119 &conf_state,NULL);
1120 if (scred) {
1121 ber_bvfree(scred);
1122 scred = NULL;
1124 if (gss_rc) {
1125 status = ADS_ERROR_GSS(gss_rc, minor_status);
1126 goto failed;
1129 p = (uint8 *)output_token.value;
1131 #if 0
1132 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
1133 #endif
1135 if (p) {
1136 wrap_type = CVAL(p,0);
1137 SCVAL(p,0,0);
1138 max_msg_size = RIVAL(p,0);
1141 gss_release_buffer(&minor_status, &output_token);
1143 if (!(wrap_type & ads->ldap.wrap_type)) {
1145 * the server doesn't supports the wrap
1146 * type we want :-(
1148 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
1149 ads->ldap.wrap_type, wrap_type));
1150 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
1151 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
1152 goto failed;
1155 /* 0x58 is the minimum windows accepts */
1156 if (max_msg_size < 0x58) {
1157 max_msg_size = 0x58;
1160 output_token.length = 4;
1161 output_token.value = SMB_MALLOC(output_token.length);
1162 if (!output_token.value) {
1163 output_token.length = 0;
1164 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1165 goto failed;
1167 p = (uint8 *)output_token.value;
1169 RSIVAL(p,0,max_msg_size);
1170 SCVAL(p,0,ads->ldap.wrap_type);
1173 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
1174 * but using ads->config.bind_path is the wrong! It should be
1175 * the DN of the user object!
1177 * w2k3 gives an error when we send an incorrect DN, but sending nothing
1178 * is ok and matches the information flow used in GSS-SPNEGO.
1181 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1182 &output_token, /* used as *input* here. */
1183 &conf_state,
1184 &input_token); /* Used as *output* here. */
1185 if (gss_rc) {
1186 status = ADS_ERROR_GSS(gss_rc, minor_status);
1187 output_token.length = 0;
1188 SAFE_FREE(output_token.value);
1189 goto failed;
1192 /* We've finished with output_token. */
1193 SAFE_FREE(output_token.value);
1194 output_token.length = 0;
1196 cred.bv_val = (char *)input_token.value;
1197 cred.bv_len = input_token.length;
1199 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1200 &scred);
1201 gss_release_buffer(&minor_status, &input_token);
1202 status = ADS_ERROR(rc);
1203 if (!ADS_ERR_OK(status)) {
1204 goto failed;
1207 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1208 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1209 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1210 GSS_C_QOP_DEFAULT,
1211 max_msg_size, &ads->ldap.out.max_unwrapped);
1212 if (gss_rc) {
1213 status = ADS_ERROR_GSS(gss_rc, minor_status);
1214 goto failed;
1217 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1218 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1219 ads->ldap.in.max_wrapped = max_msg_size;
1220 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1221 if (!ADS_ERR_OK(status)) {
1222 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1223 ads_errstr(status)));
1224 goto failed;
1226 /* make sure we don't free context_handle */
1227 context_handle = GSS_C_NO_CONTEXT;
1230 failed:
1231 if (gss_cred != GSS_C_NO_CREDENTIAL)
1232 gss_release_cred(&minor_status, &gss_cred);
1233 if (context_handle != GSS_C_NO_CONTEXT)
1234 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1236 if(scred)
1237 ber_bvfree(scred);
1238 return status;
1241 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1243 ADS_STATUS status;
1244 struct ads_service_principal p;
1246 status = ads_generate_service_principal(ads, NULL, &p);
1247 if (!ADS_ERR_OK(status)) {
1248 return status;
1251 status = ads_sasl_gssapi_do_bind(ads, p.name);
1252 if (ADS_ERR_OK(status)) {
1253 ads_free_service_principal(&p);
1254 return status;
1257 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1258 "calling kinit\n", ads_errstr(status)));
1260 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1262 if (ADS_ERR_OK(status)) {
1263 status = ads_sasl_gssapi_do_bind(ads, p.name);
1266 ads_free_service_principal(&p);
1268 return status;
1271 #endif /* HAVE_KRB5 */
1273 /* mapping between SASL mechanisms and functions */
1274 static struct {
1275 const char *name;
1276 ADS_STATUS (*fn)(ADS_STRUCT *);
1277 } sasl_mechanisms[] = {
1278 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1279 #ifdef HAVE_KRB5
1280 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1281 #endif
1282 {NULL, NULL}
1285 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1287 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1288 char **values;
1289 ADS_STATUS status;
1290 int i, j;
1291 LDAPMessage *res;
1293 /* get a list of supported SASL mechanisms */
1294 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1295 if (!ADS_ERR_OK(status)) return status;
1297 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1299 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1300 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1301 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1302 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1303 } else {
1304 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1307 /* try our supported mechanisms in order */
1308 for (i=0;sasl_mechanisms[i].name;i++) {
1309 /* see if the server supports it */
1310 for (j=0;values && values[j];j++) {
1311 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1312 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1313 retry:
1314 status = sasl_mechanisms[i].fn(ads);
1315 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1316 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1317 ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1319 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1320 "retrying with signing enabled\n"));
1321 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1322 goto retry;
1324 ldap_value_free(values);
1325 ldap_msgfree(res);
1326 return status;
1331 ldap_value_free(values);
1332 ldap_msgfree(res);
1333 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1336 #endif /* HAVE_LDAP */