ctdb-failover: Split statd_callout add-client/del-client
[Samba.git] / source3 / libads / sasl.c
blobefb2d88030546066b4618b5efcf370c83070e0b4
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 "auth/credentials/credentials.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/param.h"
28 #include "lib/util/asn1.h"
30 NTSTATUS ads_simple_creds(TALLOC_CTX *mem_ctx,
31 const char *account_domain,
32 const char *account_name,
33 const char *password,
34 struct cli_credentials **_creds)
36 TALLOC_CTX *frame = talloc_stackframe();
37 struct cli_credentials *creds = NULL;
38 struct loadparm_context *lp_ctx = NULL;
39 bool ok;
41 lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
42 if (lp_ctx == NULL) {
43 DBG_ERR("loadparm_init_s3 failed\n");
44 TALLOC_FREE(frame);
45 return NT_STATUS_INVALID_SERVER_STATE;
48 creds = cli_credentials_init(mem_ctx);
49 if (creds == NULL) {
50 TALLOC_FREE(frame);
51 return NT_STATUS_NO_MEMORY;
53 talloc_steal(frame, creds);
55 ok = cli_credentials_guess(creds, lp_ctx);
56 if (!ok) {
57 TALLOC_FREE(frame);
58 return NT_STATUS_INTERNAL_ERROR;
61 if (account_domain != NULL && account_domain[0] != '\0') {
62 ok = cli_credentials_set_domain(creds,
63 account_domain,
64 CRED_SPECIFIED);
65 if (!ok) {
66 TALLOC_FREE(frame);
67 return NT_STATUS_NO_MEMORY;
70 if (password != NULL) {
71 ok = cli_credentials_set_password(creds,
72 password,
73 CRED_SPECIFIED);
74 if (!ok) {
75 TALLOC_FREE(frame);
76 return NT_STATUS_NO_MEMORY;
80 cli_credentials_parse_string(creds,
81 account_name,
82 CRED_SPECIFIED);
84 *_creds = talloc_move(mem_ctx, &creds);
85 TALLOC_FREE(frame);
86 return NT_STATUS_OK;
89 #ifdef HAVE_LDAP
91 static ADS_STATUS ads_sasl_gensec_wrap(struct ads_saslwrap *wrap,
92 uint8_t *buf, uint32_t len)
94 struct gensec_security *gensec_security =
95 talloc_get_type_abort(wrap->wrap_private_data,
96 struct gensec_security);
97 NTSTATUS nt_status;
98 DATA_BLOB unwrapped, wrapped;
99 TALLOC_CTX *frame = talloc_stackframe();
101 unwrapped = data_blob_const(buf, len);
103 nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
104 if (!NT_STATUS_IS_OK(nt_status)) {
105 TALLOC_FREE(frame);
106 return ADS_ERROR_NT(nt_status);
109 if ((wrap->out.size - 4) < wrapped.length) {
110 TALLOC_FREE(frame);
111 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
114 /* copy the wrapped blob to the right location */
115 memcpy(wrap->out.buf + 4, wrapped.data, wrapped.length);
117 /* set how many bytes must be written to the underlying socket */
118 wrap->out.left = 4 + wrapped.length;
120 TALLOC_FREE(frame);
122 return ADS_SUCCESS;
125 static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap)
127 struct gensec_security *gensec_security =
128 talloc_get_type_abort(wrap->wrap_private_data,
129 struct gensec_security);
130 NTSTATUS nt_status;
131 DATA_BLOB unwrapped, wrapped;
132 TALLOC_CTX *frame = talloc_stackframe();
134 wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4);
136 nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
137 if (!NT_STATUS_IS_OK(nt_status)) {
138 TALLOC_FREE(frame);
139 return ADS_ERROR_NT(nt_status);
142 if (wrapped.length < unwrapped.length) {
143 TALLOC_FREE(frame);
144 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
147 /* copy the wrapped blob to the right location */
148 memcpy(wrap->in.buf + 4, unwrapped.data, unwrapped.length);
150 /* set how many bytes must be written to the underlying socket */
151 wrap->in.left = unwrapped.length;
152 wrap->in.ofs = 4;
154 TALLOC_FREE(frame);
156 return ADS_SUCCESS;
159 static void ads_sasl_gensec_disconnect(struct ads_saslwrap *wrap)
161 struct gensec_security *gensec_security =
162 talloc_get_type_abort(wrap->wrap_private_data,
163 struct gensec_security);
165 TALLOC_FREE(gensec_security);
167 wrap->wrap_ops = NULL;
168 wrap->wrap_private_data = NULL;
171 static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
172 .name = "gensec",
173 .wrap = ads_sasl_gensec_wrap,
174 .unwrap = ads_sasl_gensec_unwrap,
175 .disconnect = ads_sasl_gensec_disconnect
179 perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can
180 we fit on one socket??)
182 static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
183 struct cli_credentials *creds,
184 const char *target_service,
185 const char *target_hostname)
187 DATA_BLOB blob_in = data_blob_null;
188 DATA_BLOB blob_out = data_blob_null;
189 int rc;
190 NTSTATUS nt_status;
191 ADS_STATUS status;
192 struct auth_generic_state *auth_generic_state;
193 const char *sasl = "GSS-SPNEGO";
194 const char *sasl_list[] = { sasl, NULL };
195 struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
196 const DATA_BLOB *tls_cb = NULL;
198 nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
199 if (!NT_STATUS_IS_OK(nt_status)) {
200 return ADS_ERROR_NT(nt_status);
203 if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_creds(auth_generic_state, creds))) {
204 return ADS_ERROR_NT(nt_status);
207 if (target_service != NULL) {
208 nt_status = gensec_set_target_service(
209 auth_generic_state->gensec_security,
210 target_service);
211 if (!NT_STATUS_IS_OK(nt_status)) {
212 return ADS_ERROR_NT(nt_status);
216 if (target_hostname != NULL) {
217 nt_status = gensec_set_target_hostname(
218 auth_generic_state->gensec_security,
219 target_hostname);
220 if (!NT_STATUS_IS_OK(nt_status)) {
221 return ADS_ERROR_NT(nt_status);
225 tls_cb = ads_tls_channel_bindings(&ads->ldap_tls_data);
226 if (tls_cb != NULL) {
227 uint32_t initiator_addrtype = 0;
228 const DATA_BLOB *initiator_address = NULL;
229 uint32_t acceptor_addrtype = 0;
230 const DATA_BLOB *acceptor_address = NULL;
231 const DATA_BLOB *application_data = tls_cb;
233 nt_status = gensec_set_channel_bindings(auth_generic_state->gensec_security,
234 initiator_addrtype,
235 initiator_address,
236 acceptor_addrtype,
237 acceptor_address,
238 application_data);
239 if (!NT_STATUS_IS_OK(nt_status)) {
240 DBG_WARNING("Failed to set GENSEC channel bindings: %s\n",
241 nt_errstr(nt_status));
242 return ADS_ERROR_NT(nt_status);
246 switch (wrap->wrap_type) {
247 case ADS_SASLWRAP_TYPE_SEAL:
248 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
249 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
250 break;
251 case ADS_SASLWRAP_TYPE_SIGN:
252 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
253 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
254 } else {
256 * windows servers are broken with sign only,
257 * so we let the NTLMSSP backend to seal here,
258 * via GENSEC_FEATURE_LDAP_STYLE.
260 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
261 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE);
263 break;
264 case ADS_SASLWRAP_TYPE_PLAIN:
265 break;
268 nt_status = auth_generic_client_start_by_sasl(auth_generic_state,
269 sasl_list);
270 if (!NT_STATUS_IS_OK(nt_status)) {
271 return ADS_ERROR_NT(nt_status);
274 rc = LDAP_SASL_BIND_IN_PROGRESS;
275 blob_in = data_blob_null;
276 blob_out = data_blob_null;
278 while (true) {
279 struct berval cred, *scred = NULL;
281 nt_status = gensec_update(auth_generic_state->gensec_security,
282 talloc_tos(), blob_in, &blob_out);
283 data_blob_free(&blob_in);
284 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
285 && !NT_STATUS_IS_OK(nt_status))
287 TALLOC_FREE(auth_generic_state);
288 data_blob_free(&blob_out);
289 return ADS_ERROR_NT(nt_status);
292 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) {
293 break;
296 cred.bv_val = (char *)blob_out.data;
297 cred.bv_len = blob_out.length;
298 scred = NULL;
299 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred);
300 data_blob_free(&blob_out);
301 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
302 if (scred) {
303 ber_bvfree(scred);
306 TALLOC_FREE(auth_generic_state);
307 return ADS_ERROR(rc);
309 if (scred) {
310 blob_in = data_blob_talloc(talloc_tos(),
311 scred->bv_val,
312 scred->bv_len);
313 if (blob_in.length != scred->bv_len) {
314 ber_bvfree(scred);
315 TALLOC_FREE(auth_generic_state);
316 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
318 ber_bvfree(scred);
319 } else {
320 blob_in = data_blob_null;
322 if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) {
323 break;
327 data_blob_free(&blob_in);
328 data_blob_free(&blob_out);
330 if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) {
331 bool ok;
333 ok = gensec_have_feature(auth_generic_state->gensec_security,
334 GENSEC_FEATURE_SEAL);
335 if (!ok) {
336 DEBUG(0,("The gensec feature sealing request, but unavailable\n"));
337 TALLOC_FREE(auth_generic_state);
338 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
341 ok = gensec_have_feature(auth_generic_state->gensec_security,
342 GENSEC_FEATURE_SIGN);
343 if (!ok) {
344 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
345 TALLOC_FREE(auth_generic_state);
346 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
349 } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) {
350 bool ok;
352 ok = gensec_have_feature(auth_generic_state->gensec_security,
353 GENSEC_FEATURE_SIGN);
354 if (!ok) {
355 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
356 TALLOC_FREE(auth_generic_state);
357 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
361 ads->auth.expire_time = gensec_expire_time(auth_generic_state->gensec_security);
363 if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
364 size_t max_wrapped =
365 gensec_max_wrapped_size(auth_generic_state->gensec_security);
366 wrap->out.max_unwrapped =
367 gensec_max_input_size(auth_generic_state->gensec_security);
369 wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped;
371 * Note that we have to truncate this to 0x2C
372 * (taken from a capture with LDAP unbind), as the
373 * signature size is not constant for Kerberos with
374 * arcfour-hmac-md5.
376 wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C);
377 wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
378 status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld,
379 &ads_sasl_gensec_ops,
380 auth_generic_state->gensec_security);
381 if (!ADS_ERR_OK(status)) {
382 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
383 ads_errstr(status)));
384 TALLOC_FREE(auth_generic_state);
385 return status;
387 /* Only keep the gensec_security element around long-term */
388 talloc_steal(NULL, auth_generic_state->gensec_security);
390 TALLOC_FREE(auth_generic_state);
392 return ADS_ERROR(rc);
395 #ifdef HAVE_KRB5
396 struct ads_service_principal {
397 char *service;
398 char *hostname;
399 char *string;
402 static void ads_free_service_principal(struct ads_service_principal *p)
404 SAFE_FREE(p->service);
405 SAFE_FREE(p->hostname);
406 SAFE_FREE(p->string);
407 ZERO_STRUCTP(p);
410 static ADS_STATUS ads_guess_target(ADS_STRUCT *ads,
411 char **service,
412 char **hostname,
413 char **principal)
415 ADS_STATUS status = ADS_ERROR(LDAP_NO_MEMORY);
416 char *princ = NULL;
417 TALLOC_CTX *frame;
418 char *server = NULL;
419 char *realm = NULL;
420 int rc;
422 frame = talloc_stackframe();
423 if (frame == NULL) {
424 return ADS_ERROR(LDAP_NO_MEMORY);
427 if (ads->server.realm && ads->server.ldap_server) {
428 server = strlower_talloc(frame, ads->server.ldap_server);
429 if (server == NULL) {
430 goto out;
433 realm = strupper_talloc(frame, ads->server.realm);
434 if (realm == NULL) {
435 goto out;
439 * If we got a name which is bigger than a NetBIOS name,
440 * but isn't a FQDN, create one.
442 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
443 char *dnsdomain;
445 dnsdomain = strlower_talloc(frame, ads->server.realm);
446 if (dnsdomain == NULL) {
447 goto out;
450 server = talloc_asprintf(frame,
451 "%s.%s",
452 server, dnsdomain);
453 if (server == NULL) {
454 goto out;
457 } else if (ads->config.realm && ads->config.ldap_server_name) {
458 server = strlower_talloc(frame, ads->config.ldap_server_name);
459 if (server == NULL) {
460 goto out;
463 realm = strupper_talloc(frame, ads->config.realm);
464 if (realm == NULL) {
465 goto out;
469 * If we got a name which is bigger than a NetBIOS name,
470 * but isn't a FQDN, create one.
472 if (strlen(server) > 15 && strstr(server, ".") == NULL) {
473 char *dnsdomain;
475 dnsdomain = strlower_talloc(frame, ads->server.realm);
476 if (dnsdomain == NULL) {
477 goto out;
480 server = talloc_asprintf(frame,
481 "%s.%s",
482 server, dnsdomain);
483 if (server == NULL) {
484 goto out;
489 if (server == NULL || realm == NULL) {
490 goto out;
493 *service = SMB_STRDUP("ldap");
494 if (*service == NULL) {
495 status = ADS_ERROR(LDAP_PARAM_ERROR);
496 goto out;
498 *hostname = SMB_STRDUP(server);
499 if (*hostname == NULL) {
500 SAFE_FREE(*service);
501 status = ADS_ERROR(LDAP_PARAM_ERROR);
502 goto out;
504 rc = asprintf(&princ, "ldap/%s@%s", server, realm);
505 if (rc == -1 || princ == NULL) {
506 SAFE_FREE(*service);
507 SAFE_FREE(*hostname);
508 status = ADS_ERROR(LDAP_PARAM_ERROR);
509 goto out;
512 *principal = princ;
514 status = ADS_SUCCESS;
515 out:
516 TALLOC_FREE(frame);
517 return status;
520 static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
521 struct ads_service_principal *p)
523 ADS_STATUS status;
525 ZERO_STRUCTP(p);
527 status = ads_guess_target(ads,
528 &p->service,
529 &p->hostname,
530 &p->string);
531 if (!ADS_ERR_OK(status)) {
532 return status;
535 return ADS_SUCCESS;
538 #endif /* HAVE_KRB5 */
541 this performs a SASL/SPNEGO bind
543 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads,
544 struct cli_credentials *creds)
546 TALLOC_CTX *frame = talloc_stackframe();
547 struct ads_service_principal p = {0};
548 ADS_STATUS status;
549 const char *debug_username = NULL;
551 status = ads_generate_service_principal(ads, &p);
552 if (!ADS_ERR_OK(status)) {
553 goto done;
556 debug_username = cli_credentials_get_unparsed_name(creds, frame);
557 if (debug_username == NULL) {
558 status = ADS_ERROR_SYSTEM(errno);
559 goto done;
562 status = ads_sasl_spnego_gensec_bind(ads,
563 creds,
564 p.service,
565 p.hostname);
566 if (!ADS_ERR_OK(status)) {
567 DBG_WARNING("ads_sasl_spnego_gensec_bind() failed "
568 "for %s/%s with user[%s]: %s\n",
569 p.service, p.hostname,
570 debug_username,
571 ads_errstr(status));
572 goto done;
575 done:
576 ads_free_service_principal(&p);
577 TALLOC_FREE(frame);
578 return status;
581 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads, struct cli_credentials *creds)
583 ADS_STATUS status;
584 struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
585 bool tls = false;
587 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
588 tls = true;
589 wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
590 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
591 tls = true;
592 wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
593 } else if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
594 wrap->wrap_type = ADS_SASLWRAP_TYPE_SEAL;
595 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
596 wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
597 } else {
598 wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
601 if (tls) {
602 const DATA_BLOB *tls_cb = NULL;
604 tls_cb = ads_tls_channel_bindings(&ads->ldap_tls_data);
605 if (tls_cb == NULL) {
606 DBG_ERR("No TLS channel bindings available\n");
607 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
611 retry:
612 status = ads_sasl_spnego_bind(ads, creds);
613 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
614 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
615 !tls &&
616 wrap->wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
618 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
619 "retrying with signing enabled\n"));
620 wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
621 goto retry;
623 return status;
626 #endif /* HAVE_LDAP */