2 Unix SMB/CIFS implementation.
4 Kerberos backend for GENSEC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
7 Copyright (C) Andrew Tridgell 2001
8 Copyright (C) Luke Howard 2002-2003
9 Copyright (C) Stefan Metzmacher 2004-2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "lib/util/tevent_ntstatus.h"
29 #include "system/kerberos.h"
30 #include "auth/kerberos/kerberos.h"
31 #include "auth/auth.h"
32 #include "lib/tsocket/tsocket.h"
33 #include "librpc/gen_ndr/dcerpc.h"
34 #include "auth/credentials/credentials.h"
35 #include "auth/credentials/credentials_krb5.h"
36 #include "auth/kerberos/kerberos_credentials.h"
37 #include "auth/gensec/gensec.h"
38 #include "auth/gensec/gensec_internal.h"
39 #include "auth/gensec/gensec_proto.h"
40 #include "auth/gensec/gensec_toplevel_proto.h"
41 #include "param/param.h"
42 #include "auth/auth_sam_reply.h"
43 #include "lib/util/util_net.h"
44 #include "../lib/util/asn1.h"
45 #include "auth/kerberos/pac_utils.h"
46 #include "gensec_krb5.h"
48 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *);
50 enum GENSEC_KRB5_STATE {
51 GENSEC_KRB5_SERVER_START,
52 GENSEC_KRB5_CLIENT_START,
53 GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
57 struct gensec_krb5_state {
58 enum GENSEC_KRB5_STATE state_position;
59 struct smb_krb5_context *smb_krb5_context;
60 krb5_auth_context auth_context;
62 krb5_keyblock *keyblock;
65 krb5_flags ap_req_options;
68 static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
70 if (!gensec_krb5_state->smb_krb5_context) {
71 /* We can't clean anything else up unless we started up this far */
74 if (gensec_krb5_state->enc_ticket.length) {
75 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
76 &gensec_krb5_state->enc_ticket);
79 if (gensec_krb5_state->ticket) {
80 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
81 gensec_krb5_state->ticket);
84 /* ccache freed in a child destructor */
86 krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
87 gensec_krb5_state->keyblock);
89 if (gensec_krb5_state->auth_context) {
90 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
91 gensec_krb5_state->auth_context);
97 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
100 struct gensec_krb5_state *gensec_krb5_state;
101 struct cli_credentials *creds;
102 const struct tsocket_address *tlocal_addr, *tremote_addr;
103 krb5_address my_krb5_addr, peer_krb5_addr;
105 creds = gensec_get_credentials(gensec_security);
107 return NT_STATUS_INVALID_PARAMETER;
110 gensec_krb5_state = talloc_zero(gensec_security, struct gensec_krb5_state);
111 if (!gensec_krb5_state) {
112 return NT_STATUS_NO_MEMORY;
115 gensec_security->private_data = gensec_krb5_state;
116 gensec_krb5_state->gssapi = gssapi;
118 talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
120 if (cli_credentials_get_krb5_context(creds,
121 gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
122 talloc_free(gensec_krb5_state);
123 return NT_STATUS_INTERNAL_ERROR;
126 ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
128 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
129 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
130 ret, gensec_krb5_state)));
131 talloc_free(gensec_krb5_state);
132 return NT_STATUS_INTERNAL_ERROR;
135 ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
136 gensec_krb5_state->auth_context,
137 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
139 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
140 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
141 ret, gensec_krb5_state)));
142 talloc_free(gensec_krb5_state);
143 return NT_STATUS_INTERNAL_ERROR;
146 tlocal_addr = gensec_get_local_address(gensec_security);
148 struct samba_sockaddr addr;
151 addr.sa_socklen = tsocket_address_bsd_sockaddr(tlocal_addr,
154 if (addr.sa_socklen < 0) {
155 talloc_free(gensec_krb5_state);
156 return NT_STATUS_INTERNAL_ERROR;
158 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &my_krb5_addr);
160 DBG_WARNING("smb_krb5_sockaddr_to_kaddr (local) failed\n");
161 talloc_free(gensec_krb5_state);
162 return NT_STATUS_INTERNAL_ERROR;
166 tremote_addr = gensec_get_remote_address(gensec_security);
168 struct samba_sockaddr addr;
171 addr.sa_socklen = tsocket_address_bsd_sockaddr(tremote_addr,
174 if (addr.sa_socklen < 0) {
175 talloc_free(gensec_krb5_state);
176 return NT_STATUS_INTERNAL_ERROR;
178 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &peer_krb5_addr);
180 DBG_WARNING("smb_krb5_sockaddr_to_kaddr (remote) failed\n");
181 talloc_free(gensec_krb5_state);
182 return NT_STATUS_INTERNAL_ERROR;
186 ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
187 gensec_krb5_state->auth_context,
188 tlocal_addr ? &my_krb5_addr : NULL,
189 tremote_addr ? &peer_krb5_addr : NULL);
191 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
192 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
193 ret, gensec_krb5_state)));
194 talloc_free(gensec_krb5_state);
195 return NT_STATUS_INTERNAL_ERROR;
201 static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi)
204 struct gensec_krb5_state *gensec_krb5_state;
206 nt_status = gensec_krb5_start(gensec_security, gssapi);
207 if (!NT_STATUS_IS_OK(nt_status)) {
211 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
212 gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
217 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
219 return gensec_krb5_common_server_start(gensec_security, false);
222 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
224 return gensec_krb5_common_server_start(gensec_security, true);
227 static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
229 const char *hostname;
230 struct gensec_krb5_state *gensec_krb5_state;
232 hostname = gensec_get_target_hostname(gensec_security);
234 DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n"));
235 return NT_STATUS_INVALID_PARAMETER;
237 if (is_ipaddress(hostname)) {
238 DEBUG(2, ("Cannot do krb5 to an IP address"));
239 return NT_STATUS_INVALID_PARAMETER;
241 if (strcmp(hostname, "localhost") == 0) {
242 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
243 return NT_STATUS_INVALID_PARAMETER;
246 nt_status = gensec_krb5_start(gensec_security, gssapi);
247 if (!NT_STATUS_IS_OK(nt_status)) {
251 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
252 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
253 gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;
255 if (gensec_krb5_state->gssapi) {
256 /* The Fake GSSAPI model emulates Samba3, which does not do mutual authentication */
257 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
258 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
261 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
262 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
263 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
269 static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security,
270 struct tevent_context *ev,
273 struct gensec_krb5_state *gensec_krb5_state;
275 struct ccache_container *ccache_container;
276 const char *error_string;
277 const char *principal;
278 const char *hostname;
279 krb5_data in_data = { .length = 0 };
280 krb5_data *in_data_p = NULL;
281 #ifdef SAMBA4_USES_HEIMDAL
282 struct tevent_context *previous_ev;
285 if (lpcfg_parm_bool(gensec_security->settings->lp_ctx,
286 NULL, "gensec_krb5", "send_authenticator_checksum", true)) {
287 in_data_p = &in_data;
290 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
292 principal = gensec_get_target_principal(gensec_security);
293 hostname = gensec_get_target_hostname(gensec_security);
295 ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security),
297 gensec_security->settings->lp_ctx, &ccache_container, &error_string);
301 case KRB5KDC_ERR_PREAUTH_FAILED:
302 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
303 return NT_STATUS_LOGON_FAILURE;
304 case KRB5_KDC_UNREACH:
305 DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
306 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
307 case KRB5_CC_NOTFOUND:
309 DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
310 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
312 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
313 return NT_STATUS_UNSUCCESSFUL;
316 #ifdef SAMBA4_USES_HEIMDAL
317 /* Do this every time, in case we have weird recursive issues here */
318 ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, ev, &previous_ev);
320 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
321 return NT_STATUS_NO_MEMORY;
325 krb5_principal target_principal;
326 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
329 krb5_creds this_cred;
332 ZERO_STRUCT(this_cred);
333 ret = krb5_cc_get_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
334 ccache_container->ccache,
337 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
339 return NT_STATUS_UNSUCCESSFUL;
342 ret = krb5_copy_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
345 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
348 krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
350 return NT_STATUS_UNSUCCESSFUL;
352 this_cred.times.endtime = 0;
354 ret = krb5_get_credentials(gensec_krb5_state->smb_krb5_context->krb5_context,
356 ccache_container->ccache,
359 krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
362 return NT_STATUS_UNSUCCESSFUL;
365 ret = krb5_mk_req_extended(gensec_krb5_state->smb_krb5_context->krb5_context,
366 &gensec_krb5_state->auth_context,
367 gensec_krb5_state->ap_req_options,
370 &gensec_krb5_state->enc_ticket);
373 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
374 &gensec_krb5_state->auth_context,
375 gensec_krb5_state->ap_req_options,
376 discard_const_p(char, gensec_get_target_service(gensec_security)),
377 discard_const_p(char, hostname),
378 in_data_p, ccache_container->ccache,
379 &gensec_krb5_state->enc_ticket);
382 #ifdef SAMBA4_USES_HEIMDAL
383 smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, ev);
389 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
390 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
391 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
392 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
393 case KRB5_KDC_UNREACH:
394 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
395 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
396 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
397 case KRB5KDC_ERR_PREAUTH_FAILED:
398 case KRB5KRB_AP_ERR_TKT_EXPIRED:
400 /* Too much clock skew - we will need to kinit to re-skew the clock */
401 case KRB5KRB_AP_ERR_SKEW:
402 case KRB5_KDCREP_SKEW:
403 DEBUG(3, ("kerberos (mk_req) failed: %s\n",
404 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
406 /* just don't print a message for these really ordinary messages */
407 case KRB5_FCC_NOFILE:
408 case KRB5_CC_NOTFOUND:
411 return NT_STATUS_UNSUCCESSFUL;
415 DEBUG(0, ("kerberos: %s\n",
416 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
417 return NT_STATUS_UNSUCCESSFUL;
421 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
423 return gensec_krb5_common_client_start(gensec_security, false);
426 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
428 return gensec_krb5_common_client_start(gensec_security, true);
433 generate a krb5 GSS-API wrapper packet given a ticket
435 static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
437 struct asn1_data *data;
438 DATA_BLOB ret = data_blob_null;
440 data = asn1_init(mem_ctx);
441 if (!data || !ticket->data) {
445 if (!asn1_push_tag(data, ASN1_APPLICATION(0))) goto err;
446 if (!asn1_write_OID(data, GENSEC_OID_KERBEROS5)) goto err;
448 if (!asn1_write(data, tok_id, 2)) goto err;
449 if (!asn1_write(data, ticket->data, ticket->length)) goto err;
450 if (!asn1_pop_tag(data)) goto err;
453 if (!asn1_extract_blob(data, mem_ctx, &ret)) {
462 DEBUG(1, ("Failed to build krb5 wrapper at offset %d\n",
463 (int)asn1_current_ofs(data)));
469 parse a krb5 GSS-API wrapper packet giving a ticket
471 static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
474 struct asn1_data *data = asn1_init(mem_ctx);
481 if (!asn1_load(data, *blob)) goto err;
482 if (!asn1_start_tag(data, ASN1_APPLICATION(0))) goto err;
483 if (!asn1_check_OID(data, GENSEC_OID_KERBEROS5)) goto err;
485 data_remaining = asn1_tag_remaining(data);
487 if (data_remaining < 3) {
488 asn1_set_error(data);
490 if (!asn1_read(data, tok_id, 2)) goto err;
492 *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
493 if (!asn1_read(data, ticket->data, ticket->length)) goto err;
496 if (!asn1_end_tag(data)) goto err;
498 ret = !asn1_has_error(data);
507 static NTSTATUS gensec_krb5_update_internal(struct gensec_security *gensec_security,
508 TALLOC_CTX *out_mem_ctx,
509 struct tevent_context *ev,
510 const DATA_BLOB in, DATA_BLOB *out)
512 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
513 krb5_error_code ret = 0;
516 switch (gensec_krb5_state->state_position) {
517 case GENSEC_KRB5_CLIENT_START:
519 DATA_BLOB unwrapped_out;
521 nt_status = gensec_krb5_common_client_creds(gensec_security, ev, gensec_krb5_state->gssapi);
522 if (!NT_STATUS_IS_OK(nt_status)) {
526 if (gensec_krb5_state->gssapi) {
527 unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
529 /* wrap that up in a nice GSS-API wrapping */
530 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
532 *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
534 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
535 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
536 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
538 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
539 nt_status = NT_STATUS_OK;
544 case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
546 DATA_BLOB unwrapped_in;
548 krb5_ap_rep_enc_part *repl = NULL;
551 if (gensec_krb5_state->gssapi) {
552 if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
553 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
554 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
555 return NT_STATUS_INVALID_PARAMETER;
560 /* TODO: check the tok_id */
562 inbuf.data = (char *)unwrapped_in.data;
563 inbuf.length = unwrapped_in.length;
564 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
565 gensec_krb5_state->auth_context,
568 DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
569 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
570 dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
571 nt_status = NT_STATUS_ACCESS_DENIED;
573 *out = data_blob(NULL, 0);
574 nt_status = NT_STATUS_OK;
575 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
578 krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
583 case GENSEC_KRB5_SERVER_START:
585 DATA_BLOB unwrapped_in;
586 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
587 krb5_data inbuf, outbuf;
589 struct keytab_container *keytab;
590 krb5_principal server_in_keytab;
591 const char *error_string;
592 enum credentials_obtained obtained;
595 return NT_STATUS_INVALID_PARAMETER;
598 /* Grab the keytab, however generated */
599 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security),
600 gensec_security->settings->lp_ctx, &keytab);
602 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
605 /* This ensures we lookup the correct entry in that
606 * keytab. A NULL principal is acceptable, and means
607 * that the krb5 libs should search the keytab at
608 * accept time for any matching key */
609 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
610 gensec_krb5_state->smb_krb5_context,
611 &server_in_keytab, &obtained, &error_string);
614 DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
615 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
618 if (keytab->password_based || obtained < CRED_SPECIFIED) {
620 * Use match-by-key in this case (matches
621 * cli_credentials_get_server_gss_creds()
622 * behaviour). No need to free the memory,
623 * this is handled with a talloc destructor.
625 server_in_keytab = NULL;
628 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
629 if (gensec_krb5_state->gssapi
630 && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
631 inbuf.data = (char *)unwrapped_in.data;
632 inbuf.length = unwrapped_in.length;
634 inbuf.data = (char *)in.data;
635 inbuf.length = in.length;
638 ret = smb_krb5_rd_req_decoded(gensec_krb5_state->smb_krb5_context->krb5_context,
639 &gensec_krb5_state->auth_context,
644 &gensec_krb5_state->ticket,
645 &gensec_krb5_state->keyblock);
648 DBG_WARNING("smb_krb5_rd_req_decoded failed\n");
649 return NT_STATUS_LOGON_FAILURE;
651 unwrapped_out.data = (uint8_t *)outbuf.data;
652 unwrapped_out.length = outbuf.length;
653 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
654 /* wrap that up in a nice GSS-API wrapping */
655 if (gensec_krb5_state->gssapi) {
656 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
658 *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
660 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
665 case GENSEC_KRB5_DONE:
667 /* Asking too many times... */
668 return NT_STATUS_INVALID_PARAMETER;
672 struct gensec_krb5_update_state {
677 static struct tevent_req *gensec_krb5_update_send(TALLOC_CTX *mem_ctx,
678 struct tevent_context *ev,
679 struct gensec_security *gensec_security,
682 struct tevent_req *req = NULL;
683 struct gensec_krb5_update_state *state = NULL;
686 req = tevent_req_create(mem_ctx, &state,
687 struct gensec_krb5_update_state);
692 status = gensec_krb5_update_internal(gensec_security,
695 state->status = status;
696 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
697 tevent_req_done(req);
698 return tevent_req_post(req, ev);
700 if (tevent_req_nterror(req, status)) {
701 return tevent_req_post(req, ev);
704 tevent_req_done(req);
705 return tevent_req_post(req, ev);
708 static NTSTATUS gensec_krb5_update_recv(struct tevent_req *req,
709 TALLOC_CTX *out_mem_ctx,
712 struct gensec_krb5_update_state *state =
714 struct gensec_krb5_update_state);
717 *out = data_blob_null;
719 if (tevent_req_is_nterror(req, &status)) {
720 tevent_req_received(req);
725 talloc_steal(out_mem_ctx, state->out.data);
726 status = state->status;
727 tevent_req_received(req);
731 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
733 DATA_BLOB *session_key)
735 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
736 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
737 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
738 krb5_error_code err = -1;
742 if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
743 return NT_STATUS_NO_USER_SESSION_KEY;
746 switch (gensec_security->gensec_role) {
755 ok = smb_krb5_get_smb_session_key(mem_ctx,
761 DEBUG(10, ("KRB5 error getting session key %d\n", err));
762 return NT_STATUS_NO_USER_SESSION_KEY;
768 #ifdef SAMBA4_USES_HEIMDAL
769 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
771 struct auth_session_info **_session_info)
773 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
774 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
775 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
776 struct auth_session_info *session_info = NULL;
778 krb5_principal client_principal;
779 char *principal_string = NULL;
781 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
786 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
788 return NT_STATUS_NO_MEMORY;
791 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
793 DEBUG(5, ("krb5_ticket_get_client failed to get client principal: %s\n",
794 smb_get_krb5_error_message(context,
796 talloc_free(tmp_ctx);
797 return NT_STATUS_NO_MEMORY;
800 ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
801 client_principal, &principal_string);
803 DEBUG(1, ("Unable to parse client principal: %s\n",
804 smb_get_krb5_error_message(context,
806 krb5_free_principal(context, client_principal);
807 talloc_free(tmp_ctx);
808 return NT_STATUS_NO_MEMORY;
811 ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
812 KRB5_AUTHDATA_WIN2K_PAC,
817 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
818 smb_get_krb5_error_message(context,
822 pac_blob = data_blob_talloc(tmp_ctx, pac_data.data, pac_data.length);
823 smb_krb5_free_data_contents(context, &pac_data);
824 if (!pac_blob.data) {
825 free(principal_string);
826 krb5_free_principal(context, client_principal);
827 talloc_free(tmp_ctx);
828 return NT_STATUS_NO_MEMORY;
831 /* decode and verify the pac */
832 nt_status = kerberos_decode_pac(gensec_krb5_state,
834 gensec_krb5_state->smb_krb5_context->krb5_context,
835 NULL, gensec_krb5_state->keyblock,
837 gensec_krb5_state->ticket->ticket.authtime, NULL);
839 if (!NT_STATUS_IS_OK(nt_status)) {
840 free(principal_string);
841 krb5_free_principal(context, client_principal);
842 talloc_free(tmp_ctx);
846 pac_blob_ptr = &pac_blob;
849 nt_status = gensec_generate_session_info_pac(tmp_ctx,
851 gensec_krb5_state->smb_krb5_context,
852 pac_blob_ptr, principal_string,
853 gensec_get_remote_address(gensec_security),
856 free(principal_string);
857 krb5_free_principal(context, client_principal);
859 if (!NT_STATUS_IS_OK(nt_status)) {
860 talloc_free(tmp_ctx);
864 nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);
866 if (!NT_STATUS_IS_OK(nt_status)) {
867 talloc_free(tmp_ctx);
871 *_session_info = talloc_steal(mem_ctx, session_info);
873 talloc_free(tmp_ctx);
876 #else /* MIT KERBEROS */
877 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
879 struct auth_session_info **psession_info)
881 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
882 struct gensec_krb5_state *gensec_krb5_state =
883 (struct gensec_krb5_state *)gensec_security->private_data;
884 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
885 struct auth_session_info *session_info = NULL;
887 krb5_principal client_principal;
888 char *principal_string = NULL;
890 krb5_authdata **auth_pac_data = NULL;
891 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
893 krb5_error_code code;
897 tmp_ctx = talloc_new(mem_ctx);
898 if (tmp_ctx == NULL) {
899 return NT_STATUS_NO_MEMORY;
902 code = krb5_copy_principal(context,
903 gensec_krb5_state->ticket->enc_part2->client,
906 DBG_INFO("krb5_copy_principal failed to copy client "
908 smb_get_krb5_error_message(context, code, tmp_ctx));
909 talloc_free(tmp_ctx);
910 return NT_STATUS_NO_MEMORY;
913 code = krb5_unparse_name(context, client_principal, &principal_string);
915 DBG_WARNING("Unable to parse client principal: %s\n",
916 smb_get_krb5_error_message(context, code, tmp_ctx));
917 krb5_free_principal(context, client_principal);
918 talloc_free(tmp_ctx);
919 return NT_STATUS_NO_MEMORY;
922 code = krb5_find_authdata(context,
923 gensec_krb5_state->ticket->enc_part2->authorization_data,
925 KRB5_AUTHDATA_WIN2K_PAC,
929 DBG_INFO("krb5_find_authdata failed to find PAC: %s\n",
930 smb_get_krb5_error_message(context, code, tmp_ctx));
932 krb5_timestamp ticket_authtime =
933 gensec_krb5_state->ticket->enc_part2->times.authtime;
936 pac_blob = data_blob_talloc(tmp_ctx,
937 auth_pac_data[0]->contents,
938 auth_pac_data[0]->length);
939 krb5_free_authdata(context, auth_pac_data);
940 if (pac_blob.data == NULL) {
941 free(principal_string);
942 krb5_free_principal(context, client_principal);
943 talloc_free(tmp_ctx);
944 return NT_STATUS_NO_MEMORY;
947 /* decode and verify the pac */
948 status = kerberos_decode_pac(gensec_krb5_state,
952 gensec_krb5_state->keyblock,
957 if (!NT_STATUS_IS_OK(status)) {
958 free(principal_string);
959 krb5_free_principal(context, client_principal);
960 talloc_free(tmp_ctx);
964 pac_blob_ptr = &pac_blob;
966 krb5_free_principal(context, client_principal);
968 status = gensec_generate_session_info_pac(tmp_ctx,
970 gensec_krb5_state->smb_krb5_context,
973 gensec_get_remote_address(gensec_security),
975 SAFE_FREE(principal_string);
976 if (!NT_STATUS_IS_OK(status)) {
977 talloc_free(tmp_ctx);
981 status = gensec_krb5_session_key(gensec_security,
983 &session_info->session_key);
984 if (!NT_STATUS_IS_OK(status)) {
985 talloc_free(tmp_ctx);
989 *psession_info = talloc_steal(mem_ctx, session_info);
990 talloc_free(tmp_ctx);
994 #endif /* SAMBA4_USES_HEIMDAL */
996 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
1001 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1002 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
1003 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
1004 krb5_error_code ret;
1005 krb5_data input, output;
1006 input.length = in->length;
1007 input.data = (char *)in->data;
1009 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
1010 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
1012 DEBUG(1, ("krb5_mk_priv failed: %s\n",
1013 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
1015 return NT_STATUS_ACCESS_DENIED;
1017 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1019 smb_krb5_free_data_contents(context, &output);
1021 return NT_STATUS_ACCESS_DENIED;
1023 return NT_STATUS_OK;
1026 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
1027 TALLOC_CTX *mem_ctx,
1028 const DATA_BLOB *in,
1031 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1032 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
1033 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
1034 krb5_error_code ret;
1035 krb5_data input, output;
1036 krb5_replay_data replay;
1037 input.length = in->length;
1038 input.data = (char *)in->data;
1040 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
1041 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
1043 DEBUG(1, ("krb5_rd_priv failed: %s\n",
1044 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
1046 return NT_STATUS_ACCESS_DENIED;
1048 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1050 smb_krb5_free_data_contents(context, &output);
1052 return NT_STATUS_ACCESS_DENIED;
1054 return NT_STATUS_OK;
1057 static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
1060 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1061 if (feature & GENSEC_FEATURE_SESSION_KEY) {
1064 if (gensec_krb5_state->gssapi) {
1069 * krb5_mk_priv provides SIGN and SEAL
1071 if (feature & GENSEC_FEATURE_SIGN) {
1074 if (feature & GENSEC_FEATURE_SEAL) {
1081 static const char *gensec_krb5_final_auth_type(struct gensec_security *gensec_security)
1083 return GENSEC_FINAL_AUTH_TYPE_KRB5;
1086 static const char *gensec_krb5_oids[] = {
1087 GENSEC_OID_KERBEROS5,
1088 GENSEC_OID_KERBEROS5_OLD,
1092 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
1093 .name = "fake_gssapi_krb5",
1094 .auth_type = DCERPC_AUTH_TYPE_KRB5,
1095 .oid = gensec_krb5_oids,
1096 .client_start = gensec_fake_gssapi_krb5_client_start,
1097 .server_start = gensec_fake_gssapi_krb5_server_start,
1098 .update_send = gensec_krb5_update_send,
1099 .update_recv = gensec_krb5_update_recv,
1100 .magic = gensec_magic_check_krb5_oid,
1101 .session_key = gensec_krb5_session_key,
1102 .session_info = gensec_krb5_session_info,
1103 .have_feature = gensec_krb5_have_feature,
1104 .final_auth_type = gensec_krb5_final_auth_type,
1107 .priority = GENSEC_KRB5,
1110 static const struct gensec_security_ops gensec_krb5_security_ops = {
1112 .client_start = gensec_krb5_client_start,
1113 .server_start = gensec_krb5_server_start,
1114 .update_send = gensec_krb5_update_send,
1115 .update_recv = gensec_krb5_update_recv,
1116 .session_key = gensec_krb5_session_key,
1117 .session_info = gensec_krb5_session_info,
1118 .have_feature = gensec_krb5_have_feature,
1119 .wrap = gensec_krb5_wrap,
1120 .unwrap = gensec_krb5_unwrap,
1121 .final_auth_type = gensec_krb5_final_auth_type,
1124 .priority = GENSEC_KRB5
1127 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *ctx)
1131 ret = gensec_register(ctx, &gensec_krb5_security_ops);
1132 if (!NT_STATUS_IS_OK(ret)) {
1133 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1134 gensec_krb5_security_ops.name));
1138 ret = gensec_register(ctx, &gensec_fake_gssapi_krb5_security_ops);
1139 if (!NT_STATUS_IS_OK(ret)) {
1140 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1141 gensec_fake_gssapi_krb5_security_ops.name));