2 Unix SMB/CIFS implementation.
4 PAC Glue between Samba and the KDC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7 Copyright (C) Simo Sorce <idra@samba.org> 2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "kdc/kdc-glue.h"
26 #include "kdc/db-glue.h"
27 #include "kdc/pac-glue.h"
30 #include "librpc/gen_ndr/auth.h"
31 #include <krb5_locl.h>
32 #include "lib/replace/system/filesys.h"
35 #define DBGC_CLASS DBGC_KERBEROS
37 static int samba_wdc_pac_options(astgs_request_t r
, PAC_OPTIONS_FLAGS
*flags
)
39 const KDC_REQ
*req
= kdc_request_get_req(r
);
40 const PA_DATA
*padata_pac_options
= NULL
;
44 if (req
->padata
!= NULL
) {
47 padata_pac_options
= krb5_find_padata(req
->padata
->val
,
49 KRB5_PADATA_PAC_OPTIONS
,
53 if (padata_pac_options
!= NULL
) {
54 PA_PAC_OPTIONS pa_pac_options
= {};
57 ret
= decode_PA_PAC_OPTIONS(padata_pac_options
->padata_value
.data
,
58 padata_pac_options
->padata_value
.length
,
59 &pa_pac_options
, NULL
);
63 *flags
= pa_pac_options
.flags
;
69 static bool samba_wdc_is_s4u2self_req(astgs_request_t r
)
71 krb5_kdc_configuration
*config
= kdc_request_get_config((kdc_request_t
)r
);
72 const KDC_REQ
*req
= kdc_request_get_req(r
);
73 const PA_DATA
*pa_for_user
= NULL
;
75 if (req
->msg_type
!= krb_tgs_req
) {
79 if (config
->enable_fast
&& req
->padata
!= NULL
) {
80 const PA_DATA
*pa_fx_fast
= NULL
;
83 pa_fx_fast
= krb5_find_padata(req
->padata
->val
,
87 if (pa_fx_fast
!= NULL
) {
89 * We're in the outer request
90 * with KRB5_PADATA_FX_FAST
91 * if fast is enabled we'll
92 * process the s4u2self
100 if (req
->padata
!= NULL
) {
103 pa_for_user
= krb5_find_padata(req
->padata
->val
,
105 KRB5_PADATA_FOR_USER
,
109 if (pa_for_user
!= NULL
) {
117 * Given the right private pointer from hdb_samba4,
118 * get a PAC from the attached ldb messages.
120 * For PKINIT we also get pk_reply_key and can add PAC_CREDENTIAL_INFO.
122 static krb5_error_code
samba_wdc_get_pac(void *priv
,
126 const krb5_keyblock
*pk_reply_key
,
127 uint64_t pac_attributes
,
130 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
132 DATA_BLOB
*logon_blob
= NULL
;
133 DATA_BLOB
*cred_ndr
= NULL
;
134 DATA_BLOB
**cred_ndr_ptr
= NULL
;
135 DATA_BLOB _cred_blob
= data_blob_null
;
136 DATA_BLOB
*cred_blob
= NULL
;
137 DATA_BLOB
*upn_blob
= NULL
;
138 DATA_BLOB
*pac_attrs_blob
= NULL
;
139 DATA_BLOB
*requester_sid_blob
= NULL
;
140 DATA_BLOB
*claims_blob
= NULL
;
143 struct samba_kdc_entry
*skdc_entry
=
144 talloc_get_type_abort(client
->context
,
145 struct samba_kdc_entry
);
146 const struct samba_kdc_entry
*server_entry
=
147 talloc_get_type_abort(server
->context
,
148 struct samba_kdc_entry
);
149 bool is_krbtgt
= krb5_principal_is_krbtgt(context
, server
->principal
);
150 /* Only include resource groups in a service ticket. */
151 enum auth_group_inclusion group_inclusion
;
152 bool is_s4u2self
= samba_wdc_is_s4u2self_req(r
);
153 enum samba_asserted_identity asserted_identity
=
155 SAMBA_ASSERTED_IDENTITY_SERVICE
:
156 SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY
;
157 PAC_OPTIONS_FLAGS pac_options
= {};
159 /* Only include resource groups in a service ticket. */
161 group_inclusion
= AUTH_EXCLUDE_RESOURCE_GROUPS
;
162 } else if (server_entry
->supported_enctypes
& KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED
) {
163 group_inclusion
= AUTH_INCLUDE_RESOURCE_GROUPS
;
165 group_inclusion
= AUTH_INCLUDE_RESOURCE_GROUPS_COMPRESSED
;
168 ret
= samba_wdc_pac_options(r
, &pac_options
);
173 mem_ctx
= talloc_named(client
->context
, 0, "samba_get_pac context");
178 if (pk_reply_key
!= NULL
) {
179 cred_ndr_ptr
= &cred_ndr
;
182 nt_status
= samba_kdc_get_pac_blobs(mem_ctx
, skdc_entry
,
188 is_krbtgt
? &pac_attrs_blob
: NULL
,
190 is_krbtgt
? &requester_sid_blob
: NULL
,
192 &claims_blob
: NULL
);
193 if (!NT_STATUS_IS_OK(nt_status
)) {
194 talloc_free(mem_ctx
);
198 if (pk_reply_key
!= NULL
&& cred_ndr
!= NULL
) {
199 ret
= samba_kdc_encrypt_pac_credentials(context
,
205 talloc_free(mem_ctx
);
208 cred_blob
= &_cred_blob
;
211 ret
= krb5_pac_init(context
, pac
);
213 talloc_free(mem_ctx
);
217 ret
= samba_make_krb5_pac(context
, logon_blob
, cred_blob
,
218 upn_blob
, pac_attrs_blob
,
219 requester_sid_blob
, NULL
,
223 talloc_free(mem_ctx
);
227 static krb5_error_code
samba_wdc_reget_pac2(astgs_request_t r
,
228 const krb5_principal delegated_proxy_principal
,
233 krb5_cksumtype ctype
,
234 const hdb_entry
*device
,
235 krb5_const_pac
*device_pac
)
237 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
238 struct samba_kdc_entry
*client_skdc_entry
= NULL
;
239 struct samba_kdc_entry
*server_skdc_entry
=
240 talloc_get_type_abort(server
->context
, struct samba_kdc_entry
);
241 struct samba_kdc_entry
*krbtgt_skdc_entry
=
242 talloc_get_type_abort(krbtgt
->context
, struct samba_kdc_entry
);
243 TALLOC_CTX
*mem_ctx
= NULL
;
244 krb5_pac new_pac
= NULL
;
246 bool is_s4u2self
= samba_wdc_is_s4u2self_req(r
);
247 bool is_in_db
= false;
248 bool is_untrusted
= false;
250 PAC_OPTIONS_FLAGS pac_options
= {};
252 ret
= samba_wdc_pac_options(r
, &pac_options
);
257 mem_ctx
= talloc_named(NULL
, 0, "samba_kdc_reget_pac2 context");
258 if (mem_ctx
== NULL
) {
262 if (client
!= NULL
) {
263 client_skdc_entry
= talloc_get_type_abort(client
->context
,
264 struct samba_kdc_entry
);
267 if (device
!= NULL
) {
268 struct samba_kdc_entry
*device_skdc_entry
= NULL
;
270 device_skdc_entry
= talloc_get_type_abort(device
->context
,
271 struct samba_kdc_entry
);
274 * Check the objectSID of the device and pac data are the same.
275 * Does a parse and SID check, but no crypto.
277 ret
= samba_kdc_validate_pac_blob(context
,
281 talloc_free(mem_ctx
);
287 * If the krbtgt was generated by an RODC, and we are not that
288 * RODC, then we need to regenerate the PAC - we can't trust
289 * it, and confirm that the RODC was permitted to print this ticket
291 * Becasue of the samba_kdc_validate_pac_blob() step we can be
292 * sure that the record in 'client' matches the SID in the
295 ret
= samba_krbtgt_is_in_db(krbtgt_skdc_entry
, &is_in_db
, &is_untrusted
);
301 flags
|= SAMBA_KDC_FLAG_PROTOCOL_TRANSITION
;
304 if (delegated_proxy_principal
!= NULL
) {
310 * The RODC-issued PAC was signed by a KDC entry that we
311 * don't have a key for. The server signature is not
312 * trustworthy, since it could have been created by the
313 * server we got the ticket from. We must not proceed as
314 * otherwise the ticket signature is unchecked.
316 ret
= HDB_ERR_NOT_FOUND_HERE
;
320 /* Fetch the correct key depending on the checksum type. */
321 if (ctype
== CKSUMTYPE_HMAC_MD5
) {
322 etype
= ENCTYPE_ARCFOUR_HMAC
;
324 ret
= krb5_cksumtype_to_enctype(context
,
331 ret
= hdb_enctype2key(context
, krbtgt
, NULL
, etype
, &key
);
336 /* Check the KDC, whole-PAC and ticket signatures. */
337 ret
= krb5_pac_verify(context
,
344 DEBUG(1, ("PAC KDC signature failed to verify\n"));
348 flags
|= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION
;
352 flags
|= SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED
;
356 flags
|= SAMBA_KDC_FLAG_KRBTGT_IN_DB
;
359 ret
= krb5_pac_init(context
, &new_pac
);
365 ret
= samba_kdc_update_pac(mem_ctx
,
367 krbtgt_skdc_entry
->kdc_db_ctx
->samdb
,
373 delegated_proxy_principal
,
377 krb5_pac_free(context
, new_pac
);
378 if (ret
== ENOATTR
) {
379 krb5_pac_free(context
, *pac
);
386 /* Replace the pac */
387 krb5_pac_free(context
, *pac
);
391 talloc_free(mem_ctx
);
395 /* Resign (and reform, including possibly new groups) a PAC */
397 static krb5_error_code
samba_wdc_reget_pac(void *priv
, astgs_request_t r
,
398 const krb5_principal client_principal
,
399 const krb5_principal delegated_proxy_principal
,
405 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
406 krb5_kdc_configuration
*config
= kdc_request_get_config((kdc_request_t
)r
);
407 struct samba_kdc_entry
*krbtgt_skdc_entry
=
408 talloc_get_type_abort(krbtgt
->context
,
409 struct samba_kdc_entry
);
411 krb5_cksumtype ctype
= CKSUMTYPE_NONE
;
412 hdb_entry signing_krbtgt_hdb
;
413 const hdb_entry
*explicit_armor_client
=
414 kdc_request_get_explicit_armor_client(r
);
415 krb5_const_pac explicit_armor_pac
=
416 kdc_request_get_explicit_armor_pac(r
);
418 if (delegated_proxy_principal
) {
420 unsigned int my_krbtgt_number
;
423 * We're using delegated_proxy_principal for the moment to
424 * indicate cases where the ticket was encrypted with the server
425 * key, and not a krbtgt key. This cannot be trusted, so we need
426 * to find a krbtgt key that signs the PAC in order to trust the
429 * The krbtgt passed in to this function refers to the krbtgt
430 * used to decrypt the ticket of the server requesting
433 * When we implement service ticket renewal, we need to check
434 * the PAC, and this will need to be updated.
436 ret
= krb5_pac_get_kdc_checksum_info(context
,
441 DEBUG(1, ("Failed to get PAC checksum info\n"));
446 * We need to check the KDC and ticket signatures, fetching the
447 * correct key based on the enctype.
450 my_krbtgt_number
= krbtgt_skdc_entry
->kdc_db_ctx
->my_krbtgt_number
;
452 if (my_krbtgt_number
!= 0) {
454 * If we are an RODC, and we are not the KDC that signed
455 * the evidence ticket, then we need to proxy the
458 if (rodc_id
!= my_krbtgt_number
) {
459 return HDB_ERR_NOT_FOUND_HERE
;
463 * If we are a DC, the ticket may have been signed by a
464 * different KDC than the one that issued the header
467 if (rodc_id
!= krbtgt
->kvno
>> 16) {
468 struct sdb_entry signing_krbtgt_sdb
;
471 * If we didn't sign the ticket, then return an
475 return KRB5KRB_AP_ERR_MODIFIED
;
479 * Fetch our key from the database. To support
480 * key rollover, we're going to need to try
481 * multiple keys by trial and error. For now,
482 * krbtgt keys aren't assumed to change.
484 ret
= samba_kdc_fetch(context
,
485 krbtgt_skdc_entry
->kdc_db_ctx
,
487 SDB_F_GET_KRBTGT
| SDB_F_CANON
,
489 &signing_krbtgt_sdb
);
494 ret
= sdb_entry_to_hdb_entry(context
,
496 &signing_krbtgt_hdb
);
497 sdb_entry_free(&signing_krbtgt_sdb
);
503 * Replace the krbtgt entry with our own entry
504 * for further processing.
506 krbtgt
= &signing_krbtgt_hdb
;
509 } else if (!krbtgt_skdc_entry
->is_trust
) {
511 * We expect to have received a TGT, so check that we haven't
512 * been given a kpasswd ticket instead. We don't need to do this
513 * check for an incoming trust, as they use a different secret
514 * and can't be confused with a normal TGT.
516 krb5_ticket
*tgt
= kdc_request_get_ticket(r
);
518 struct timeval now
= krb5_kdc_get_time();
521 * Check if the ticket is in the last two minutes of its
524 KerberosTime lifetime
= rk_time_sub(tgt
->ticket
.endtime
, now
.tv_sec
);
525 if (lifetime
<= CHANGEPW_LIFETIME
) {
527 * This ticket has at most two minutes left to live. It
528 * may be a kpasswd ticket rather than a TGT, so don't
531 kdc_audit_addreason((kdc_request_t
)r
,
532 "Ticket is not a ticket-granting ticket");
533 return KRB5KRB_AP_ERR_TKT_EXPIRED
;
537 ret
= samba_wdc_reget_pac2(r
,
538 delegated_proxy_principal
,
544 explicit_armor_client
,
545 &explicit_armor_pac
);
547 if (krbtgt
== &signing_krbtgt_hdb
) {
548 hdb_free_entry(context
, config
->db
[0], &signing_krbtgt_hdb
);
554 static char *get_netbios_name(TALLOC_CTX
*mem_ctx
, HostAddresses
*addrs
)
556 char *nb_name
= NULL
;
560 for (i
= 0; addrs
&& i
< addrs
->len
; i
++) {
561 if (addrs
->val
[i
].addr_type
!= KRB5_ADDRESS_NETBIOS
) {
564 len
= MIN(addrs
->val
[i
].address
.length
, 15);
565 nb_name
= talloc_strndup(mem_ctx
,
566 addrs
->val
[i
].address
.data
, len
);
572 if ((nb_name
== NULL
) || (nb_name
[0] == '\0')) {
576 /* Strip space padding */
577 for (len
= strlen(nb_name
) - 1;
578 (len
> 0) && (nb_name
[len
] == ' ');
586 /* this function allocates 'data' using malloc.
587 * The caller is responsible for freeing it */
588 static void samba_kdc_build_edata_reply(NTSTATUS nt_status
, krb5_data
*e_data
)
590 e_data
->data
= malloc(12);
591 if (e_data
->data
== NULL
) {
598 SIVAL(e_data
->data
, 0, NT_STATUS_V(nt_status
));
599 SIVAL(e_data
->data
, 4, 0);
600 SIVAL(e_data
->data
, 8, 1);
605 static krb5_error_code
samba_wdc_check_client_access(void *priv
,
608 struct samba_kdc_entry
*kdc_entry
;
609 bool password_change
;
614 kdc_entry
= talloc_get_type(kdc_request_get_client(r
)->context
, struct samba_kdc_entry
);
615 password_change
= (kdc_request_get_server(r
) && kdc_request_get_server(r
)->flags
.change_pw
);
616 workstation
= get_netbios_name((TALLOC_CTX
*)kdc_request_get_client(r
)->context
,
617 kdc_request_get_req(r
)->req_body
.addresses
);
619 nt_status
= samba_kdc_check_client_access(kdc_entry
,
620 kdc_request_get_cname((kdc_request_t
)r
),
624 if (!NT_STATUS_IS_OK(nt_status
)) {
625 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_MEMORY
)) {
629 if (kdc_request_get_rep(r
)->padata
) {
633 samba_kdc_build_edata_reply(nt_status
, &kd
);
634 ret
= krb5_padata_add(kdc_request_get_context((kdc_request_t
)r
), kdc_request_get_rep(r
)->padata
,
639 * So we do not leak the allocated
640 * memory on kd in the error case
646 return samba_kdc_map_policy_err(nt_status
);
649 /* Now do the standard Heimdal check */
650 return KRB5_PLUGIN_NO_HANDLE
;
653 /* this function allocates 'data' using malloc.
654 * The caller is responsible for freeing it */
655 static krb5_error_code
samba_kdc_build_supported_etypes(uint32_t supported_etypes
,
658 e_data
->data
= malloc(4);
659 if (e_data
->data
== NULL
) {
664 PUSH_LE_U32(e_data
->data
, 0, supported_etypes
);
669 static krb5_error_code
samba_wdc_finalize_reply(void *priv
,
672 struct samba_kdc_entry
*server_kdc_entry
;
673 uint32_t supported_enctypes
;
675 server_kdc_entry
= talloc_get_type(kdc_request_get_server(r
)->context
, struct samba_kdc_entry
);
678 * If the canonicalize flag is set, add PA-SUPPORTED-ENCTYPES padata
679 * type to indicate what encryption types the server supports.
681 supported_enctypes
= server_kdc_entry
->supported_enctypes
;
682 if (kdc_request_get_req(r
)->req_body
.kdc_options
.canonicalize
&& supported_enctypes
!= 0) {
687 ret
= samba_kdc_build_supported_etypes(supported_enctypes
, &md
.padata_value
);
692 md
.padata_type
= KRB5_PADATA_SUPPORTED_ETYPES
;
694 ret
= kdc_request_add_encrypted_padata(r
, &md
);
697 * So we do not leak the allocated
698 * memory on kd in the error case
700 krb5_data_free(&md
.padata_value
);
707 static krb5_error_code
samba_wdc_plugin_init(krb5_context context
, void **ptr
)
713 static void samba_wdc_plugin_fini(void *ptr
)
718 static krb5_error_code
samba_wdc_referral_policy(void *priv
,
721 return kdc_request_get_error_code((kdc_request_t
)r
);
724 struct krb5plugin_kdc_ftable kdc_plugin_table
= {
725 .minor_version
= KRB5_PLUGIN_KDC_VERSION_10
,
726 .init
= samba_wdc_plugin_init
,
727 .fini
= samba_wdc_plugin_fini
,
728 .pac_verify
= samba_wdc_reget_pac
,
729 .client_access
= samba_wdc_check_client_access
,
730 .finalize_reply
= samba_wdc_finalize_reply
,
731 .pac_generate
= samba_wdc_get_pac
,
732 .referral_policy
= samba_wdc_referral_policy
,