tests/krb5: Clarify checksum type assertion message
[Samba.git] / source3 / libads / sasl.c
blob87beeafe3edc37cabb51a0a8b998fb708b565b42
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/credentials/credentials.h"
23 #include "auth/gensec/gensec.h"
24 #include "auth_generic.h"
25 #include "ads.h"
26 #include "smb_krb5.h"
27 #include "system/gssapi.h"
28 #include "lib/param/loadparm.h"
29 #include "krb5_env.h"
31 #ifdef HAVE_LDAP
33 static ADS_STATUS ads_sasl_gensec_wrap(struct ads_saslwrap *wrap,
34 uint8_t *buf, uint32_t len)
36 struct gensec_security *gensec_security =
37 talloc_get_type_abort(wrap->wrap_private_data,
38 struct gensec_security);
39 NTSTATUS nt_status;
40 DATA_BLOB unwrapped, wrapped;
41 TALLOC_CTX *frame = talloc_stackframe();
43 unwrapped = data_blob_const(buf, len);
45 nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
46 if (!NT_STATUS_IS_OK(nt_status)) {
47 TALLOC_FREE(frame);
48 return ADS_ERROR_NT(nt_status);
51 if ((wrap->out.size - 4) < wrapped.length) {
52 TALLOC_FREE(frame);
53 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
56 /* copy the wrapped blob to the right location */
57 memcpy(wrap->out.buf + 4, wrapped.data, wrapped.length);
59 /* set how many bytes must be written to the underlying socket */
60 wrap->out.left = 4 + wrapped.length;
62 TALLOC_FREE(frame);
64 return ADS_SUCCESS;
67 static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap)
69 struct gensec_security *gensec_security =
70 talloc_get_type_abort(wrap->wrap_private_data,
71 struct gensec_security);
72 NTSTATUS nt_status;
73 DATA_BLOB unwrapped, wrapped;
74 TALLOC_CTX *frame = talloc_stackframe();
76 wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4);
78 nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
79 if (!NT_STATUS_IS_OK(nt_status)) {
80 TALLOC_FREE(frame);
81 return ADS_ERROR_NT(nt_status);
84 if (wrapped.length < unwrapped.length) {
85 TALLOC_FREE(frame);
86 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
89 /* copy the wrapped blob to the right location */
90 memcpy(wrap->in.buf + 4, unwrapped.data, unwrapped.length);
92 /* set how many bytes must be written to the underlying socket */
93 wrap->in.left = unwrapped.length;
94 wrap->in.ofs = 4;
96 TALLOC_FREE(frame);
98 return ADS_SUCCESS;
101 static void ads_sasl_gensec_disconnect(struct ads_saslwrap *wrap)
103 struct gensec_security *gensec_security =
104 talloc_get_type_abort(wrap->wrap_private_data,
105 struct gensec_security);
107 TALLOC_FREE(gensec_security);
109 wrap->wrap_ops = NULL;
110 wrap->wrap_private_data = NULL;
113 static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
114 .name = "gensec",
115 .wrap = ads_sasl_gensec_wrap,
116 .unwrap = ads_sasl_gensec_unwrap,
117 .disconnect = ads_sasl_gensec_disconnect
121 perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can
122 we fit on one socket??)
124 static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
125 const char *sasl,
126 enum credentials_use_kerberos krb5_state,
127 const char *target_service,
128 const char *target_hostname,
129 const DATA_BLOB server_blob)
131 DATA_BLOB blob_in = data_blob_null;
132 DATA_BLOB blob_out = data_blob_null;
133 int rc;
134 NTSTATUS nt_status;
135 ADS_STATUS status;
136 struct auth_generic_state *auth_generic_state;
137 bool use_spnego_principal = lp_client_use_spnego_principal();
138 const char *sasl_list[] = { sasl, NULL };
139 NTTIME end_nt_time;
140 struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
142 nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
143 if (!NT_STATUS_IS_OK(nt_status)) {
144 return ADS_ERROR_NT(nt_status);
147 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) {
148 return ADS_ERROR_NT(nt_status);
150 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
151 return ADS_ERROR_NT(nt_status);
153 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
154 return ADS_ERROR_NT(nt_status);
157 if (server_blob.length == 0) {
158 use_spnego_principal = false;
161 if (krb5_state == CRED_DONT_USE_KERBEROS) {
162 use_spnego_principal = false;
165 cli_credentials_set_kerberos_state(auth_generic_state->credentials,
166 krb5_state);
168 if (target_service != NULL) {
169 nt_status = gensec_set_target_service(
170 auth_generic_state->gensec_security,
171 target_service);
172 if (!NT_STATUS_IS_OK(nt_status)) {
173 return ADS_ERROR_NT(nt_status);
177 if (target_hostname != NULL) {
178 nt_status = gensec_set_target_hostname(
179 auth_generic_state->gensec_security,
180 target_hostname);
181 if (!NT_STATUS_IS_OK(nt_status)) {
182 return ADS_ERROR_NT(nt_status);
186 if (target_service != NULL && target_hostname != NULL) {
187 use_spnego_principal = false;
190 switch (wrap->wrap_type) {
191 case ADS_SASLWRAP_TYPE_SEAL:
192 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
193 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
194 break;
195 case ADS_SASLWRAP_TYPE_SIGN:
196 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
197 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
198 } else {
200 * windows servers are broken with sign only,
201 * so we let the NTLMSSP backend to seal here,
202 * via GENSEC_FEATURE_LDAP_STYLE.
204 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
205 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE);
207 break;
208 case ADS_SASLWRAP_TYPE_PLAIN:
209 break;
212 nt_status = auth_generic_client_start_by_sasl(auth_generic_state,
213 sasl_list);
214 if (!NT_STATUS_IS_OK(nt_status)) {
215 return ADS_ERROR_NT(nt_status);
218 rc = LDAP_SASL_BIND_IN_PROGRESS;
219 if (use_spnego_principal) {
220 blob_in = data_blob_dup_talloc(talloc_tos(), server_blob);
221 if (blob_in.length == 0) {
222 TALLOC_FREE(auth_generic_state);
223 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
225 } else {
226 blob_in = data_blob_null;
228 blob_out = data_blob_null;
230 while (true) {
231 struct berval cred, *scred = NULL;
233 nt_status = gensec_update(auth_generic_state->gensec_security,
234 talloc_tos(), blob_in, &blob_out);
235 data_blob_free(&blob_in);
236 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
237 && !NT_STATUS_IS_OK(nt_status))
239 TALLOC_FREE(auth_generic_state);
240 data_blob_free(&blob_out);
241 return ADS_ERROR_NT(nt_status);
244 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) {
245 break;
248 cred.bv_val = (char *)blob_out.data;
249 cred.bv_len = blob_out.length;
250 scred = NULL;
251 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred);
252 data_blob_free(&blob_out);
253 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
254 if (scred) {
255 ber_bvfree(scred);
258 TALLOC_FREE(auth_generic_state);
259 return ADS_ERROR(rc);
261 if (scred) {
262 blob_in = data_blob_talloc(talloc_tos(),
263 scred->bv_val,
264 scred->bv_len);
265 if (blob_in.length != scred->bv_len) {
266 ber_bvfree(scred);
267 TALLOC_FREE(auth_generic_state);
268 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
270 ber_bvfree(scred);
271 } else {
272 blob_in = data_blob_null;
274 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) {
275 break;
279 data_blob_free(&blob_in);
280 data_blob_free(&blob_out);
282 if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) {
283 bool ok;
285 ok = gensec_have_feature(auth_generic_state->gensec_security,
286 GENSEC_FEATURE_SEAL);
287 if (!ok) {
288 DEBUG(0,("The gensec feature sealing request, but unavailable\n"));
289 TALLOC_FREE(auth_generic_state);
290 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
293 ok = gensec_have_feature(auth_generic_state->gensec_security,
294 GENSEC_FEATURE_SIGN);
295 if (!ok) {
296 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
297 TALLOC_FREE(auth_generic_state);
298 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
301 } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) {
302 bool ok;
304 ok = gensec_have_feature(auth_generic_state->gensec_security,
305 GENSEC_FEATURE_SIGN);
306 if (!ok) {
307 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
308 TALLOC_FREE(auth_generic_state);
309 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
313 ads->auth.tgs_expire = LONG_MAX;
314 end_nt_time = gensec_expire_time(auth_generic_state->gensec_security);
315 if (end_nt_time != GENSEC_EXPIRE_TIME_INFINITY) {
316 struct timeval tv;
317 nttime_to_timeval(&tv, end_nt_time);
318 ads->auth.tgs_expire = tv.tv_sec;
321 if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
322 size_t max_wrapped =
323 gensec_max_wrapped_size(auth_generic_state->gensec_security);
324 wrap->out.max_unwrapped =
325 gensec_max_input_size(auth_generic_state->gensec_security);
327 wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped;
329 * Note that we have to truncate this to 0x2C
330 * (taken from a capture with LDAP unbind), as the
331 * signature size is not constant for Kerberos with
332 * arcfour-hmac-md5.
334 wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C);
335 wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
336 status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld,
337 &ads_sasl_gensec_ops,
338 auth_generic_state->gensec_security);
339 if (!ADS_ERR_OK(status)) {
340 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
341 ads_errstr(status)));
342 TALLOC_FREE(auth_generic_state);
343 return status;
345 /* Only keep the gensec_security element around long-term */
346 talloc_steal(NULL, auth_generic_state->gensec_security);
348 TALLOC_FREE(auth_generic_state);
350 return ADS_ERROR(rc);
353 #ifdef HAVE_KRB5
354 struct ads_service_principal {
355 char *service;
356 char *hostname;
357 char *string;
360 static void ads_free_service_principal(struct ads_service_principal *p)
362 SAFE_FREE(p->service);
363 SAFE_FREE(p->hostname);
364 SAFE_FREE(p->string);
365 ZERO_STRUCTP(p);
368 static ADS_STATUS ads_guess_target(ADS_STRUCT *ads,
369 char **service,
370 char **hostname,
371 char **principal)
373 ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
374 char *princ = NULL;
375 TALLOC_CTX *frame;
376 char *server = NULL;
377 char *realm = NULL;
378 int rc;
380 frame = talloc_stackframe();
381 if (frame == NULL) {
382 return ADS_ERROR(LDAP_NO_MEMORY);
385 if (ads->server.realm && ads->server.ldap_server) {
386 server = strlower_talloc(frame, ads->server.ldap_server);
387 if (server == NULL) {
388 goto out;
391 realm = strupper_talloc(frame, ads->server.realm);
392 if (realm == NULL) {
393 goto out;
397 * If we got a name which is bigger than a NetBIOS name,
398 * but isn't a FQDN, create one.
400 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
401 char *dnsdomain;
403 dnsdomain = strlower_talloc(frame, ads->server.realm);
404 if (dnsdomain == NULL) {
405 goto out;
408 server = talloc_asprintf(frame,
409 "%s.%s",
410 server, dnsdomain);
411 if (server == NULL) {
412 goto out;
415 } else if (ads->config.realm && ads->config.ldap_server_name) {
416 server = strlower_talloc(frame, ads->config.ldap_server_name);
417 if (server == NULL) {
418 goto out;
421 realm = strupper_talloc(frame, ads->config.realm);
422 if (realm == NULL) {
423 goto out;
427 * If we got a name which is bigger than a NetBIOS name,
428 * but isn't a FQDN, create one.
430 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
431 char *dnsdomain;
433 dnsdomain = strlower_talloc(frame, ads->server.realm);
434 if (dnsdomain == NULL) {
435 goto out;
438 server = talloc_asprintf(frame,
439 "%s.%s",
440 server, dnsdomain);
441 if (server == NULL) {
442 goto out;
447 if (server == NULL || realm == NULL) {
448 goto out;
451 *service = SMB_STRDUP("ldap");
452 if (*service == NULL) {
453 status = ADS_ERROR(LDAP_PARAM_ERROR);
454 goto out;
456 *hostname = SMB_STRDUP(server);
457 if (*hostname == NULL) {
458 SAFE_FREE(*service);
459 status = ADS_ERROR(LDAP_PARAM_ERROR);
460 goto out;
462 rc = asprintf(&princ, "ldap/%s@%s", server, realm);
463 if (rc == -1 || princ == NULL) {
464 SAFE_FREE(*service);
465 SAFE_FREE(*hostname);
466 status = ADS_ERROR(LDAP_PARAM_ERROR);
467 goto out;
470 *principal = princ;
472 status = ADS_SUCCESS;
473 out:
474 TALLOC_FREE(frame);
475 return status;
478 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
479 struct ads_service_principal *p)
481 ADS_STATUS status;
483 ZERO_STRUCTP(p);
485 status = ads_guess_target(ads,
486 &p->service,
487 &p->hostname,
488 &p->string);
489 if (!ADS_ERR_OK(status)) {
490 return status;
493 return ADS_SUCCESS;
496 #endif /* HAVE_KRB5 */
499 this performs a SASL/SPNEGO bind
501 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
503 TALLOC_CTX *frame = talloc_stackframe();
504 struct ads_service_principal p = {0};
505 struct berval *scred=NULL;
506 int rc, i;
507 ADS_STATUS status;
508 DATA_BLOB blob = data_blob_null;
509 char *given_principal = NULL;
510 char *OIDs[ASN1_MAX_OIDS];
511 #ifdef HAVE_KRB5
512 bool got_kerberos_mechanism = False;
513 #endif
514 const char *mech = NULL;
516 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
518 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
519 status = ADS_ERROR(rc);
520 goto done;
523 blob = data_blob(scred->bv_val, scred->bv_len);
525 ber_bvfree(scred);
527 #if 0
528 file_save("sasl_spnego.dat", blob.data, blob.length);
529 #endif
531 /* the server sent us the first part of the SPNEGO exchange in the negprot
532 reply */
533 if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
534 OIDs[0] == NULL) {
535 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
536 goto done;
538 TALLOC_FREE(given_principal);
540 /* make sure the server understands kerberos */
541 for (i=0;OIDs[i];i++) {
542 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
543 #ifdef HAVE_KRB5
544 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
545 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
546 got_kerberos_mechanism = True;
548 #endif
549 talloc_free(OIDs[i]);
552 status = ads_generate_service_principal(ads, &p);
553 if (!ADS_ERR_OK(status)) {
554 goto done;
557 #ifdef HAVE_KRB5
558 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
559 got_kerberos_mechanism)
561 mech = "KRB5";
563 if (ads->auth.password == NULL ||
564 ads->auth.password[0] == '\0')
567 status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
568 CRED_MUST_USE_KERBEROS,
569 p.service, p.hostname,
570 blob);
571 if (ADS_ERR_OK(status)) {
572 ads_free_service_principal(&p);
573 goto done;
576 DEBUG(10,("ads_sasl_spnego_gensec_bind(KRB5) failed with: %s, "
577 "calling kinit\n", ads_errstr(status)));
580 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
582 if (ADS_ERR_OK(status)) {
583 status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
584 CRED_MUST_USE_KERBEROS,
585 p.service, p.hostname,
586 blob);
587 if (!ADS_ERR_OK(status)) {
588 DEBUG(0,("kinit succeeded but "
589 "ads_sasl_spnego_gensec_bind(KRB5) failed "
590 "for %s/%s with user[%s] realm[%s]: %s\n",
591 p.service, p.hostname,
592 ads->auth.user_name,
593 ads->auth.realm,
594 ads_errstr(status)));
598 /* only fallback to NTLMSSP if allowed */
599 if (ADS_ERR_OK(status) ||
600 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
601 goto done;
604 DEBUG(1,("ads_sasl_spnego_gensec_bind(KRB5) failed "
605 "for %s/%s with user[%s] realm[%s]: %s, "
606 "fallback to NTLMSSP\n",
607 p.service, p.hostname,
608 ads->auth.user_name,
609 ads->auth.realm,
610 ads_errstr(status)));
612 #endif
614 /* lets do NTLMSSP ... this has the big advantage that we don't need
615 to sync clocks, and we don't rely on special versions of the krb5
616 library for HMAC_MD4 encryption */
617 mech = "NTLMSSP";
618 status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
619 CRED_DONT_USE_KERBEROS,
620 p.service, p.hostname,
621 data_blob_null);
622 done:
623 if (!ADS_ERR_OK(status)) {
624 DEBUG(1,("ads_sasl_spnego_gensec_bind(%s) failed "
625 "for %s/%s with user[%s] realm=[%s]: %s\n", mech,
626 p.service, p.hostname,
627 ads->auth.user_name,
628 ads->auth.realm,
629 ads_errstr(status)));
631 ads_free_service_principal(&p);
632 TALLOC_FREE(frame);
633 if (blob.data != NULL) {
634 data_blob_free(&blob);
636 return status;
639 /* mapping between SASL mechanisms and functions */
640 static struct {
641 const char *name;
642 ADS_STATUS (*fn)(ADS_STRUCT *);
643 } sasl_mechanisms[] = {
644 {"GSS-SPNEGO", ads_sasl_spnego_bind},
645 {NULL, NULL}
648 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
650 const char *attrs[] = {"supportedSASLMechanisms", NULL};
651 char **values;
652 ADS_STATUS status;
653 int i, j;
654 LDAPMessage *res;
655 struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
657 /* get a list of supported SASL mechanisms */
658 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
659 if (!ADS_ERR_OK(status)) return status;
661 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
663 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
664 wrap->wrap_type = ADS_SASLWRAP_TYPE_SEAL;
665 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
666 wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
667 } else {
668 wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
671 /* try our supported mechanisms in order */
672 for (i=0;sasl_mechanisms[i].name;i++) {
673 /* see if the server supports it */
674 for (j=0;values && values[j];j++) {
675 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
676 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
677 retry:
678 status = sasl_mechanisms[i].fn(ads);
679 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
680 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
681 wrap->wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
683 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
684 "retrying with signing enabled\n"));
685 wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
686 goto retry;
688 ldap_value_free(values);
689 ldap_msgfree(res);
690 return status;
695 ldap_value_free(values);
696 ldap_msgfree(res);
697 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
700 #endif /* HAVE_LDAP */