s4:winbindd: do not drop the workgroup name in the getgrgid call
[Samba.git] / source3 / libads / sasl.c
blob33f4e2419183681277b41e10622e2f1e6608e47e
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 char *princ = NULL;
719 if (ads->server.realm && ads->server.ldap_server) {
720 char *server, *server_realm;
722 server = SMB_STRDUP(ads->server.ldap_server);
723 server_realm = SMB_STRDUP(ads->server.realm);
725 if (!server || !server_realm) {
726 SAFE_FREE(server);
727 SAFE_FREE(server_realm);
728 return ADS_ERROR(LDAP_NO_MEMORY);
731 if (!strlower_m(server)) {
732 SAFE_FREE(server);
733 SAFE_FREE(server_realm);
734 return ADS_ERROR(LDAP_NO_MEMORY);
737 if (!strupper_m(server_realm)) {
738 SAFE_FREE(server);
739 SAFE_FREE(server_realm);
740 return ADS_ERROR(LDAP_NO_MEMORY);
743 if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
744 SAFE_FREE(server);
745 SAFE_FREE(server_realm);
746 return ADS_ERROR(LDAP_NO_MEMORY);
749 SAFE_FREE(server);
750 SAFE_FREE(server_realm);
752 if (!princ) {
753 return ADS_ERROR(LDAP_NO_MEMORY);
755 } else if (ads->config.realm && ads->config.ldap_server_name) {
756 char *server, *server_realm;
758 server = SMB_STRDUP(ads->config.ldap_server_name);
759 server_realm = SMB_STRDUP(ads->config.realm);
761 if (!server || !server_realm) {
762 SAFE_FREE(server);
763 SAFE_FREE(server_realm);
764 return ADS_ERROR(LDAP_NO_MEMORY);
767 if (!strlower_m(server)) {
768 SAFE_FREE(server);
769 SAFE_FREE(server_realm);
770 return ADS_ERROR(LDAP_NO_MEMORY);
773 if (!strupper_m(server_realm)) {
774 SAFE_FREE(server);
775 SAFE_FREE(server_realm);
776 return ADS_ERROR(LDAP_NO_MEMORY);
778 if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
779 SAFE_FREE(server);
780 SAFE_FREE(server_realm);
781 return ADS_ERROR(LDAP_NO_MEMORY);
784 SAFE_FREE(server);
785 SAFE_FREE(server_realm);
787 if (!princ) {
788 return ADS_ERROR(LDAP_NO_MEMORY);
792 if (!princ) {
793 return ADS_ERROR(LDAP_PARAM_ERROR);
796 *returned_principal = princ;
798 return ADS_SUCCESS;
801 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
802 const char *given_principal,
803 struct ads_service_principal *p)
805 ADS_STATUS status;
806 #ifdef HAVE_KRB5
807 gss_buffer_desc input_name;
808 /* GSS_KRB5_NT_PRINCIPAL_NAME */
809 gss_OID_desc nt_principal =
810 {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
811 uint32 minor_status;
812 int gss_rc;
813 #endif
815 ZERO_STRUCTP(p);
817 /* I've seen a child Windows 2000 domain not send
818 the principal name back in the first round of
819 the SASL bind reply. So we guess based on server
820 name and realm. --jerry */
821 /* Also try best guess when we get the w2k8 ignore principal
822 back, or when we are configured to ignore it - gd,
823 abartlet */
825 if (!lp_client_use_spnego_principal() ||
826 !given_principal ||
827 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
829 status = ads_guess_service_principal(ads, &p->string);
830 if (!ADS_ERR_OK(status)) {
831 return status;
833 } else {
834 p->string = SMB_STRDUP(given_principal);
835 if (!p->string) {
836 return ADS_ERROR(LDAP_NO_MEMORY);
840 #ifdef HAVE_KRB5
841 input_name.value = p->string;
842 input_name.length = strlen(p->string);
844 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
845 if (gss_rc) {
846 ads_free_service_principal(p);
847 return ADS_ERROR_GSS(gss_rc, minor_status);
849 #endif
851 return ADS_SUCCESS;
855 perform a LDAP/SASL/SPNEGO/KRB5 bind
857 static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
859 DATA_BLOB blob = data_blob_null;
860 struct berval cred, *scred = NULL;
861 DATA_BLOB session_key = data_blob_null;
862 int rc;
864 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
865 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
868 rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
869 ads->auth.time_offset, &blob, &session_key, 0,
870 ads->auth.ccache_name,
871 &ads->auth.tgs_expire);
873 if (rc) {
874 return ADS_ERROR_KRB5(rc);
877 /* now send the auth packet and we should be done */
878 cred.bv_val = (char *)blob.data;
879 cred.bv_len = blob.length;
881 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
883 data_blob_free(&blob);
884 data_blob_free(&session_key);
885 if(scred)
886 ber_bvfree(scred);
888 return ADS_ERROR(rc);
891 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
892 struct ads_service_principal *p)
894 #ifdef HAVE_KRB5
896 * we only use the gsskrb5 based implementation
897 * when sasl sign or seal is requested.
899 * This has the following reasons:
900 * - it's likely that the gssapi krb5 mech implementation
901 * doesn't support to negotiate plain connections
902 * - the ads_sasl_spnego_rawkrb5_bind is more robust
903 * against clock skew errors
905 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
906 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
908 #endif
909 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
911 #endif /* HAVE_KRB5 */
914 this performs a SASL/SPNEGO bind
916 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
918 struct berval *scred=NULL;
919 int rc, i;
920 ADS_STATUS status;
921 DATA_BLOB blob;
922 char *given_principal = NULL;
923 char *OIDs[ASN1_MAX_OIDS];
924 #ifdef HAVE_KRB5
925 bool got_kerberos_mechanism = False;
926 #endif
928 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
930 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
931 status = ADS_ERROR(rc);
932 goto failed;
935 blob = data_blob(scred->bv_val, scred->bv_len);
937 ber_bvfree(scred);
939 #if 0
940 file_save("sasl_spnego.dat", blob.data, blob.length);
941 #endif
943 /* the server sent us the first part of the SPNEGO exchange in the negprot
944 reply */
945 if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
946 OIDs[0] == NULL) {
947 data_blob_free(&blob);
948 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
949 goto failed;
951 data_blob_free(&blob);
953 /* make sure the server understands kerberos */
954 for (i=0;OIDs[i];i++) {
955 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
956 #ifdef HAVE_KRB5
957 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
958 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
959 got_kerberos_mechanism = True;
961 #endif
962 talloc_free(OIDs[i]);
964 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
966 #ifdef HAVE_KRB5
967 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
968 got_kerberos_mechanism)
970 struct ads_service_principal p;
972 status = ads_generate_service_principal(ads, given_principal, &p);
973 TALLOC_FREE(given_principal);
974 if (!ADS_ERR_OK(status)) {
975 return status;
978 status = ads_sasl_spnego_krb5_bind(ads, &p);
979 if (ADS_ERR_OK(status)) {
980 ads_free_service_principal(&p);
981 return status;
984 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
985 "calling kinit\n", ads_errstr(status)));
987 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
989 if (ADS_ERR_OK(status)) {
990 status = ads_sasl_spnego_krb5_bind(ads, &p);
991 if (!ADS_ERR_OK(status)) {
992 DEBUG(0,("kinit succeeded but "
993 "ads_sasl_spnego_krb5_bind failed: %s\n",
994 ads_errstr(status)));
998 ads_free_service_principal(&p);
1000 /* only fallback to NTLMSSP if allowed */
1001 if (ADS_ERR_OK(status) ||
1002 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
1003 return status;
1005 } else
1006 #endif
1008 TALLOC_FREE(given_principal);
1011 /* lets do NTLMSSP ... this has the big advantage that we don't need
1012 to sync clocks, and we don't rely on special versions of the krb5
1013 library for HMAC_MD4 encryption */
1014 return ads_sasl_spnego_ntlmssp_bind(ads);
1016 failed:
1017 return status;
1020 #ifdef HAVE_KRB5
1021 #define MAX_GSS_PASSES 3
1023 /* this performs a SASL/gssapi bind
1024 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
1025 is very dependent on correctly configured DNS whereas
1026 this routine is much less fragile
1027 see RFC2078 and RFC2222 for details
1029 static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
1031 uint32 minor_status;
1032 gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
1033 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
1034 gss_OID mech_type = GSS_C_NULL_OID;
1035 gss_buffer_desc output_token, input_token;
1036 uint32 req_flags, ret_flags;
1037 int conf_state;
1038 struct berval cred;
1039 struct berval *scred = NULL;
1040 int i=0;
1041 int gss_rc, rc;
1042 uint8 *p;
1043 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
1044 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1045 ADS_STATUS status;
1047 input_token.value = NULL;
1048 input_token.length = 0;
1050 status = ads_init_gssapi_cred(ads, &gss_cred);
1051 if (!ADS_ERR_OK(status)) {
1052 goto failed;
1056 * Note: here we always ask the gssapi for sign and seal
1057 * as this is negotiated later after the mutal
1058 * authentication
1060 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
1062 for (i=0; i < MAX_GSS_PASSES; i++) {
1063 gss_rc = gss_init_sec_context(&minor_status,
1064 gss_cred,
1065 &context_handle,
1066 serv_name,
1067 mech_type,
1068 req_flags,
1070 NULL,
1071 &input_token,
1072 NULL,
1073 &output_token,
1074 &ret_flags,
1075 NULL);
1076 if (scred) {
1077 ber_bvfree(scred);
1078 scred = NULL;
1080 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
1081 status = ADS_ERROR_GSS(gss_rc, minor_status);
1082 goto failed;
1085 cred.bv_val = (char *)output_token.value;
1086 cred.bv_len = output_token.length;
1088 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1089 &scred);
1090 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
1091 status = ADS_ERROR(rc);
1092 goto failed;
1095 if (output_token.value) {
1096 gss_release_buffer(&minor_status, &output_token);
1099 if (scred) {
1100 input_token.value = scred->bv_val;
1101 input_token.length = scred->bv_len;
1102 } else {
1103 input_token.value = NULL;
1104 input_token.length = 0;
1107 if (gss_rc == 0) break;
1110 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
1111 &conf_state,NULL);
1112 if (scred) {
1113 ber_bvfree(scred);
1114 scred = NULL;
1116 if (gss_rc) {
1117 status = ADS_ERROR_GSS(gss_rc, minor_status);
1118 goto failed;
1121 p = (uint8 *)output_token.value;
1123 #if 0
1124 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
1125 #endif
1127 if (p) {
1128 wrap_type = CVAL(p,0);
1129 SCVAL(p,0,0);
1130 max_msg_size = RIVAL(p,0);
1133 gss_release_buffer(&minor_status, &output_token);
1135 if (!(wrap_type & ads->ldap.wrap_type)) {
1137 * the server doesn't supports the wrap
1138 * type we want :-(
1140 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
1141 ads->ldap.wrap_type, wrap_type));
1142 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
1143 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
1144 goto failed;
1147 /* 0x58 is the minimum windows accepts */
1148 if (max_msg_size < 0x58) {
1149 max_msg_size = 0x58;
1152 output_token.length = 4;
1153 output_token.value = SMB_MALLOC(output_token.length);
1154 if (!output_token.value) {
1155 output_token.length = 0;
1156 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1157 goto failed;
1159 p = (uint8 *)output_token.value;
1161 RSIVAL(p,0,max_msg_size);
1162 SCVAL(p,0,ads->ldap.wrap_type);
1165 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
1166 * but using ads->config.bind_path is the wrong! It should be
1167 * the DN of the user object!
1169 * w2k3 gives an error when we send an incorrect DN, but sending nothing
1170 * is ok and matches the information flow used in GSS-SPNEGO.
1173 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1174 &output_token, /* used as *input* here. */
1175 &conf_state,
1176 &input_token); /* Used as *output* here. */
1177 if (gss_rc) {
1178 status = ADS_ERROR_GSS(gss_rc, minor_status);
1179 output_token.length = 0;
1180 SAFE_FREE(output_token.value);
1181 goto failed;
1184 /* We've finished with output_token. */
1185 SAFE_FREE(output_token.value);
1186 output_token.length = 0;
1188 cred.bv_val = (char *)input_token.value;
1189 cred.bv_len = input_token.length;
1191 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1192 &scred);
1193 gss_release_buffer(&minor_status, &input_token);
1194 status = ADS_ERROR(rc);
1195 if (!ADS_ERR_OK(status)) {
1196 goto failed;
1199 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1200 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1201 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1202 GSS_C_QOP_DEFAULT,
1203 max_msg_size, &ads->ldap.out.max_unwrapped);
1204 if (gss_rc) {
1205 status = ADS_ERROR_GSS(gss_rc, minor_status);
1206 goto failed;
1209 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1210 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1211 ads->ldap.in.max_wrapped = max_msg_size;
1212 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1213 if (!ADS_ERR_OK(status)) {
1214 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1215 ads_errstr(status)));
1216 goto failed;
1218 /* make sure we don't free context_handle */
1219 context_handle = GSS_C_NO_CONTEXT;
1222 failed:
1223 if (gss_cred != GSS_C_NO_CREDENTIAL)
1224 gss_release_cred(&minor_status, &gss_cred);
1225 if (context_handle != GSS_C_NO_CONTEXT)
1226 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1228 if(scred)
1229 ber_bvfree(scred);
1230 return status;
1233 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1235 ADS_STATUS status;
1236 struct ads_service_principal p;
1238 status = ads_generate_service_principal(ads, NULL, &p);
1239 if (!ADS_ERR_OK(status)) {
1240 return status;
1243 status = ads_sasl_gssapi_do_bind(ads, p.name);
1244 if (ADS_ERR_OK(status)) {
1245 ads_free_service_principal(&p);
1246 return status;
1249 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1250 "calling kinit\n", ads_errstr(status)));
1252 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1254 if (ADS_ERR_OK(status)) {
1255 status = ads_sasl_gssapi_do_bind(ads, p.name);
1258 ads_free_service_principal(&p);
1260 return status;
1263 #endif /* HAVE_KRB5 */
1265 /* mapping between SASL mechanisms and functions */
1266 static struct {
1267 const char *name;
1268 ADS_STATUS (*fn)(ADS_STRUCT *);
1269 } sasl_mechanisms[] = {
1270 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1271 #ifdef HAVE_KRB5
1272 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1273 #endif
1274 {NULL, NULL}
1277 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1279 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1280 char **values;
1281 ADS_STATUS status;
1282 int i, j;
1283 LDAPMessage *res;
1285 /* get a list of supported SASL mechanisms */
1286 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1287 if (!ADS_ERR_OK(status)) return status;
1289 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1291 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1292 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1293 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1294 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1295 } else {
1296 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1299 /* try our supported mechanisms in order */
1300 for (i=0;sasl_mechanisms[i].name;i++) {
1301 /* see if the server supports it */
1302 for (j=0;values && values[j];j++) {
1303 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1304 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1305 retry:
1306 status = sasl_mechanisms[i].fn(ads);
1307 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1308 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1309 ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1311 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1312 "retrying with signing enabled\n"));
1313 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1314 goto retry;
1316 ldap_value_free(values);
1317 ldap_msgfree(res);
1318 return status;
1323 ldap_value_free(values);
1324 ldap_msgfree(res);
1325 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1328 #endif /* HAVE_LDAP */