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"
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 */
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
);
81 static NTSTATUS
gensec_krb5_start(struct gensec_security
*gensec_security
, bool gssapi
)
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
);
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
);
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
);
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
);
132 ssize_t sockaddr_ret
;
133 struct samba_sockaddr addr
;
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
);
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
);
154 ssize_t sockaddr_ret
;
155 struct samba_sockaddr addr
;
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
);
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
);
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
;
189 static NTSTATUS
gensec_krb5_common_server_start(struct gensec_security
*gensec_security
, bool gssapi
)
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
)) {
199 gensec_krb5_state
= (struct gensec_krb5_state
*)gensec_security
->private_data
;
200 gensec_krb5_state
->state_position
= GENSEC_KRB5_SERVER_START
;
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
;
220 hostname
= gensec_get_target_hostname(gensec_security
);
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
)) {
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
;
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
;
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
;
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
;
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
);
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
:
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 */
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
);
307 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
308 return NT_STATUS_NO_MEMORY
;
312 krb5_principal target_principal
;
313 ret
= krb5_parse_name(gensec_krb5_state
->smb_krb5_context
->krb5_context
, principal
,
316 krb5_creds this_cred
;
319 ZERO_STRUCT(this_cred
);
320 ret
= krb5_cc_get_principal(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
321 ccache_container
->ccache
,
324 krb5_free_principal(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
326 return NT_STATUS_UNSUCCESSFUL
;
329 ret
= krb5_copy_principal(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
332 krb5_free_principal(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
335 krb5_free_cred_contents(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
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
,
346 krb5_free_cred_contents(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
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
,
357 &gensec_krb5_state
->enc_ticket
);
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
);
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
:
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
)));
393 /* just don't print a message for these really ordinary messages */
394 case KRB5_FCC_NOFILE
:
395 case KRB5_CC_NOTFOUND
:
398 return NT_STATUS_UNSUCCESSFUL
;
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
) {
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
)) {
449 DEBUG(1, ("Failed to build krb5 wrapper at offset %d\n",
450 (int)asn1_current_ofs(data
)));
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])
461 struct asn1_data
*data
= asn1_init(mem_ctx
, ASN1_MAX_TREE_DEPTH
);
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
);
477 if (!asn1_read(data
, tok_id
, 2)) goto err
;
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
);
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;
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
)) {
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
);
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
;
525 gensec_krb5_state
->state_position
= GENSEC_KRB5_DONE
;
526 nt_status
= NT_STATUS_OK
;
531 case GENSEC_KRB5_CLIENT_MUTUAL_AUTH
:
533 DATA_BLOB unwrapped_in
;
535 krb5_ap_rep_enc_part
*repl
= NULL
;
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
;
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
,
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
;
560 *out
= data_blob(NULL
, 0);
561 nt_status
= NT_STATUS_OK
;
562 gensec_krb5_state
->state_position
= GENSEC_KRB5_DONE
;
565 krb5_free_ap_rep_enc_part(gensec_krb5_state
->smb_krb5_context
->krb5_context
, repl
);
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
;
576 struct keytab_container
*keytab
;
577 krb5_principal server_in_keytab
;
578 const char *error_string
;
579 enum credentials_obtained obtained
;
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
);
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
);
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
;
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
,
631 &gensec_krb5_state
->ticket
,
632 &gensec_krb5_state
->keyblock
);
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
);
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
,
652 case GENSEC_KRB5_DONE
:
654 /* Asking too many times... */
655 return NT_STATUS_INVALID_PARAMETER
;
659 struct gensec_krb5_update_state
{
664 static struct tevent_req
*gensec_krb5_update_send(TALLOC_CTX
*mem_ctx
,
665 struct tevent_context
*ev
,
666 struct gensec_security
*gensec_security
,
669 struct tevent_req
*req
= NULL
;
670 struct gensec_krb5_update_state
*state
= NULL
;
673 req
= tevent_req_create(mem_ctx
, &state
,
674 struct gensec_krb5_update_state
);
679 status
= gensec_krb5_update_internal(gensec_security
,
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
,
699 struct gensec_krb5_update_state
*state
=
701 struct gensec_krb5_update_state
);
704 *out
= data_blob_null
;
706 if (tevent_req_is_nterror(req
, &status
)) {
707 tevent_req_received(req
);
712 talloc_steal(out_mem_ctx
, state
->out
.data
);
713 status
= state
->status
;
714 tevent_req_received(req
);
718 static NTSTATUS
gensec_krb5_session_key(struct gensec_security
*gensec_security
,
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;
729 if (gensec_krb5_state
->state_position
!= GENSEC_KRB5_DONE
) {
730 return NT_STATUS_NO_USER_SESSION_KEY
;
733 switch (gensec_security
->gensec_role
) {
742 ok
= smb_krb5_get_smb_session_key(mem_ctx
,
748 DEBUG(10, ("KRB5 error getting session key %d\n", err
));
749 return NT_STATUS_NO_USER_SESSION_KEY
;
755 #ifdef SAMBA4_USES_HEIMDAL
756 static NTSTATUS
gensec_krb5_session_info(struct gensec_security
*gensec_security
,
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
;
773 TALLOC_CTX
*tmp_ctx
= talloc_new(mem_ctx
);
775 return NT_STATUS_NO_MEMORY
;
778 ret
= krb5_ticket_get_client(context
, gensec_krb5_state
->ticket
, &client_principal
);
780 DEBUG(5, ("krb5_ticket_get_client failed to get client principal: %s\n",
781 smb_get_krb5_error_message(context
,
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
);
790 DEBUG(1, ("Unable to parse client principal: %s\n",
791 smb_get_krb5_error_message(context
,
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
,
804 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
805 smb_get_krb5_error_message(context
,
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
,
821 gensec_krb5_state
->smb_krb5_context
->krb5_context
,
822 NULL
, gensec_krb5_state
->keyblock
,
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
);
833 pac_blob_ptr
= &pac_blob
;
836 nt_status
= gensec_generate_session_info_pac(tmp_ctx
,
838 gensec_krb5_state
->smb_krb5_context
,
839 pac_blob_ptr
, principal_string
,
840 gensec_get_remote_address(gensec_security
),
843 free(principal_string
);
844 krb5_free_principal(context
, client_principal
);
846 if (!NT_STATUS_IS_OK(nt_status
)) {
847 talloc_free(tmp_ctx
);
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
);
858 *_session_info
= talloc_steal(mem_ctx
, session_info
);
860 talloc_free(tmp_ctx
);
863 #else /* MIT KERBEROS */
864 static NTSTATUS
gensec_krb5_session_info(struct gensec_security
*gensec_security
,
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
;
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
,
893 DBG_INFO("krb5_copy_principal failed to copy client "
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
);
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
,
912 KRB5_AUTHDATA_WIN2K_PAC
,
916 DBG_INFO("krb5_find_authdata failed to find PAC: %s\n",
917 smb_get_krb5_error_message(context
, code
, tmp_ctx
));
919 krb5_timestamp ticket_authtime
=
920 gensec_krb5_state
->ticket
->enc_part2
->times
.authtime
;
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
,
939 gensec_krb5_state
->keyblock
,
944 if (!NT_STATUS_IS_OK(status
)) {
945 free(principal_string
);
946 krb5_free_principal(context
, client_principal
);
947 talloc_free(tmp_ctx
);
951 pac_blob_ptr
= &pac_blob
;
953 krb5_free_principal(context
, client_principal
);
955 status
= gensec_generate_session_info_pac(tmp_ctx
,
957 gensec_krb5_state
->smb_krb5_context
,
960 gensec_get_remote_address(gensec_security
),
962 SAFE_FREE(principal_string
);
963 if (!NT_STATUS_IS_OK(status
)) {
964 talloc_free(tmp_ctx
);
968 status
= gensec_krb5_session_key(gensec_security
,
970 &session_info
->session_key
);
971 if (!NT_STATUS_IS_OK(status
)) {
972 talloc_free(tmp_ctx
);
976 *psession_info
= talloc_steal(mem_ctx
, session_info
);
977 talloc_free(tmp_ctx
);
981 #endif /* SAMBA4_USES_HEIMDAL */
983 static NTSTATUS
gensec_krb5_wrap(struct gensec_security
*gensec_security
,
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
;
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
);
999 DEBUG(1, ("krb5_mk_priv failed: %s\n",
1000 smb_get_krb5_error_message(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
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
);
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
,
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
);
1030 DEBUG(1, ("krb5_rd_priv failed: %s\n",
1031 smb_get_krb5_error_message(gensec_krb5_state
->smb_krb5_context
->krb5_context
,
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
);
1039 return NT_STATUS_ACCESS_DENIED
;
1041 return NT_STATUS_OK
;
1044 static bool gensec_krb5_have_feature(struct gensec_security
*gensec_security
,
1047 struct gensec_krb5_state
*gensec_krb5_state
= (struct gensec_krb5_state
*)gensec_security
->private_data
;
1048 if (feature
& GENSEC_FEATURE_SESSION_KEY
) {
1051 if (gensec_krb5_state
->gssapi
) {
1056 * krb5_mk_priv provides SIGN and SEAL
1058 if (feature
& GENSEC_FEATURE_SIGN
) {
1061 if (feature
& GENSEC_FEATURE_SEAL
) {
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
,
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
,
1094 .priority
= GENSEC_KRB5
,
1097 static const struct gensec_security_ops gensec_krb5_security_ops
= {
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
,
1111 .priority
= GENSEC_KRB5
1114 _PUBLIC_ NTSTATUS
gensec_krb5_init(TALLOC_CTX
*ctx
)
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
));
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
));