1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-10 01:18:15 +03:00

s4:kdc: Check ticket signature

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Joseph Sutton 2021-10-08 16:08:39 +13:00 committed by Andrew Bartlett
parent 3bdce12789
commit 02fa69c6c7
2 changed files with 215 additions and 64 deletions

View File

@ -70,13 +70,8 @@
#
# S4U tests
#
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_constrained_delegation_old_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_service_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_client_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_service_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a
@ -88,10 +83,6 @@
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
#
# RODC tests
#
^samba.tests.krb5.rodc_tests.samba.tests.krb5.rodc_tests.RodcKerberosTests.test_rodc_ticket_signature
#
# The lack of KRB5SignedPath means we no longer return
# KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case
#

View File

@ -23,7 +23,10 @@
#include "includes.h"
#include "kdc/kdc-glue.h"
#include "kdc/db-glue.h"
#include "kdc/pac-glue.h"
#include "sdb.h"
#include "sdb_hdb.h"
/*
* Given the right private pointer from hdb_samba4,
@ -94,15 +97,13 @@ static krb5_error_code samba_wdc_get_pac_compat(void *priv, krb5_context context
return samba_wdc_get_pac(priv, context, client, NULL, pac);
}
/* Resign (and reform, including possibly new groups) a PAC */
static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
const krb5_principal client_principal,
const krb5_principal delegated_proxy_principal,
struct hdb_entry_ex *client,
struct hdb_entry_ex *server,
struct hdb_entry_ex *krbtgt,
krb5_pac *pac)
static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
const krb5_principal delegated_proxy_principal,
struct hdb_entry_ex *client,
struct hdb_entry_ex *server,
struct hdb_entry_ex *krbtgt,
krb5_pac *pac,
krb5_cksumtype ctype)
{
struct samba_kdc_entry *p =
talloc_get_type_abort(server->ctx,
@ -110,15 +111,13 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
struct samba_kdc_entry *krbtgt_skdc_entry =
talloc_get_type_abort(krbtgt->ctx,
struct samba_kdc_entry);
TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac context");
TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac2 context");
krb5_pac new_pac = NULL;
DATA_BLOB *pac_blob = NULL;
DATA_BLOB *upn_blob = NULL;
DATA_BLOB *deleg_blob = NULL;
krb5_error_code ret;
NTSTATUS nt_status;
struct PAC_SIGNATURE_DATA *pac_srv_sig;
struct PAC_SIGNATURE_DATA *pac_kdc_sig;
bool is_in_db, is_untrusted;
size_t num_types = 0;
uint32_t *types = NULL;
@ -130,6 +129,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
ssize_t upn_dns_info_idx = -1;
ssize_t srv_checksum_idx = -1;
ssize_t kdc_checksum_idx = -1;
ssize_t tkt_checksum_idx = -1;
if (!mem_ctx) {
return ENOMEM;
@ -150,6 +150,71 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
return ret;
}
if (delegated_proxy_principal != NULL) {
krb5_enctype etype;
Key *key = NULL;
if (!is_in_db) {
/*
* The RODC-issued PAC was signed by a KDC entry that we
* don't have a key for. The server signature is not
* trustworthy, since it could have been created by the
* server we got the ticket from. We must not proceed as
* otherwise the ticket signature is unchecked.
*/
talloc_free(mem_ctx);
return HDB_ERR_NOT_FOUND_HERE;
}
/* Fetch the correct key depending on the checksum type. */
if (ctype == CKSUMTYPE_HMAC_MD5) {
etype = ENCTYPE_ARCFOUR_HMAC;
} else {
ret = krb5_cksumtype_to_enctype(context,
ctype,
&etype);
if (ret != 0) {
talloc_free(mem_ctx);
return ret;
}
}
ret = hdb_enctype2key(context, &krbtgt->entry, etype, &key);
if (ret != 0) {
return ret;
}
/* Check the KDC and ticket signatures. */
ret = krb5_pac_verify(context,
*pac,
0,
NULL,
NULL,
&key->key);
if (ret != 0) {
DEBUG(1, ("PAC KDC signature failed to verify\n"));
talloc_free(mem_ctx);
return ret;
}
deleg_blob = talloc_zero(mem_ctx, DATA_BLOB);
if (!deleg_blob) {
talloc_free(mem_ctx);
return ENOMEM;
}
nt_status = samba_kdc_update_delegation_info_blob(mem_ctx,
context, *pac,
server->entry.principal,
delegated_proxy_principal,
deleg_blob);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("Building PAC failed: %s\n",
nt_errstr(nt_status)));
talloc_free(mem_ctx);
return EINVAL;
}
}
if (is_untrusted) {
struct samba_kdc_entry *client_skdc_entry = NULL;
@ -173,52 +238,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
return ENOMEM;
}
pac_srv_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA);
if (!pac_srv_sig) {
talloc_free(mem_ctx);
return ENOMEM;
}
pac_kdc_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA);
if (!pac_kdc_sig) {
talloc_free(mem_ctx);
return ENOMEM;
}
nt_status = samba_kdc_update_pac_blob(mem_ctx, context,
krbtgt_skdc_entry->kdc_db_ctx->samdb,
*pac, pac_blob,
pac_srv_sig, pac_kdc_sig);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("Building PAC failed: %s\n",
nt_errstr(nt_status)));
talloc_free(mem_ctx);
return EINVAL;
}
if (is_in_db) {
/* Now check the KDC signature, fetching the correct key based on the enc type */
ret = kdc_check_pac(context, pac_srv_sig->signature, pac_kdc_sig, krbtgt);
if (ret != 0) {
DEBUG(1, ("PAC KDC signature failed to verify\n"));
talloc_free(mem_ctx);
return ret;
}
}
}
if (delegated_proxy_principal) {
deleg_blob = talloc_zero(mem_ctx, DATA_BLOB);
if (!deleg_blob) {
talloc_free(mem_ctx);
return ENOMEM;
}
nt_status = samba_kdc_update_delegation_info_blob(mem_ctx,
context, *pac,
server->entry.principal,
delegated_proxy_principal,
deleg_blob);
NULL, NULL);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("Building PAC failed: %s\n",
nt_errstr(nt_status)));
@ -308,6 +331,18 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
}
kdc_checksum_idx = i;
break;
case PAC_TYPE_TICKET_CHECKSUM:
if (tkt_checksum_idx != -1) {
DEBUG(1, ("ticket checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n",
types[i],
tkt_checksum_idx,
i));
SAFE_FREE(types);
talloc_free(mem_ctx);
return EINVAL;
}
tkt_checksum_idx = i;
break;
default:
continue;
}
@ -471,6 +506,131 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
return ret;
}
/* Resign (and reform, including possibly new groups) a PAC */
static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
const krb5_principal client_principal,
const krb5_principal delegated_proxy_principal,
struct hdb_entry_ex *client,
struct hdb_entry_ex *server,
struct hdb_entry_ex *krbtgt,
krb5_pac *pac)
{
struct samba_kdc_entry *krbtgt_skdc_entry =
talloc_get_type_abort(krbtgt->ctx,
struct samba_kdc_entry);
krb5_error_code ret;
krb5_cksumtype ctype = CKSUMTYPE_NONE;
struct hdb_entry_ex signing_krbtgt_hdb;
if (delegated_proxy_principal) {
uint16_t rodc_id;
unsigned int my_krbtgt_number;
/*
* We're using delegated_proxy_principal for the moment to
* indicate cases where the ticket was encrypted with the server
* key, and not a krbtgt key. This cannot be trusted, so we need
* to find a krbtgt key that signs the PAC in order to trust the
* ticket.
*
* The krbtgt passed in to this function refers to the krbtgt
* used to decrypt the ticket of the server requesting
* S4U2Proxy.
*
* When we implement service ticket renewal, we need to check
* the PAC, and this will need to be updated.
*/
ret = krb5_pac_get_kdc_checksum_info(context,
*pac,
&ctype,
&rodc_id);
if (ret != 0) {
DEBUG(1, ("Failed to get PAC checksum info\n"));
return ret;
}
/*
* We need to check the KDC and ticket signatures, fetching the
* correct key based on the enctype.
*/
my_krbtgt_number = krbtgt_skdc_entry->kdc_db_ctx->my_krbtgt_number;
if (my_krbtgt_number != 0) {
/*
* If we are an RODC, and we are not the KDC that signed
* the evidence ticket, then we need to proxy the
* request.
*/
if (rodc_id != my_krbtgt_number) {
return HDB_ERR_NOT_FOUND_HERE;
}
} else {
/*
* If we are a DC, the ticket may have been signed by a
* different KDC than the one that issued the header
* ticket.
*/
if (rodc_id != krbtgt->entry.kvno >> 16) {
struct sdb_entry_ex signing_krbtgt_sdb;
/*
* If we didn't sign the ticket, then return an
* error.
*/
if (rodc_id != 0) {
return KRB5KRB_AP_ERR_MODIFIED;
}
/*
* Fetch our key from the database. To support
* key rollover, we're going to need to try
* multiple keys by trial and error. For now,
* krbtgt keys aren't assumed to change.
*/
ret = samba_kdc_fetch(context,
krbtgt_skdc_entry->kdc_db_ctx,
krbtgt->entry.principal,
SDB_F_GET_KRBTGT | SDB_F_CANON,
0,
&signing_krbtgt_sdb);
if (ret != 0) {
return ret;
}
ret = sdb_entry_ex_to_hdb_entry_ex(context,
&signing_krbtgt_sdb,
&signing_krbtgt_hdb);
sdb_free_entry(&signing_krbtgt_sdb);
if (ret != 0) {
return ret;
}
/*
* Replace the krbtgt entry with our own entry
* for further processing.
*/
krbtgt = &signing_krbtgt_hdb;
}
}
}
ret = samba_wdc_reget_pac2(context,
delegated_proxy_principal,
client,
server,
krbtgt,
pac,
ctype);
if (krbtgt == &signing_krbtgt_hdb) {
hdb_free_entry(context, &signing_krbtgt_hdb);
}
return ret;
}
static char *get_netbios_name(TALLOC_CTX *mem_ctx, HostAddresses *addrs)
{
char *nb_name = NULL;