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/>.
27 #include "system/kerberos.h"
28 #include "auth/kerberos/kerberos.h"
29 #include "auth/auth.h"
30 #include "lib/socket/socket.h"
31 #include "lib/tsocket/tsocket.h"
32 #include "librpc/rpc/dcerpc.h"
33 #include "auth/credentials/credentials.h"
34 #include "auth/credentials/credentials_krb5.h"
35 #include "auth/kerberos/kerberos_credentials.h"
36 #include "auth/gensec/gensec.h"
37 #include "auth/gensec/gensec_proto.h"
38 #include "auth/gensec/gensec_toplevel_proto.h"
39 #include "param/param.h"
40 #include "auth/auth_sam_reply.h"
41 #include "lib/util/util_net.h"
43 _PUBLIC_ NTSTATUS gensec_krb5_init(void);
45 enum GENSEC_KRB5_STATE {
46 GENSEC_KRB5_SERVER_START,
47 GENSEC_KRB5_CLIENT_START,
48 GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
52 struct gensec_krb5_state {
53 enum GENSEC_KRB5_STATE state_position;
54 struct smb_krb5_context *smb_krb5_context;
55 krb5_auth_context auth_context;
57 krb5_keyblock *keyblock;
60 krb5_flags ap_req_options;
63 static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
65 if (!gensec_krb5_state->smb_krb5_context) {
66 /* We can't clean anything else up unless we started up this far */
69 if (gensec_krb5_state->enc_ticket.length) {
70 kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
71 &gensec_krb5_state->enc_ticket);
74 if (gensec_krb5_state->ticket) {
75 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
76 gensec_krb5_state->ticket);
79 /* ccache freed in a child destructor */
81 krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
82 gensec_krb5_state->keyblock);
84 if (gensec_krb5_state->auth_context) {
85 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
86 gensec_krb5_state->auth_context);
92 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
95 struct gensec_krb5_state *gensec_krb5_state;
96 struct cli_credentials *creds;
97 const struct tsocket_address *tlocal_addr, *tremote_addr;
98 krb5_address my_krb5_addr, peer_krb5_addr;
100 creds = gensec_get_credentials(gensec_security);
102 return NT_STATUS_INVALID_PARAMETER;
105 gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
106 if (!gensec_krb5_state) {
107 return NT_STATUS_NO_MEMORY;
110 gensec_security->private_data = gensec_krb5_state;
111 gensec_krb5_state->smb_krb5_context = NULL;
112 gensec_krb5_state->auth_context = NULL;
113 gensec_krb5_state->ticket = NULL;
114 ZERO_STRUCT(gensec_krb5_state->enc_ticket);
115 gensec_krb5_state->keyblock = NULL;
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);
149 struct sockaddr_storage ss;
151 socklen = tsocket_address_bsd_sockaddr(tlocal_addr,
152 (struct sockaddr *) &ss,
153 sizeof(struct sockaddr_storage));
155 talloc_free(gensec_krb5_state);
156 return NT_STATUS_INTERNAL_ERROR;
158 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
159 (const struct sockaddr *) &ss, &my_krb5_addr);
161 DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
162 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
163 ret, gensec_krb5_state)));
164 talloc_free(gensec_krb5_state);
165 return NT_STATUS_INTERNAL_ERROR;
169 tremote_addr = gensec_get_remote_address(gensec_security);
172 struct sockaddr_storage ss;
174 socklen = tsocket_address_bsd_sockaddr(tremote_addr,
175 (struct sockaddr *) &ss,
176 sizeof(struct sockaddr_storage));
178 talloc_free(gensec_krb5_state);
179 return NT_STATUS_INTERNAL_ERROR;
181 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
182 (const struct sockaddr *) &ss, &peer_krb5_addr);
184 DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
185 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
186 ret, gensec_krb5_state)));
187 talloc_free(gensec_krb5_state);
188 return NT_STATUS_INTERNAL_ERROR;
192 ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
193 gensec_krb5_state->auth_context,
194 tlocal_addr ? &my_krb5_addr : NULL,
195 tremote_addr ? &peer_krb5_addr : NULL);
197 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
198 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
199 ret, gensec_krb5_state)));
200 talloc_free(gensec_krb5_state);
201 return NT_STATUS_INTERNAL_ERROR;
207 static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi)
210 struct gensec_krb5_state *gensec_krb5_state;
212 nt_status = gensec_krb5_start(gensec_security, gssapi);
213 if (!NT_STATUS_IS_OK(nt_status)) {
217 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
218 gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
223 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
225 return gensec_krb5_common_server_start(gensec_security, false);
228 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
230 return gensec_krb5_common_server_start(gensec_security, true);
233 static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
235 const char *hostname;
236 struct gensec_krb5_state *gensec_krb5_state;
238 hostname = gensec_get_target_hostname(gensec_security);
240 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
241 return NT_STATUS_INVALID_PARAMETER;
243 if (is_ipaddress(hostname)) {
244 DEBUG(2, ("Cannot do krb5 to an IP address"));
245 return NT_STATUS_INVALID_PARAMETER;
247 if (strcmp(hostname, "localhost") == 0) {
248 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
249 return NT_STATUS_INVALID_PARAMETER;
252 nt_status = gensec_krb5_start(gensec_security, gssapi);
253 if (!NT_STATUS_IS_OK(nt_status)) {
257 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
258 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
259 gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;
261 if (gensec_krb5_state->gssapi) {
262 /* The Fake GSSAPI modal emulates Samba3, which does not do mutual authentication */
263 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
264 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
267 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
268 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
269 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
275 static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security, bool gssapi)
277 struct gensec_krb5_state *gensec_krb5_state;
279 struct ccache_container *ccache_container;
280 const char *error_string;
281 const char *principal;
282 const char *hostname;
284 struct tevent_context *previous_ev;
286 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
288 principal = gensec_get_target_principal(gensec_security);
289 hostname = gensec_get_target_hostname(gensec_security);
291 ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security),
292 gensec_security->event_ctx,
293 gensec_security->settings->lp_ctx, &ccache_container, &error_string);
297 case KRB5KDC_ERR_PREAUTH_FAILED:
298 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
299 return NT_STATUS_LOGON_FAILURE;
300 case KRB5_KDC_UNREACH:
301 DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
302 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
303 case KRB5_CC_NOTFOUND:
305 DEBUG(3, ("Error preparing credentials 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 */
308 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
309 return NT_STATUS_UNSUCCESSFUL;
313 /* Do this every time, in case we have weird recursive issues here */
314 ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, gensec_security->event_ctx, &previous_ev);
316 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
317 return NT_STATUS_NO_MEMORY;
320 krb5_principal target_principal;
321 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
324 ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
325 &gensec_krb5_state->auth_context,
326 gensec_krb5_state->ap_req_options,
328 &in_data, ccache_container->ccache,
329 &gensec_krb5_state->enc_ticket);
330 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
334 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
335 &gensec_krb5_state->auth_context,
336 gensec_krb5_state->ap_req_options,
337 gensec_get_target_service(gensec_security),
339 &in_data, ccache_container->ccache,
340 &gensec_krb5_state->enc_ticket);
343 smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, gensec_security->event_ctx);
348 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
349 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
350 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
351 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
352 case KRB5_KDC_UNREACH:
353 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
354 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
355 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
356 case KRB5KDC_ERR_PREAUTH_FAILED:
357 case KRB5KRB_AP_ERR_TKT_EXPIRED:
359 /* Too much clock skew - we will need to kinit to re-skew the clock */
360 case KRB5KRB_AP_ERR_SKEW:
361 case KRB5_KDCREP_SKEW:
363 DEBUG(3, ("kerberos (mk_req) failed: %s\n",
364 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
368 /* just don't print a message for these really ordinary messages */
369 case KRB5_FCC_NOFILE:
370 case KRB5_CC_NOTFOUND:
373 return NT_STATUS_UNSUCCESSFUL;
377 DEBUG(0, ("kerberos: %s\n",
378 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
379 return NT_STATUS_UNSUCCESSFUL;
383 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
385 return gensec_krb5_common_client_start(gensec_security, false);
388 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
390 return gensec_krb5_common_client_start(gensec_security, true);
394 * Check if the packet is one for this mechansim
396 * @param gensec_security GENSEC state
397 * @param in The request, as a DATA_BLOB
398 * @return Error, INVALID_PARAMETER if it's not a packet for us
399 * or NT_STATUS_OK if the packet is ok.
402 static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security,
405 if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
408 return NT_STATUS_INVALID_PARAMETER;
414 * Next state function for the Krb5 GENSEC mechanism
416 * @param gensec_krb5_state KRB5 State
417 * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
418 * @param in The request, as a DATA_BLOB
419 * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
420 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
421 * or NT_STATUS_OK if the user is authenticated.
424 static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
425 TALLOC_CTX *out_mem_ctx,
426 const DATA_BLOB in, DATA_BLOB *out)
428 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
429 krb5_error_code ret = 0;
432 switch (gensec_krb5_state->state_position) {
433 case GENSEC_KRB5_CLIENT_START:
435 DATA_BLOB unwrapped_out;
437 nt_status = gensec_krb5_common_client_creds(gensec_security, gensec_krb5_state->gssapi);
438 if (!NT_STATUS_IS_OK(nt_status)) {
442 if (gensec_krb5_state->gssapi) {
443 unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
445 /* wrap that up in a nice GSS-API wrapping */
446 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
448 *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
450 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
451 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
452 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
454 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
455 nt_status = NT_STATUS_OK;
460 case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
462 DATA_BLOB unwrapped_in;
464 krb5_ap_rep_enc_part *repl = NULL;
467 if (gensec_krb5_state->gssapi) {
468 if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
469 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
470 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
471 return NT_STATUS_INVALID_PARAMETER;
476 /* TODO: check the tok_id */
478 inbuf.data = unwrapped_in.data;
479 inbuf.length = unwrapped_in.length;
480 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
481 gensec_krb5_state->auth_context,
484 DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
485 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
486 dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
487 nt_status = NT_STATUS_ACCESS_DENIED;
489 *out = data_blob(NULL, 0);
490 nt_status = NT_STATUS_OK;
491 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
494 krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
499 case GENSEC_KRB5_SERVER_START:
501 DATA_BLOB unwrapped_in;
502 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
503 krb5_data inbuf, outbuf;
505 struct keytab_container *keytab;
506 krb5_principal server_in_keytab;
507 const char *error_string;
508 enum credentials_obtained obtained;
511 return NT_STATUS_INVALID_PARAMETER;
514 /* Grab the keytab, however generated */
515 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security),
516 gensec_security->settings->lp_ctx, &keytab);
518 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
521 /* This ensures we lookup the correct entry in that keytab */
522 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
523 gensec_krb5_state->smb_krb5_context,
524 &server_in_keytab, &obtained, &error_string);
527 DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
528 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
531 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
532 if (gensec_krb5_state->gssapi
533 && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
534 inbuf.data = unwrapped_in.data;
535 inbuf.length = unwrapped_in.length;
537 inbuf.data = in.data;
538 inbuf.length = in.length;
541 ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
542 &gensec_krb5_state->auth_context,
543 &inbuf, keytab->keytab, server_in_keytab,
545 &gensec_krb5_state->ticket,
546 &gensec_krb5_state->keyblock);
549 return NT_STATUS_LOGON_FAILURE;
551 unwrapped_out.data = (uint8_t *)outbuf.data;
552 unwrapped_out.length = outbuf.length;
553 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
554 /* wrap that up in a nice GSS-API wrapping */
555 if (gensec_krb5_state->gssapi) {
556 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
558 *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
560 krb5_data_free(&outbuf);
564 case GENSEC_KRB5_DONE:
566 /* Asking too many times... */
567 return NT_STATUS_INVALID_PARAMETER;
571 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
573 DATA_BLOB *session_key)
575 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
576 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
577 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
579 krb5_error_code err = -1;
581 if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
582 return NT_STATUS_NO_USER_SESSION_KEY;
585 switch (gensec_security->gensec_role) {
587 err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
590 err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
593 if (err == 0 && skey != NULL) {
594 DEBUG(10, ("Got KRB5 session key of length %d\n",
595 (int)KRB5_KEY_LENGTH(skey)));
596 *session_key = data_blob_talloc(mem_ctx,
597 KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
598 dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
600 krb5_free_keyblock(context, skey);
603 DEBUG(10, ("KRB5 error getting session key %d\n", err));
604 return NT_STATUS_NO_USER_SESSION_KEY;
608 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
609 TALLOC_CTX *mem_ctx_out,
610 struct auth_session_info **_session_info)
612 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
613 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
614 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
615 struct auth_user_info_dc *user_info_dc = NULL;
616 struct auth_session_info *session_info = NULL;
617 struct PAC_LOGON_INFO *logon_info;
619 krb5_principal client_principal;
620 char *principal_string;
627 TALLOC_CTX *mem_ctx = talloc_new(mem_ctx_out);
629 return NT_STATUS_NO_MEMORY;
632 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
634 DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
635 smb_get_krb5_error_message(context,
637 talloc_free(mem_ctx);
638 return NT_STATUS_NO_MEMORY;
641 ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
642 client_principal, &principal_string);
644 DEBUG(1, ("Unable to parse client principal: %s\n",
645 smb_get_krb5_error_message(context,
647 krb5_free_principal(context, client_principal);
648 talloc_free(mem_ctx);
649 return NT_STATUS_NO_MEMORY;
652 ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
653 KRB5_AUTHDATA_WIN2K_PAC,
656 if (ret && gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
657 DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
659 smb_get_krb5_error_message(context,
661 free(principal_string);
662 krb5_free_principal(context, client_principal);
663 talloc_free(mem_ctx);
664 return NT_STATUS_ACCESS_DENIED;
667 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
668 smb_get_krb5_error_message(context,
670 if (gensec_security->auth_context &&
671 !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
672 DEBUG(1, ("Unable to find PAC for %s, resorting to local user lookup: %s",
673 principal_string, smb_get_krb5_error_message(context,
675 nt_status = gensec_security->auth_context->get_user_info_dc_principal(mem_ctx,
676 gensec_security->auth_context,
678 NULL, &user_info_dc);
679 if (!NT_STATUS_IS_OK(nt_status)) {
680 free(principal_string);
681 krb5_free_principal(context, client_principal);
682 talloc_free(mem_ctx);
686 DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n",
688 free(principal_string);
689 krb5_free_principal(context, client_principal);
690 talloc_free(mem_ctx);
691 return NT_STATUS_ACCESS_DENIED;
695 union netr_Validation validation;
697 pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
699 free(principal_string);
700 krb5_free_principal(context, client_principal);
701 talloc_free(mem_ctx);
702 return NT_STATUS_NO_MEMORY;
705 /* decode and verify the pac */
706 nt_status = kerberos_pac_logon_info(gensec_krb5_state,
708 gensec_krb5_state->smb_krb5_context->krb5_context,
709 NULL, gensec_krb5_state->keyblock,
711 gensec_krb5_state->ticket->ticket.authtime, &logon_info);
713 if (!NT_STATUS_IS_OK(nt_status)) {
714 free(principal_string);
715 krb5_free_principal(context, client_principal);
716 talloc_free(mem_ctx);
720 validation.sam3 = &logon_info->info3;
721 nt_status = make_user_info_dc_netlogon_validation(mem_ctx,
724 true, /* This user was authenticated */
726 if (!NT_STATUS_IS_OK(nt_status)) {
727 free(principal_string);
728 krb5_free_principal(context, client_principal);
729 talloc_free(mem_ctx);
734 free(principal_string);
735 krb5_free_principal(context, client_principal);
737 /* references the user_info_dc into the session_info */
738 nt_status = gensec_generate_session_info(mem_ctx, gensec_security, user_info_dc, &session_info);
740 if (!NT_STATUS_IS_OK(nt_status)) {
741 talloc_free(mem_ctx);
745 nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);
747 if (!NT_STATUS_IS_OK(nt_status)) {
748 talloc_free(mem_ctx);
752 *_session_info = talloc_steal(mem_ctx_out, session_info);
754 talloc_free(mem_ctx);
758 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
763 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
764 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
765 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
767 krb5_data input, output;
768 input.length = in->length;
769 input.data = in->data;
771 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
772 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
774 DEBUG(1, ("krb5_mk_priv failed: %s\n",
775 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
777 return NT_STATUS_ACCESS_DENIED;
779 *out = data_blob_talloc(mem_ctx, output.data, output.length);
781 krb5_data_free(&output);
783 return NT_STATUS_ACCESS_DENIED;
788 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
793 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
794 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
795 krb5_auth_context auth_context = gensec_krb5_state->auth_context;
797 krb5_data input, output;
798 krb5_replay_data replay;
799 input.length = in->length;
800 input.data = in->data;
802 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
803 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
805 DEBUG(1, ("krb5_rd_priv failed: %s\n",
806 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
808 return NT_STATUS_ACCESS_DENIED;
810 *out = data_blob_talloc(mem_ctx, output.data, output.length);
812 krb5_data_free(&output);
814 return NT_STATUS_ACCESS_DENIED;
819 static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
822 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
823 if (feature & GENSEC_FEATURE_SESSION_KEY) {
826 if (!gensec_krb5_state->gssapi &&
827 (feature & GENSEC_FEATURE_SEAL)) {
834 static const char *gensec_krb5_oids[] = {
835 GENSEC_OID_KERBEROS5,
836 GENSEC_OID_KERBEROS5_OLD,
840 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
841 .name = "fake_gssapi_krb5",
842 .auth_type = DCERPC_AUTH_TYPE_KRB5,
843 .oid = gensec_krb5_oids,
844 .client_start = gensec_fake_gssapi_krb5_client_start,
845 .server_start = gensec_fake_gssapi_krb5_server_start,
846 .update = gensec_krb5_update,
847 .magic = gensec_fake_gssapi_krb5_magic,
848 .session_key = gensec_krb5_session_key,
849 .session_info = gensec_krb5_session_info,
850 .have_feature = gensec_krb5_have_feature,
853 .priority = GENSEC_KRB5
856 static const struct gensec_security_ops gensec_krb5_security_ops = {
858 .client_start = gensec_krb5_client_start,
859 .server_start = gensec_krb5_server_start,
860 .update = gensec_krb5_update,
861 .session_key = gensec_krb5_session_key,
862 .session_info = gensec_krb5_session_info,
863 .have_feature = gensec_krb5_have_feature,
864 .wrap = gensec_krb5_wrap,
865 .unwrap = gensec_krb5_unwrap,
868 .priority = GENSEC_KRB5
871 _PUBLIC_ NTSTATUS gensec_krb5_init(void)
875 ret = gensec_register(&gensec_krb5_security_ops);
876 if (!NT_STATUS_IS_OK(ret)) {
877 DEBUG(0,("Failed to register '%s' gensec backend!\n",
878 gensec_krb5_security_ops.name));
882 ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
883 if (!NT_STATUS_IS_OK(ret)) {
884 DEBUG(0,("Failed to register '%s' gensec backend!\n",
885 gensec_krb5_security_ops.name));