summaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorJoseph Sutton <josephsutton@catalyst.net.nz>2022-11-09 13:45:13 +1300
committerStefan Metzmacher <metze@samba.org>2022-12-13 13:07:29 +0000
commita50a2be622afaa7a280312ea12f5eb9c9a0c41da (patch)
treecdc51bdbc2effdc4a65fe0af39ab5777cb5805e1 /third_party
parent538315a2aa6d03b7639b49eb1576efa8755fefec (diff)
downloadsamba-a50a2be622afaa7a280312ea12f5eb9c9a0c41da.tar.gz
CVE-2022-37967 Add new PAC checksum
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15231 Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org> Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'third_party')
-rw-r--r--third_party/heimdal/kdc/kerberos5.c1
-rw-r--r--third_party/heimdal/kdc/krb5tgs.c2
-rw-r--r--third_party/heimdal/lib/krb5/pac.c169
3 files changed, 143 insertions, 29 deletions
diff --git a/third_party/heimdal/kdc/kerberos5.c b/third_party/heimdal/kdc/kerberos5.c
index 3e0f2dbd6b6..b4968afcaaf 100644
--- a/third_party/heimdal/kdc/kerberos5.c
+++ b/third_party/heimdal/kdc/kerberos5.c
@@ -1913,6 +1913,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey,
rodc_id,
NULL, /* UPN */
canon_princ,
+ false, /* add_full_sig */
is_tgs ? &r->pac_attributes : NULL,
&data);
krb5_free_principal(r->context, client);
diff --git a/third_party/heimdal/kdc/krb5tgs.c b/third_party/heimdal/kdc/krb5tgs.c
index aab6806fbe1..893e77749cf 100644
--- a/third_party/heimdal/kdc/krb5tgs.c
+++ b/third_party/heimdal/kdc/krb5tgs.c
@@ -778,7 +778,7 @@ tgs_make_reply(astgs_request_t r,
ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, r->client_princ, serverkey,
krbtgtkey, rodc_id, NULL, r->canon_client_princ,
- add_ticket_sig, et,
+ add_ticket_sig, add_ticket_sig, et,
is_tgs ? &r->pac_attributes : NULL);
if (ret)
goto out;
diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c
index e6dfe2aef3d..26403e665a0 100644
--- a/third_party/heimdal/lib/krb5/pac.c
+++ b/third_party/heimdal/lib/krb5/pac.c
@@ -74,6 +74,7 @@ struct krb5_pac_data {
struct PAC_INFO_BUFFER *upn_dns_info;
struct PAC_INFO_BUFFER *ticket_checksum;
struct PAC_INFO_BUFFER *attributes_info;
+ struct PAC_INFO_BUFFER *full_checksum;
krb5_data ticket_sign_data;
/* PAC_UPN_DNS_INFO */
@@ -101,6 +102,7 @@ struct krb5_pac_data {
#define PAC_TICKET_CHECKSUM 16
#define PAC_ATTRIBUTES_INFO 17
#define PAC_REQUESTOR_SID 18
+#define PAC_FULL_CHECKSUM 19
/* Flag in PAC_UPN_DNS_INFO */
#define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED 0x1
@@ -398,6 +400,13 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
else
p->attributes_info = &p->pac->buffers[i];
break;
+ case PAC_FULL_CHECKSUM:
+ if (p->full_checksum)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple full checksums", ""));
+ else
+ p->full_checksum = &p->pac->buffers[i];
+ break;
default: break;
}
}
@@ -668,7 +677,8 @@ verify_checksum(krb5_context context,
const struct PAC_INFO_BUFFER *sig,
const krb5_data *data,
void *ptr, size_t len,
- const krb5_keyblock *key)
+ const krb5_keyblock *key,
+ krb5_boolean strict_cksumtype_match)
{
krb5_storage *sp = NULL;
uint32_t type;
@@ -726,7 +736,7 @@ verify_checksum(krb5_context context,
* http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
* for Microsoft's explaination */
- if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
+ if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5 && !strict_cksumtype_match) {
Checksum local_checksum;
memset(&local_checksum, 0, sizeof(local_checksum));
@@ -1192,7 +1202,7 @@ parse_attributes_info(krb5_context context,
* @param pac the pac structure returned by krb5_pac_parse().
* @param authtime The time of the ticket the PAC belongs to.
* @param principal the principal to verify.
- * @param server The service key, most always be given.
+ * @param server The service key, may be given.
* @param privsvr The KDC key, may be given.
* @return Returns 0 to indicate success. Otherwise an kerberos et
@@ -1210,6 +1220,21 @@ krb5_pac_verify(krb5_context context,
const krb5_keyblock *privsvr)
{
krb5_error_code ret;
+ /*
+ * If we are in the KDC, we expect back a full signature in the PAC
+ *
+ * This is set up as a seperate variable to make it easier if a
+ * subsequent patch is added to make this configurable in the
+ * krb5.conf (or forced into the krb5_context via Samba)
+ */
+ krb5_boolean expect_full_sig = privsvr != NULL;
+
+ /*
+ * If we are on the KDC, then we trust we are not in a realm with
+ * buggy Windows 2008 or similar era DCs that give our HMAC-MD5
+ * sigatures over AES keys. DES is also already gone.
+ */
+ krb5_boolean strict_cksumtype_match = expect_full_sig;
if (pac->server_checksum == NULL) {
krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
@@ -1223,6 +1248,10 @@ krb5_pac_verify(krb5_context context,
krb5_set_error_message(context, EINVAL, "PAC missing logon name");
return EINVAL;
}
+ if (expect_full_sig && pac->full_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing full checksum");
+ return EINVAL;
+ }
if (principal != NULL) {
ret = verify_logonname(context, pac->logon_name, &pac->data, authtime,
@@ -1235,14 +1264,15 @@ krb5_pac_verify(krb5_context context,
pac->privsvr_checksum->buffersize < 4)
return EINVAL;
- /*
- * in the service case, clean out data option of the privsvr and
- * server checksum before checking the checksum.
- */
- if (server != NULL)
+ if (server != NULL || privsvr != NULL)
{
krb5_data *copy;
+ /*
+ * in the service case, clean out data option of the privsvr and
+ * server checksum before checking the checksum.
+ */
+
ret = krb5_copy_data(context, &pac->data, &copy);
if (ret)
return ret;
@@ -1255,15 +1285,43 @@ krb5_pac_verify(krb5_context context,
0,
pac->privsvr_checksum->buffersize - 4);
- ret = verify_checksum(context,
- pac->server_checksum,
- &pac->data,
- copy->data,
- copy->length,
- server);
+ if (server != NULL) {
+ ret = verify_checksum(context,
+ pac->server_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ server,
+ strict_cksumtype_match);
+ if (ret) {
+ krb5_free_data(context, copy);
+ return ret;
+ }
+ }
+
+ if (privsvr != NULL && pac->full_checksum != NULL) {
+ /*
+ * in the full checksum case, also clean out the full
+ * checksum before verifying it.
+ */
+ memset((char *)copy->data + pac->full_checksum->offset + 4,
+ 0,
+ pac->full_checksum->buffersize - 4);
+
+ ret = verify_checksum(context,
+ pac->full_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ privsvr,
+ strict_cksumtype_match);
+ if (ret) {
+ krb5_free_data(context, copy);
+ return ret;
+ }
+ }
+
krb5_free_data(context, copy);
- if (ret)
- return ret;
}
if (privsvr) {
/* The priv checksum covers the server checksum */
@@ -1273,7 +1331,8 @@ krb5_pac_verify(krb5_context context,
(char *)pac->data.data
+ pac->server_checksum->offset + 4,
pac->server_checksum->buffersize - 4,
- privsvr);
+ privsvr,
+ strict_cksumtype_match);
if (ret)
return ret;
@@ -1286,7 +1345,8 @@ krb5_pac_verify(krb5_context context,
ret = verify_checksum(context, pac->ticket_checksum, &pac->data,
pac->ticket_sign_data.data,
- pac->ticket_sign_data.length, privsvr);
+ pac->ticket_sign_data.length, privsvr,
+ strict_cksumtype_match);
if (ret)
return ret;
}
@@ -1377,6 +1437,7 @@ _krb5_pac_sign(krb5_context context,
uint16_t rodc_id,
krb5_const_principal upn_princ,
krb5_const_principal canon_princ,
+ krb5_boolean add_full_sig,
uint64_t *pac_attributes, /* optional */
krb5_data *data)
{
@@ -1384,7 +1445,7 @@ _krb5_pac_sign(krb5_context context,
krb5_storage *sp = NULL, *spdata = NULL;
uint32_t end;
size_t server_size, priv_size;
- uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0;
+ uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0, full_offset = 0;
uint32_t server_cksumtype = 0, priv_cksumtype = 0;
uint32_t num = 0;
uint32_t i, sz;
@@ -1461,6 +1522,16 @@ _krb5_pac_sign(krb5_context context,
N_("PAC has multiple attributes info buffers", ""));
goto out;
}
+ } else if (p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
+ if (p->full_checksum == NULL) {
+ p->full_checksum = &p->pac->buffers[i];
+ }
+ if (p->full_checksum != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple full checksums", ""));
+ goto out;
+ }
}
}
@@ -1473,6 +1544,8 @@ _krb5_pac_sign(krb5_context context,
num++;
if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL)
num++;
+ if (add_full_sig && p->full_checksum == NULL)
+ num++;
/* Allocate any missing-but-necessary buffers */
if (num) {
@@ -1515,6 +1588,11 @@ _krb5_pac_sign(krb5_context context,
p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++];
p->ticket_checksum->type = PAC_TICKET_CHECKSUM;
}
+ if (add_full_sig && p->full_checksum == NULL) {
+ p->full_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->full_checksum, 0, sizeof(*p->full_checksum));
+ p->full_checksum->type = PAC_FULL_CHECKSUM;
+ }
}
/* Calculate LOGON NAME */
@@ -1647,6 +1725,31 @@ _krb5_pac_sign(krb5_context context,
len += sizeof(rodc_id);
CHECK(ret, krb5_store_uint16(spdata, rodc_id), out);
}
+ } else if (add_full_sig &&
+ p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
+ if (priv_size > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len = priv_size + 4;
+ if (end > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ full_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ if (rodc_id != 0) {
+ if (len > UINT32_MAX - sizeof(rodc_id)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len += sizeof(rodc_id);
+ CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
+ }
} else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
len = krb5_storage_write(spdata, logon.data, logon.length);
if (logon.length != len) {
@@ -1708,6 +1811,21 @@ _krb5_pac_sign(krb5_context context,
p->ticket_sign_data.data,
p->ticket_sign_data.length,
(char *)d.data + ticket_offset, priv_size);
+ if (ret == 0 && add_full_sig)
+ ret = create_checksum(context, priv_key, priv_cksumtype,
+ d.data, d.length,
+ (char *)d.data + full_offset, priv_size);
+ if (ret == 0 && add_full_sig && rodc_id != 0) {
+ void *buf = (char *)d.data + full_offset + priv_size;
+ krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
+ if (rs == NULL)
+ ret = krb5_enomem(context);
+ else
+ krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
+ if (ret == 0)
+ ret = krb5_store_uint16(rs, rodc_id);
+ krb5_storage_free(rs);
+ }
if (ret == 0)
ret = create_checksum(context, server_key, server_cksumtype,
d.data, d.length,
@@ -1717,22 +1835,15 @@ _krb5_pac_sign(krb5_context context,
(char *)d.data + server_offset, server_size,
(char *)d.data + priv_offset, priv_size);
if (ret == 0 && rodc_id != 0) {
- krb5_data rd;
- krb5_storage *rs = krb5_storage_emem();
+ void *buf = (char *)d.data + priv_offset + priv_size;
+ krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
if (rs == NULL)
ret = krb5_enomem(context);
else
krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
if (ret == 0)
ret = krb5_store_uint16(rs, rodc_id);
- if (ret == 0)
- ret = krb5_storage_to_data(rs, &rd);
krb5_storage_free(rs);
- if (ret)
- goto out;
- heim_assert(rd.length == sizeof(rodc_id), "invalid length");
- memcpy((char *)d.data + priv_offset + priv_size, rd.data, rd.length);
- krb5_data_free(&rd);
}
if (ret)
@@ -1975,6 +2086,7 @@ _krb5_kdc_pac_sign_ticket(krb5_context context,
krb5_const_principal upn,
krb5_const_principal canon_name,
krb5_boolean add_ticket_sig,
+ krb5_boolean add_full_sig,
EncTicketPart *tkt,
uint64_t *pac_attributes) /* optional */
{
@@ -2012,6 +2124,7 @@ _krb5_kdc_pac_sign_ticket(krb5_context context,
ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key,
kdc_key, rodc_id, upn, canon_name,
+ add_full_sig,
pac_attributes, &rspac);
if (ret == 0)
ret = _kdc_tkt_insert_pac(context, tkt, &rspac);