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>
33 static bool samba_wdc_is_s4u2self_req(astgs_request_t r
)
35 krb5_kdc_configuration
*config
= kdc_request_get_config((kdc_request_t
)r
);
36 const KDC_REQ
*req
= kdc_request_get_req(r
);
37 const PA_DATA
*pa_for_user
= NULL
;
39 if (req
->msg_type
!= krb_tgs_req
) {
43 if (config
->enable_fast
&& req
->padata
!= NULL
) {
44 const PA_DATA
*pa_fx_fast
= NULL
;
47 pa_fx_fast
= krb5_find_padata(req
->padata
->val
,
51 if (pa_fx_fast
!= NULL
) {
53 * We're in the outer request
54 * with KRB5_PADATA_FX_FAST
55 * if fast is enabled we'll
56 * process the s4u2self
64 if (req
->padata
!= NULL
) {
67 pa_for_user
= krb5_find_padata(req
->padata
->val
,
73 if (pa_for_user
!= NULL
) {
81 * Given the right private pointer from hdb_samba4,
82 * get a PAC from the attached ldb messages.
84 * For PKINIT we also get pk_reply_key and can add PAC_CREDENTIAL_INFO.
86 static krb5_error_code
samba_wdc_get_pac(void *priv
,
90 const krb5_keyblock
*pk_reply_key
,
91 uint64_t pac_attributes
,
94 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
96 DATA_BLOB
*logon_blob
= NULL
;
97 DATA_BLOB
*cred_ndr
= NULL
;
98 DATA_BLOB
**cred_ndr_ptr
= NULL
;
99 DATA_BLOB _cred_blob
= data_blob_null
;
100 DATA_BLOB
*cred_blob
= NULL
;
101 DATA_BLOB
*upn_blob
= NULL
;
102 DATA_BLOB
*pac_attrs_blob
= NULL
;
103 DATA_BLOB
*requester_sid_blob
= NULL
;
106 struct samba_kdc_entry
*skdc_entry
=
107 talloc_get_type_abort(client
->context
,
108 struct samba_kdc_entry
);
110 bool is_s4u2self
= samba_wdc_is_s4u2self_req(r
);
111 enum samba_asserted_identity asserted_identity
=
113 SAMBA_ASSERTED_IDENTITY_SERVICE
:
114 SAMBA_ASSERTED_IDENTITY_AUTHENTICATION_AUTHORITY
;
116 mem_ctx
= talloc_named(client
->context
, 0, "samba_get_pac context");
121 if (pk_reply_key
!= NULL
) {
122 cred_ndr_ptr
= &cred_ndr
;
125 is_krbtgt
= krb5_principal_is_krbtgt(context
, server
->principal
);
127 nt_status
= samba_kdc_get_pac_blobs(mem_ctx
, skdc_entry
,
132 is_krbtgt
? &pac_attrs_blob
: NULL
,
134 is_krbtgt
? &requester_sid_blob
: NULL
);
135 if (!NT_STATUS_IS_OK(nt_status
)) {
136 talloc_free(mem_ctx
);
140 if (pk_reply_key
!= NULL
&& cred_ndr
!= NULL
) {
141 ret
= samba_kdc_encrypt_pac_credentials(context
,
147 talloc_free(mem_ctx
);
150 cred_blob
= &_cred_blob
;
153 ret
= krb5_pac_init(context
, pac
);
155 talloc_free(mem_ctx
);
159 ret
= samba_make_krb5_pac(context
, logon_blob
, cred_blob
,
160 upn_blob
, pac_attrs_blob
,
161 requester_sid_blob
, NULL
, *pac
);
163 talloc_free(mem_ctx
);
167 static krb5_error_code
samba_wdc_reget_pac2(astgs_request_t r
,
168 const krb5_principal delegated_proxy_principal
,
173 krb5_cksumtype ctype
)
175 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
176 struct samba_kdc_entry
*client_skdc_entry
= NULL
;
177 struct samba_kdc_entry
*server_skdc_entry
=
178 talloc_get_type_abort(server
->context
, struct samba_kdc_entry
);
179 struct samba_kdc_entry
*krbtgt_skdc_entry
=
180 talloc_get_type_abort(krbtgt
->context
, struct samba_kdc_entry
);
181 TALLOC_CTX
*mem_ctx
= NULL
;
182 krb5_pac new_pac
= NULL
;
184 bool is_s4u2self
= samba_wdc_is_s4u2self_req(r
);
185 bool is_in_db
= false;
186 bool is_untrusted
= false;
189 mem_ctx
= talloc_named(NULL
, 0, "samba_kdc_reget_pac2 context");
190 if (mem_ctx
== NULL
) {
194 if (client
!= NULL
) {
195 client_skdc_entry
= talloc_get_type_abort(client
->context
,
196 struct samba_kdc_entry
);
200 * If the krbtgt was generated by an RODC, and we are not that
201 * RODC, then we need to regenerate the PAC - we can't trust
202 * it, and confirm that the RODC was permitted to print this ticket
204 * Becasue of the samba_kdc_validate_pac_blob() step we can be
205 * sure that the record in 'client' matches the SID in the
208 ret
= samba_krbtgt_is_in_db(krbtgt_skdc_entry
, &is_in_db
, &is_untrusted
);
214 flags
|= SAMBA_KDC_FLAG_PROTOCOL_TRANSITION
;
217 if (delegated_proxy_principal
!= NULL
) {
223 * The RODC-issued PAC was signed by a KDC entry that we
224 * don't have a key for. The server signature is not
225 * trustworthy, since it could have been created by the
226 * server we got the ticket from. We must not proceed as
227 * otherwise the ticket signature is unchecked.
229 ret
= HDB_ERR_NOT_FOUND_HERE
;
233 /* Fetch the correct key depending on the checksum type. */
234 if (ctype
== CKSUMTYPE_HMAC_MD5
) {
235 etype
= ENCTYPE_ARCFOUR_HMAC
;
237 ret
= krb5_cksumtype_to_enctype(context
,
244 ret
= hdb_enctype2key(context
, krbtgt
, NULL
, etype
, &key
);
249 /* Check the KDC, whole-PAC and ticket signatures. */
250 ret
= krb5_pac_verify(context
,
257 DEBUG(1, ("PAC KDC signature failed to verify\n"));
261 flags
|= SAMBA_KDC_FLAG_CONSTRAINED_DELEGATION
;
265 flags
|= SAMBA_KDC_FLAG_KRBTGT_IS_UNTRUSTED
;
269 flags
|= SAMBA_KDC_FLAG_KRBTGT_IN_DB
;
272 ret
= krb5_pac_init(context
, &new_pac
);
278 ret
= samba_kdc_update_pac(mem_ctx
,
280 krbtgt_skdc_entry
->kdc_db_ctx
->samdb
,
286 delegated_proxy_principal
,
290 krb5_pac_free(context
, new_pac
);
291 if (ret
== ENODATA
) {
292 krb5_pac_free(context
, *pac
);
299 /* Replace the pac */
300 krb5_pac_free(context
, *pac
);
304 talloc_free(mem_ctx
);
308 /* Resign (and reform, including possibly new groups) a PAC */
310 static krb5_error_code
samba_wdc_reget_pac(void *priv
, astgs_request_t r
,
311 const krb5_principal client_principal
,
312 const krb5_principal delegated_proxy_principal
,
318 krb5_context context
= kdc_request_get_context((kdc_request_t
)r
);
319 krb5_kdc_configuration
*config
= kdc_request_get_config((kdc_request_t
)r
);
320 struct samba_kdc_entry
*krbtgt_skdc_entry
=
321 talloc_get_type_abort(krbtgt
->context
,
322 struct samba_kdc_entry
);
324 krb5_cksumtype ctype
= CKSUMTYPE_NONE
;
325 hdb_entry signing_krbtgt_hdb
;
327 if (delegated_proxy_principal
) {
329 unsigned int my_krbtgt_number
;
332 * We're using delegated_proxy_principal for the moment to
333 * indicate cases where the ticket was encrypted with the server
334 * key, and not a krbtgt key. This cannot be trusted, so we need
335 * to find a krbtgt key that signs the PAC in order to trust the
338 * The krbtgt passed in to this function refers to the krbtgt
339 * used to decrypt the ticket of the server requesting
342 * When we implement service ticket renewal, we need to check
343 * the PAC, and this will need to be updated.
345 ret
= krb5_pac_get_kdc_checksum_info(context
,
350 DEBUG(1, ("Failed to get PAC checksum info\n"));
355 * We need to check the KDC and ticket signatures, fetching the
356 * correct key based on the enctype.
359 my_krbtgt_number
= krbtgt_skdc_entry
->kdc_db_ctx
->my_krbtgt_number
;
361 if (my_krbtgt_number
!= 0) {
363 * If we are an RODC, and we are not the KDC that signed
364 * the evidence ticket, then we need to proxy the
367 if (rodc_id
!= my_krbtgt_number
) {
368 return HDB_ERR_NOT_FOUND_HERE
;
372 * If we are a DC, the ticket may have been signed by a
373 * different KDC than the one that issued the header
376 if (rodc_id
!= krbtgt
->kvno
>> 16) {
377 struct sdb_entry signing_krbtgt_sdb
;
380 * If we didn't sign the ticket, then return an
384 return KRB5KRB_AP_ERR_MODIFIED
;
388 * Fetch our key from the database. To support
389 * key rollover, we're going to need to try
390 * multiple keys by trial and error. For now,
391 * krbtgt keys aren't assumed to change.
393 ret
= samba_kdc_fetch(context
,
394 krbtgt_skdc_entry
->kdc_db_ctx
,
396 SDB_F_GET_KRBTGT
| SDB_F_CANON
,
398 &signing_krbtgt_sdb
);
403 ret
= sdb_entry_to_hdb_entry(context
,
405 &signing_krbtgt_hdb
);
406 sdb_entry_free(&signing_krbtgt_sdb
);
412 * Replace the krbtgt entry with our own entry
413 * for further processing.
415 krbtgt
= &signing_krbtgt_hdb
;
418 } else if (!krbtgt_skdc_entry
->is_trust
) {
420 * We expect to have received a TGT, so check that we haven't
421 * been given a kpasswd ticket instead. We don't need to do this
422 * check for an incoming trust, as they use a different secret
423 * and can't be confused with a normal TGT.
425 krb5_ticket
*tgt
= kdc_request_get_ticket(r
);
427 struct timeval now
= krb5_kdc_get_time();
430 * Check if the ticket is in the last two minutes of its
433 KerberosTime lifetime
= rk_time_sub(tgt
->ticket
.endtime
, now
.tv_sec
);
434 if (lifetime
<= CHANGEPW_LIFETIME
) {
436 * This ticket has at most two minutes left to live. It
437 * may be a kpasswd ticket rather than a TGT, so don't
440 kdc_audit_addreason((kdc_request_t
)r
,
441 "Ticket is not a ticket-granting ticket");
442 return KRB5KRB_AP_ERR_TKT_EXPIRED
;
446 ret
= samba_wdc_reget_pac2(r
,
447 delegated_proxy_principal
,
454 if (krbtgt
== &signing_krbtgt_hdb
) {
455 hdb_free_entry(context
, config
->db
[0], &signing_krbtgt_hdb
);
461 static char *get_netbios_name(TALLOC_CTX
*mem_ctx
, HostAddresses
*addrs
)
463 char *nb_name
= NULL
;
467 for (i
= 0; addrs
&& i
< addrs
->len
; i
++) {
468 if (addrs
->val
[i
].addr_type
!= KRB5_ADDRESS_NETBIOS
) {
471 len
= MIN(addrs
->val
[i
].address
.length
, 15);
472 nb_name
= talloc_strndup(mem_ctx
,
473 addrs
->val
[i
].address
.data
, len
);
479 if ((nb_name
== NULL
) || (nb_name
[0] == '\0')) {
483 /* Strip space padding */
484 for (len
= strlen(nb_name
) - 1;
485 (len
> 0) && (nb_name
[len
] == ' ');
493 /* this function allocates 'data' using malloc.
494 * The caller is responsible for freeing it */
495 static void samba_kdc_build_edata_reply(NTSTATUS nt_status
, krb5_data
*e_data
)
497 e_data
->data
= malloc(12);
498 if (e_data
->data
== NULL
) {
505 SIVAL(e_data
->data
, 0, NT_STATUS_V(nt_status
));
506 SIVAL(e_data
->data
, 4, 0);
507 SIVAL(e_data
->data
, 8, 1);
512 static krb5_error_code
samba_wdc_check_client_access(void *priv
,
515 struct samba_kdc_entry
*kdc_entry
;
516 bool password_change
;
521 kdc_entry
= talloc_get_type(kdc_request_get_client(r
)->context
, struct samba_kdc_entry
);
522 password_change
= (kdc_request_get_server(r
) && kdc_request_get_server(r
)->flags
.change_pw
);
523 workstation
= get_netbios_name((TALLOC_CTX
*)kdc_request_get_client(r
)->context
,
524 kdc_request_get_req(r
)->req_body
.addresses
);
526 nt_status
= samba_kdc_check_client_access(kdc_entry
,
527 kdc_request_get_cname((kdc_request_t
)r
),
531 if (!NT_STATUS_IS_OK(nt_status
)) {
532 if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_NO_MEMORY
)) {
536 if (kdc_request_get_rep(r
)->padata
) {
540 samba_kdc_build_edata_reply(nt_status
, &kd
);
541 ret
= krb5_padata_add(kdc_request_get_context((kdc_request_t
)r
), kdc_request_get_rep(r
)->padata
,
546 * So we do not leak the allocated
547 * memory on kd in the error case
553 return samba_kdc_map_policy_err(nt_status
);
556 /* Now do the standard Heimdal check */
557 return KRB5_PLUGIN_NO_HANDLE
;
560 /* this function allocates 'data' using malloc.
561 * The caller is responsible for freeing it */
562 static krb5_error_code
samba_kdc_build_supported_etypes(uint32_t supported_etypes
,
565 e_data
->data
= malloc(4);
566 if (e_data
->data
== NULL
) {
571 PUSH_LE_U32(e_data
->data
, 0, supported_etypes
);
576 static krb5_error_code
samba_wdc_finalize_reply(void *priv
,
579 struct samba_kdc_entry
*server_kdc_entry
;
580 uint32_t supported_enctypes
;
582 server_kdc_entry
= talloc_get_type(kdc_request_get_server(r
)->context
, struct samba_kdc_entry
);
585 * If the canonicalize flag is set, add PA-SUPPORTED-ENCTYPES padata
586 * type to indicate what encryption types the server supports.
588 supported_enctypes
= server_kdc_entry
->supported_enctypes
;
589 if (kdc_request_get_req(r
)->req_body
.kdc_options
.canonicalize
&& supported_enctypes
!= 0) {
594 ret
= samba_kdc_build_supported_etypes(supported_enctypes
, &md
.padata_value
);
599 md
.padata_type
= KRB5_PADATA_SUPPORTED_ETYPES
;
601 ret
= kdc_request_add_encrypted_padata(r
, &md
);
604 * So we do not leak the allocated
605 * memory on kd in the error case
607 krb5_data_free(&md
.padata_value
);
614 static krb5_error_code
samba_wdc_plugin_init(krb5_context context
, void **ptr
)
620 static void samba_wdc_plugin_fini(void *ptr
)
625 static krb5_error_code
samba_wdc_referral_policy(void *priv
,
628 return kdc_request_get_error_code((kdc_request_t
)r
);
631 struct krb5plugin_kdc_ftable kdc_plugin_table
= {
632 .minor_version
= KRB5_PLUGIN_KDC_VERSION_10
,
633 .init
= samba_wdc_plugin_init
,
634 .fini
= samba_wdc_plugin_fini
,
635 .pac_verify
= samba_wdc_reget_pac
,
636 .client_access
= samba_wdc_check_client_access
,
637 .finalize_reply
= samba_wdc_finalize_reply
,
638 .pac_generate
= samba_wdc_get_pac
,
639 .referral_policy
= samba_wdc_referral_policy
,