diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-11-09 13:45:13 +1300 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-12-13 13:07:29 +0000 |
commit | a50a2be622afaa7a280312ea12f5eb9c9a0c41da (patch) | |
tree | cdc51bdbc2effdc4a65fe0af39ab5777cb5805e1 /third_party | |
parent | 538315a2aa6d03b7639b49eb1576efa8755fefec (diff) | |
download | samba-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.c | 1 | ||||
-rw-r--r-- | third_party/heimdal/kdc/krb5tgs.c | 2 | ||||
-rw-r--r-- | third_party/heimdal/lib/krb5/pac.c | 169 |
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, ©); 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); |