s3: smbd: Correctly process SMB3 POSIX paths in create.
[Samba.git] / source4 / auth / gensec / gensec_krb5.c
blob104e4639c4466efc729a9ca8a80748fc48838549
1 /*
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/>.
26 #include "includes.h"
27 #include <tevent.h>
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"
47 #include "gensec_krb5_internal.h"
48 #include "gensec_krb5_helpers.h"
50 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *);
52 static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
54 if (!gensec_krb5_state->smb_krb5_context) {
55 /* We can't clean anything else up unless we started up this far */
56 return 0;
58 if (gensec_krb5_state->enc_ticket.length) {
59 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
60 &gensec_krb5_state->enc_ticket);
63 if (gensec_krb5_state->ticket) {
64 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
65 gensec_krb5_state->ticket);
68 /* ccache freed in a child destructor */
70 krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
71 gensec_krb5_state->keyblock);
73 if (gensec_krb5_state->auth_context) {
74 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
75 gensec_krb5_state->auth_context);
78 return 0;
81 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
83 krb5_error_code ret;
84 struct gensec_krb5_state *gensec_krb5_state;
85 struct cli_credentials *creds;
86 const struct tsocket_address *tlocal_addr, *tremote_addr;
87 krb5_address my_krb5_addr, peer_krb5_addr;
89 creds = gensec_get_credentials(gensec_security);
90 if (!creds) {
91 return NT_STATUS_INVALID_PARAMETER;
94 gensec_krb5_state = talloc_zero(gensec_security, struct gensec_krb5_state);
95 if (!gensec_krb5_state) {
96 return NT_STATUS_NO_MEMORY;
99 gensec_security->private_data = gensec_krb5_state;
100 gensec_krb5_state->gssapi = gssapi;
102 talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
104 if (cli_credentials_get_krb5_context(creds,
105 gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
106 talloc_free(gensec_krb5_state);
107 return NT_STATUS_INTERNAL_ERROR;
110 ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
111 if (ret) {
112 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
113 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
114 ret, gensec_krb5_state)));
115 talloc_free(gensec_krb5_state);
116 return NT_STATUS_INTERNAL_ERROR;
119 ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
120 gensec_krb5_state->auth_context,
121 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
122 if (ret) {
123 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
124 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
125 ret, gensec_krb5_state)));
126 talloc_free(gensec_krb5_state);
127 return NT_STATUS_INTERNAL_ERROR;
130 tlocal_addr = gensec_get_local_address(gensec_security);
131 if (tlocal_addr) {
132 ssize_t sockaddr_ret;
133 struct samba_sockaddr addr;
134 bool ok;
136 addr.sa_socklen = sizeof(addr.u);
137 sockaddr_ret = tsocket_address_bsd_sockaddr(
138 tlocal_addr, &addr.u.sa, addr.sa_socklen);
139 if (sockaddr_ret < 0) {
140 talloc_free(gensec_krb5_state);
141 return NT_STATUS_INTERNAL_ERROR;
143 addr.sa_socklen = sockaddr_ret;
144 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &my_krb5_addr);
145 if (!ok) {
146 DBG_WARNING("smb_krb5_sockaddr_to_kaddr (local) failed\n");
147 talloc_free(gensec_krb5_state);
148 return NT_STATUS_INTERNAL_ERROR;
152 tremote_addr = gensec_get_remote_address(gensec_security);
153 if (tremote_addr) {
154 ssize_t sockaddr_ret;
155 struct samba_sockaddr addr;
156 bool ok;
158 addr.sa_socklen = sizeof(addr.u);
159 sockaddr_ret = tsocket_address_bsd_sockaddr(
160 tremote_addr, &addr.u.sa, addr.sa_socklen);
161 if (sockaddr_ret < 0) {
162 talloc_free(gensec_krb5_state);
163 return NT_STATUS_INTERNAL_ERROR;
165 addr.sa_socklen = sockaddr_ret;
166 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &peer_krb5_addr);
167 if (!ok) {
168 DBG_WARNING("smb_krb5_sockaddr_to_kaddr (remote) failed\n");
169 talloc_free(gensec_krb5_state);
170 return NT_STATUS_INTERNAL_ERROR;
174 ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
175 gensec_krb5_state->auth_context,
176 tlocal_addr ? &my_krb5_addr : NULL,
177 tremote_addr ? &peer_krb5_addr : NULL);
178 if (ret) {
179 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
180 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
181 ret, gensec_krb5_state)));
182 talloc_free(gensec_krb5_state);
183 return NT_STATUS_INTERNAL_ERROR;
186 return NT_STATUS_OK;
189 static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi)
191 NTSTATUS nt_status;
192 struct gensec_krb5_state *gensec_krb5_state;
194 nt_status = gensec_krb5_start(gensec_security, gssapi);
195 if (!NT_STATUS_IS_OK(nt_status)) {
196 return nt_status;
199 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
200 gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
202 return NT_STATUS_OK;
205 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
207 return gensec_krb5_common_server_start(gensec_security, false);
210 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
212 return gensec_krb5_common_server_start(gensec_security, true);
215 static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
217 const char *hostname;
218 struct gensec_krb5_state *gensec_krb5_state;
219 NTSTATUS nt_status;
220 hostname = gensec_get_target_hostname(gensec_security);
221 if (!hostname) {
222 DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n"));
223 return NT_STATUS_INVALID_PARAMETER;
225 if (is_ipaddress(hostname)) {
226 DEBUG(2, ("Cannot do krb5 to an IP address"));
227 return NT_STATUS_INVALID_PARAMETER;
229 if (strcmp(hostname, "localhost") == 0) {
230 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
231 return NT_STATUS_INVALID_PARAMETER;
234 nt_status = gensec_krb5_start(gensec_security, gssapi);
235 if (!NT_STATUS_IS_OK(nt_status)) {
236 return nt_status;
239 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
240 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
241 gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;
243 if (gensec_krb5_state->gssapi) {
244 /* The Fake GSSAPI model emulates Samba3, which does not do mutual authentication */
245 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
246 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
248 } else {
249 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
250 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
251 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
254 return NT_STATUS_OK;
257 static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security,
258 struct tevent_context *ev)
260 struct gensec_krb5_state *gensec_krb5_state;
261 krb5_error_code ret;
262 struct ccache_container *ccache_container;
263 const char *error_string;
264 const char *principal;
265 const char *hostname;
266 krb5_data in_data = { .length = 0 };
267 krb5_data *in_data_p = NULL;
268 #ifdef SAMBA4_USES_HEIMDAL
269 struct tevent_context *previous_ev;
270 #endif
272 if (lpcfg_parm_bool(gensec_security->settings->lp_ctx,
273 NULL, "gensec_krb5", "send_authenticator_checksum", true)) {
274 in_data_p = &in_data;
277 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
279 principal = gensec_get_target_principal(gensec_security);
280 hostname = gensec_get_target_hostname(gensec_security);
282 ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security),
284 gensec_security->settings->lp_ctx, &ccache_container, &error_string);
285 switch (ret) {
286 case 0:
287 break;
288 case KRB5KDC_ERR_PREAUTH_FAILED:
289 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
290 return NT_STATUS_LOGON_FAILURE;
291 case KRB5_KDC_UNREACH:
292 DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
293 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
294 case KRB5_CC_NOTFOUND:
295 case KRB5_CC_END:
296 DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
297 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
298 default:
299 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
300 return NT_STATUS_UNSUCCESSFUL;
303 #ifdef SAMBA4_USES_HEIMDAL
304 /* Do this every time, in case we have weird recursive issues here */
305 ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, ev, &previous_ev);
306 if (ret != 0) {
307 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
308 return NT_STATUS_NO_MEMORY;
310 #endif
311 if (principal) {
312 krb5_principal target_principal;
313 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
314 &target_principal);
315 if (ret == 0) {
316 krb5_creds this_cred;
317 krb5_creds *cred;
319 ZERO_STRUCT(this_cred);
320 ret = krb5_cc_get_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
321 ccache_container->ccache,
322 &this_cred.client);
323 if (ret != 0) {
324 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
325 target_principal);
326 return NT_STATUS_UNSUCCESSFUL;
329 ret = krb5_copy_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
330 target_principal,
331 &this_cred.server);
332 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
333 target_principal);
334 if (ret != 0) {
335 krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
336 &this_cred);
337 return NT_STATUS_UNSUCCESSFUL;
339 this_cred.times.endtime = 0;
341 ret = krb5_get_credentials(gensec_krb5_state->smb_krb5_context->krb5_context,
343 ccache_container->ccache,
344 &this_cred,
345 &cred);
346 krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
347 &this_cred);
348 if (ret != 0) {
349 return NT_STATUS_UNSUCCESSFUL;
352 ret = krb5_mk_req_extended(gensec_krb5_state->smb_krb5_context->krb5_context,
353 &gensec_krb5_state->auth_context,
354 gensec_krb5_state->ap_req_options,
355 in_data_p,
356 cred,
357 &gensec_krb5_state->enc_ticket);
359 } else {
360 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
361 &gensec_krb5_state->auth_context,
362 gensec_krb5_state->ap_req_options,
363 discard_const_p(char, gensec_get_target_service(gensec_security)),
364 discard_const_p(char, hostname),
365 in_data_p, ccache_container->ccache,
366 &gensec_krb5_state->enc_ticket);
369 #ifdef SAMBA4_USES_HEIMDAL
370 smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, ev);
371 #endif
373 switch (ret) {
374 case 0:
375 return NT_STATUS_OK;
376 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
377 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
378 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
379 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
380 case KRB5_KDC_UNREACH:
381 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
382 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
383 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
384 case KRB5KDC_ERR_PREAUTH_FAILED:
385 case KRB5KRB_AP_ERR_TKT_EXPIRED:
386 case KRB5_CC_END:
387 /* Too much clock skew - we will need to kinit to re-skew the clock */
388 case KRB5KRB_AP_ERR_SKEW:
389 case KRB5_KDCREP_SKEW:
390 DEBUG(3, ("kerberos (mk_req) failed: %s\n",
391 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
392 FALL_THROUGH;
393 /* just don't print a message for these really ordinary messages */
394 case KRB5_FCC_NOFILE:
395 case KRB5_CC_NOTFOUND:
396 case ENOENT:
398 return NT_STATUS_UNSUCCESSFUL;
399 break;
401 default:
402 DEBUG(0, ("kerberos: %s\n",
403 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
404 return NT_STATUS_UNSUCCESSFUL;
408 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
410 return gensec_krb5_common_client_start(gensec_security, false);
413 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
415 return gensec_krb5_common_client_start(gensec_security, true);
420 generate a krb5 GSS-API wrapper packet given a ticket
422 static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
424 struct asn1_data *data;
425 DATA_BLOB ret = data_blob_null;
427 data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
428 if (!data || !ticket->data) {
429 return ret;
432 if (!asn1_push_tag(data, ASN1_APPLICATION(0))) goto err;
433 if (!asn1_write_OID(data, GENSEC_OID_KERBEROS5)) goto err;
435 if (!asn1_write(data, tok_id, 2)) goto err;
436 if (!asn1_write(data, ticket->data, ticket->length)) goto err;
437 if (!asn1_pop_tag(data)) goto err;
440 if (!asn1_extract_blob(data, mem_ctx, &ret)) {
441 goto err;
443 asn1_free(data);
445 return ret;
447 err:
449 DEBUG(1, ("Failed to build krb5 wrapper at offset %d\n",
450 (int)asn1_current_ofs(data)));
451 asn1_free(data);
452 return ret;
456 parse a krb5 GSS-API wrapper packet giving a ticket
458 static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
460 bool ret = false;
461 struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
462 int data_remaining;
464 if (!data) {
465 return false;
468 if (!asn1_load(data, *blob)) goto err;
469 if (!asn1_start_tag(data, ASN1_APPLICATION(0))) goto err;
470 if (!asn1_check_OID(data, GENSEC_OID_KERBEROS5)) goto err;
472 data_remaining = asn1_tag_remaining(data);
474 if (data_remaining < 3) {
475 asn1_set_error(data);
476 } else {
477 if (!asn1_read(data, tok_id, 2)) goto err;
478 data_remaining -= 2;
479 *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
480 if (!asn1_read(data, ticket->data, ticket->length)) goto err;
483 if (!asn1_end_tag(data)) goto err;
485 ret = !asn1_has_error(data);
487 err:
489 asn1_free(data);
491 return ret;
494 static NTSTATUS gensec_krb5_update_internal(struct gensec_security *gensec_security,
495 TALLOC_CTX *out_mem_ctx,
496 struct tevent_context *ev,
497 const DATA_BLOB in, DATA_BLOB *out)
499 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
500 krb5_error_code ret = 0;
501 NTSTATUS nt_status;
503 switch (gensec_krb5_state->state_position) {
504 case GENSEC_KRB5_CLIENT_START:
506 DATA_BLOB unwrapped_out;
508 nt_status = gensec_krb5_common_client_creds(gensec_security, ev);
509 if (!NT_STATUS_IS_OK(nt_status)) {
510 return nt_status;
513 if (gensec_krb5_state->gssapi) {
514 unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
516 /* wrap that up in a nice GSS-API wrapping */
517 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
518 } else {
519 *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
521 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
522 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
523 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
524 } else {
525 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
526 nt_status = NT_STATUS_OK;
528 return nt_status;
531 case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
533 DATA_BLOB unwrapped_in;
534 krb5_data inbuf;
535 krb5_ap_rep_enc_part *repl = NULL;
536 uint8_t tok_id[2];
538 if (gensec_krb5_state->gssapi) {
539 if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
540 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
541 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
542 return NT_STATUS_INVALID_PARAMETER;
544 } else {
545 unwrapped_in = in;
547 /* TODO: check the tok_id */
549 inbuf.data = (char *)unwrapped_in.data;
550 inbuf.length = unwrapped_in.length;
551 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
552 gensec_krb5_state->auth_context,
553 &inbuf, &repl);
554 if (ret) {
555 DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
556 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
557 dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
558 nt_status = NT_STATUS_ACCESS_DENIED;
559 } else {
560 *out = data_blob(NULL, 0);
561 nt_status = NT_STATUS_OK;
562 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
564 if (repl) {
565 krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
567 return nt_status;
570 case GENSEC_KRB5_SERVER_START:
572 DATA_BLOB unwrapped_in;
573 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
574 krb5_data inbuf, outbuf;
575 uint8_t tok_id[2];
576 struct keytab_container *keytab;
577 krb5_principal server_in_keytab;
578 const char *error_string;
579 enum credentials_obtained obtained;
581 if (!in.data) {
582 return NT_STATUS_INVALID_PARAMETER;
585 /* Grab the keytab, however generated */
586 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security),
587 gensec_security->settings->lp_ctx, &keytab);
588 if (ret) {
589 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
592 /* This ensures we lookup the correct entry in that
593 * keytab. A NULL principal is acceptable, and means
594 * that the krb5 libs should search the keytab at
595 * accept time for any matching key */
596 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
597 gensec_krb5_state->smb_krb5_context,
598 &server_in_keytab, &obtained, &error_string);
600 if (ret) {
601 DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
602 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
605 if (keytab->password_based || obtained < CRED_SPECIFIED) {
607 * Use match-by-key in this case (matches
608 * cli_credentials_get_server_gss_creds()
609 * behaviour). No need to free the memory,
610 * this is handled with a talloc destructor.
612 server_in_keytab = NULL;
615 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
616 if (gensec_krb5_state->gssapi
617 && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
618 inbuf.data = (char *)unwrapped_in.data;
619 inbuf.length = unwrapped_in.length;
620 } else {
621 inbuf.data = (char *)in.data;
622 inbuf.length = in.length;
625 ret = smb_krb5_rd_req_decoded(gensec_krb5_state->smb_krb5_context->krb5_context,
626 &gensec_krb5_state->auth_context,
627 &inbuf,
628 keytab->keytab,
629 server_in_keytab,
630 &outbuf,
631 &gensec_krb5_state->ticket,
632 &gensec_krb5_state->keyblock);
634 if (ret) {
635 DBG_WARNING("smb_krb5_rd_req_decoded failed\n");
636 return NT_STATUS_LOGON_FAILURE;
638 unwrapped_out.data = (uint8_t *)outbuf.data;
639 unwrapped_out.length = outbuf.length;
640 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
641 /* wrap that up in a nice GSS-API wrapping */
642 if (gensec_krb5_state->gssapi) {
643 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
644 } else {
645 *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
647 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
648 &outbuf);
649 return NT_STATUS_OK;
652 case GENSEC_KRB5_DONE:
653 default:
654 /* Asking too many times... */
655 return NT_STATUS_INVALID_PARAMETER;
659 struct gensec_krb5_update_state {
660 NTSTATUS status;
661 DATA_BLOB out;
664 static struct tevent_req *gensec_krb5_update_send(TALLOC_CTX *mem_ctx,
665 struct tevent_context *ev,
666 struct gensec_security *gensec_security,
667 const DATA_BLOB in)
669 struct tevent_req *req = NULL;
670 struct gensec_krb5_update_state *state = NULL;
671 NTSTATUS status;
673 req = tevent_req_create(mem_ctx, &state,
674 struct gensec_krb5_update_state);
675 if (req == NULL) {
676 return NULL;
679 status = gensec_krb5_update_internal(gensec_security,
680 state, ev, in,
681 &state->out);
682 state->status = status;
683 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
684 tevent_req_done(req);
685 return tevent_req_post(req, ev);
687 if (tevent_req_nterror(req, status)) {
688 return tevent_req_post(req, ev);
691 tevent_req_done(req);
692 return tevent_req_post(req, ev);
695 static NTSTATUS gensec_krb5_update_recv(struct tevent_req *req,
696 TALLOC_CTX *out_mem_ctx,
697 DATA_BLOB *out)
699 struct gensec_krb5_update_state *state =
700 tevent_req_data(req,
701 struct gensec_krb5_update_state);
702 NTSTATUS status;
704 *out = data_blob_null;
706 if (tevent_req_is_nterror(req, &status)) {
707 tevent_req_received(req);
708 return status;
711 *out = state->out;
712 talloc_steal(out_mem_ctx, state->out.data);
713 status = state->status;
714 tevent_req_received(req);
715 return status;
718 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
719 TALLOC_CTX *mem_ctx,
720 DATA_BLOB *session_key)
722 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
723 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
724 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
725 krb5_error_code err = -1;
726 bool remote = false;
727 bool ok;
729 if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
730 return NT_STATUS_NO_USER_SESSION_KEY;
733 switch (gensec_security->gensec_role) {
734 case GENSEC_CLIENT:
735 remote = false;
736 break;
737 case GENSEC_SERVER:
738 remote = true;
739 break;
742 ok = smb_krb5_get_smb_session_key(mem_ctx,
743 context,
744 auth_context,
745 session_key,
746 remote);
747 if (!ok) {
748 DEBUG(10, ("KRB5 error getting session key %d\n", err));
749 return NT_STATUS_NO_USER_SESSION_KEY;
752 return NT_STATUS_OK;
755 #ifdef SAMBA4_USES_HEIMDAL
756 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
757 TALLOC_CTX *mem_ctx,
758 struct auth_session_info **_session_info)
760 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
761 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
762 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
763 struct auth_session_info *session_info = NULL;
765 krb5_principal client_principal;
766 char *principal_string = NULL;
768 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
769 krb5_data pac_data;
771 krb5_error_code ret;
773 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
774 if (!tmp_ctx) {
775 return NT_STATUS_NO_MEMORY;
778 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
779 if (ret) {
780 DEBUG(5, ("krb5_ticket_get_client failed to get client principal: %s\n",
781 smb_get_krb5_error_message(context,
782 ret, tmp_ctx)));
783 talloc_free(tmp_ctx);
784 return NT_STATUS_NO_MEMORY;
787 ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
788 client_principal, &principal_string);
789 if (ret) {
790 DEBUG(1, ("Unable to parse client principal: %s\n",
791 smb_get_krb5_error_message(context,
792 ret, tmp_ctx)));
793 krb5_free_principal(context, client_principal);
794 talloc_free(tmp_ctx);
795 return NT_STATUS_NO_MEMORY;
798 ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
799 KRB5_AUTHDATA_WIN2K_PAC,
800 &pac_data);
802 if (ret) {
803 /* NO pac */
804 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
805 smb_get_krb5_error_message(context,
806 ret, tmp_ctx)));
807 } else {
808 /* Found pac */
809 pac_blob = data_blob_talloc(tmp_ctx, pac_data.data, pac_data.length);
810 smb_krb5_free_data_contents(context, &pac_data);
811 if (!pac_blob.data) {
812 free(principal_string);
813 krb5_free_principal(context, client_principal);
814 talloc_free(tmp_ctx);
815 return NT_STATUS_NO_MEMORY;
818 /* decode and verify the pac */
819 nt_status = kerberos_decode_pac(gensec_krb5_state,
820 pac_blob,
821 gensec_krb5_state->smb_krb5_context->krb5_context,
822 NULL, gensec_krb5_state->keyblock,
823 client_principal,
824 gensec_krb5_state->ticket->ticket.authtime, NULL);
826 if (!NT_STATUS_IS_OK(nt_status)) {
827 free(principal_string);
828 krb5_free_principal(context, client_principal);
829 talloc_free(tmp_ctx);
830 return nt_status;
833 pac_blob_ptr = &pac_blob;
836 nt_status = gensec_generate_session_info_pac(tmp_ctx,
837 gensec_security,
838 gensec_krb5_state->smb_krb5_context,
839 pac_blob_ptr, principal_string,
840 gensec_get_remote_address(gensec_security),
841 &session_info);
843 free(principal_string);
844 krb5_free_principal(context, client_principal);
846 if (!NT_STATUS_IS_OK(nt_status)) {
847 talloc_free(tmp_ctx);
848 return nt_status;
851 nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);
853 if (!NT_STATUS_IS_OK(nt_status)) {
854 talloc_free(tmp_ctx);
855 return nt_status;
858 *_session_info = talloc_steal(mem_ctx, session_info);
860 talloc_free(tmp_ctx);
861 return NT_STATUS_OK;
863 #else /* MIT KERBEROS */
864 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
865 TALLOC_CTX *mem_ctx,
866 struct auth_session_info **psession_info)
868 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
869 struct gensec_krb5_state *gensec_krb5_state =
870 (struct gensec_krb5_state *)gensec_security->private_data;
871 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
872 struct auth_session_info *session_info = NULL;
874 krb5_principal client_principal;
875 char *principal_string = NULL;
877 krb5_authdata **auth_pac_data = NULL;
878 DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
880 krb5_error_code code;
882 TALLOC_CTX *tmp_ctx;
884 tmp_ctx = talloc_new(mem_ctx);
885 if (tmp_ctx == NULL) {
886 return NT_STATUS_NO_MEMORY;
889 code = krb5_copy_principal(context,
890 gensec_krb5_state->ticket->enc_part2->client,
891 &client_principal);
892 if (code != 0) {
893 DBG_INFO("krb5_copy_principal failed to copy client "
894 "principal: %s\n",
895 smb_get_krb5_error_message(context, code, tmp_ctx));
896 talloc_free(tmp_ctx);
897 return NT_STATUS_NO_MEMORY;
900 code = krb5_unparse_name(context, client_principal, &principal_string);
901 if (code != 0) {
902 DBG_WARNING("Unable to parse client principal: %s\n",
903 smb_get_krb5_error_message(context, code, tmp_ctx));
904 krb5_free_principal(context, client_principal);
905 talloc_free(tmp_ctx);
906 return NT_STATUS_NO_MEMORY;
909 code = krb5_find_authdata(context,
910 gensec_krb5_state->ticket->enc_part2->authorization_data,
911 NULL,
912 KRB5_AUTHDATA_WIN2K_PAC,
913 &auth_pac_data);
914 if (code != 0) {
915 /* NO pac */
916 DBG_INFO("krb5_find_authdata failed to find PAC: %s\n",
917 smb_get_krb5_error_message(context, code, tmp_ctx));
918 } else {
919 krb5_timestamp ticket_authtime =
920 gensec_krb5_state->ticket->enc_part2->times.authtime;
922 /* Found pac */
923 pac_blob = data_blob_talloc(tmp_ctx,
924 auth_pac_data[0]->contents,
925 auth_pac_data[0]->length);
926 krb5_free_authdata(context, auth_pac_data);
927 if (pac_blob.data == NULL) {
928 free(principal_string);
929 krb5_free_principal(context, client_principal);
930 talloc_free(tmp_ctx);
931 return NT_STATUS_NO_MEMORY;
934 /* decode and verify the pac */
935 status = kerberos_decode_pac(gensec_krb5_state,
936 pac_blob,
937 context,
938 NULL,
939 gensec_krb5_state->keyblock,
940 client_principal,
941 ticket_authtime,
942 NULL);
944 if (!NT_STATUS_IS_OK(status)) {
945 free(principal_string);
946 krb5_free_principal(context, client_principal);
947 talloc_free(tmp_ctx);
948 return status;
951 pac_blob_ptr = &pac_blob;
953 krb5_free_principal(context, client_principal);
955 status = gensec_generate_session_info_pac(tmp_ctx,
956 gensec_security,
957 gensec_krb5_state->smb_krb5_context,
958 pac_blob_ptr,
959 principal_string,
960 gensec_get_remote_address(gensec_security),
961 &session_info);
962 SAFE_FREE(principal_string);
963 if (!NT_STATUS_IS_OK(status)) {
964 talloc_free(tmp_ctx);
965 return status;
968 status = gensec_krb5_session_key(gensec_security,
969 session_info,
970 &session_info->session_key);
971 if (!NT_STATUS_IS_OK(status)) {
972 talloc_free(tmp_ctx);
973 return status;
976 *psession_info = talloc_steal(mem_ctx, session_info);
977 talloc_free(tmp_ctx);
979 return NT_STATUS_OK;
981 #endif /* SAMBA4_USES_HEIMDAL */
983 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
984 TALLOC_CTX *mem_ctx,
985 const DATA_BLOB *in,
986 DATA_BLOB *out)
988 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
989 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
990 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
991 krb5_error_code ret;
992 krb5_data input, output;
993 input.length = in->length;
994 input.data = (char *)in->data;
996 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
997 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
998 if (ret) {
999 DEBUG(1, ("krb5_mk_priv failed: %s\n",
1000 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
1001 ret, mem_ctx)));
1002 return NT_STATUS_ACCESS_DENIED;
1004 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1006 smb_krb5_free_data_contents(context, &output);
1007 } else {
1008 return NT_STATUS_ACCESS_DENIED;
1010 return NT_STATUS_OK;
1013 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
1014 TALLOC_CTX *mem_ctx,
1015 const DATA_BLOB *in,
1016 DATA_BLOB *out)
1018 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1019 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
1020 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
1021 krb5_error_code ret;
1022 krb5_data input, output;
1023 krb5_replay_data replay;
1024 input.length = in->length;
1025 input.data = (char *)in->data;
1027 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
1028 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
1029 if (ret) {
1030 DEBUG(1, ("krb5_rd_priv failed: %s\n",
1031 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
1032 ret, mem_ctx)));
1033 return NT_STATUS_ACCESS_DENIED;
1035 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1037 smb_krb5_free_data_contents(context, &output);
1038 } else {
1039 return NT_STATUS_ACCESS_DENIED;
1041 return NT_STATUS_OK;
1044 static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
1045 uint32_t feature)
1047 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1048 if (feature & GENSEC_FEATURE_SESSION_KEY) {
1049 return true;
1051 if (gensec_krb5_state->gssapi) {
1052 return false;
1056 * krb5_mk_priv provides SIGN and SEAL
1058 if (feature & GENSEC_FEATURE_SIGN) {
1059 return true;
1061 if (feature & GENSEC_FEATURE_SEAL) {
1062 return true;
1065 return false;
1068 static const char *gensec_krb5_final_auth_type(struct gensec_security *gensec_security)
1070 return GENSEC_FINAL_AUTH_TYPE_KRB5;
1073 static const char *gensec_krb5_oids[] = {
1074 GENSEC_OID_KERBEROS5,
1075 GENSEC_OID_KERBEROS5_OLD,
1076 NULL
1079 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
1080 .name = "fake_gssapi_krb5",
1081 .auth_type = DCERPC_AUTH_TYPE_KRB5,
1082 .oid = gensec_krb5_oids,
1083 .client_start = gensec_fake_gssapi_krb5_client_start,
1084 .server_start = gensec_fake_gssapi_krb5_server_start,
1085 .update_send = gensec_krb5_update_send,
1086 .update_recv = gensec_krb5_update_recv,
1087 .magic = gensec_magic_check_krb5_oid,
1088 .session_key = gensec_krb5_session_key,
1089 .session_info = gensec_krb5_session_info,
1090 .have_feature = gensec_krb5_have_feature,
1091 .final_auth_type = gensec_krb5_final_auth_type,
1092 .enabled = false,
1093 .kerberos = true,
1094 .priority = GENSEC_KRB5,
1097 static const struct gensec_security_ops gensec_krb5_security_ops = {
1098 .name = "krb5",
1099 .client_start = gensec_krb5_client_start,
1100 .server_start = gensec_krb5_server_start,
1101 .update_send = gensec_krb5_update_send,
1102 .update_recv = gensec_krb5_update_recv,
1103 .session_key = gensec_krb5_session_key,
1104 .session_info = gensec_krb5_session_info,
1105 .have_feature = gensec_krb5_have_feature,
1106 .wrap = gensec_krb5_wrap,
1107 .unwrap = gensec_krb5_unwrap,
1108 .final_auth_type = gensec_krb5_final_auth_type,
1109 .enabled = true,
1110 .kerberos = true,
1111 .priority = GENSEC_KRB5
1114 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *ctx)
1116 NTSTATUS ret;
1118 ret = gensec_register(ctx, &gensec_krb5_security_ops);
1119 if (!NT_STATUS_IS_OK(ret)) {
1120 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1121 gensec_krb5_security_ops.name));
1122 return ret;
1125 ret = gensec_register(ctx, &gensec_fake_gssapi_krb5_security_ops);
1126 if (!NT_STATUS_IS_OK(ret)) {
1127 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1128 gensec_fake_gssapi_krb5_security_ops.name));
1129 return ret;
1132 return ret;