The branch, v4-16-stable has been updated via fc0f1090f4c VERSION: Disable GIT_SNAPSHOT for the 4.16.7 release. via 2e1f66e3d9d WHATSNEW: Add release notes for Samba 4.16.7. via a8ef840d436 CVE-2022-42898 third_party/heimdal: PAC parse integer overflows via b403ae70a05 VERSION: Bump version up to Samba 4.16.7... from dc74e56c03d VERSION: Disable GIT_SNAPSHOT for the 4.16.6 release.
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-16-stable - Log ----------------------------------------------------------------- commit fc0f1090f4ca6d240b4bcfc84890cfc26a44827f Author: Jule Anger <jan...@samba.org> Date: Sun Nov 13 18:40:26 2022 +0100 VERSION: Disable GIT_SNAPSHOT for the 4.16.7 release. Signed-off-by: Jule Anger <jan...@samba.org> commit 2e1f66e3d9dd6ef89ccc9926e8d1f0195e539635 Author: Jule Anger <jan...@samba.org> Date: Sun Nov 13 18:39:47 2022 +0100 WHATSNEW: Add release notes for Samba 4.16.7. Signed-off-by: Jule Anger <jan...@samba.org> commit a8ef840d4362d3ffeab13c1d5fea417511b727c2 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Fri Oct 14 16:45:37 2022 +1300 CVE-2022-42898 third_party/heimdal: PAC parse integer overflows Catch overflows that result from adding PAC_INFO_BUFFER_SIZE. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15203 Heavily edited by committer Nico Williams <n...@twosigma.com>, original by Joseph Sutton <josephsut...@catalyst.net.nz>. Signed-off-by: Nico Williams <n...@twosigma.com> [jsut...@samba.org Zero-initialised header_size in krb5_pac_parse() to avoid a maybe-uninitialized error; added a missing check for ret == 0] commit b403ae70a059f8ec053443675801efec946b8b5b Author: Jule Anger <jan...@samba.org> Date: Tue Oct 25 11:56:55 2022 +0200 VERSION: Bump version up to Samba 4.16.7... and re-enable GIT_SNAPSHOT. Signed-off-by: Jule Anger <jan...@samba.org> (cherry picked from commit c2095819c31ca66fa8a0936cca79ff1e7973966b) ----------------------------------------------------------------------- Summary of changes: VERSION | 2 +- WHATSNEW.txt | 50 ++- third_party/heimdal/lib/krb5/pac.c | 614 +++++++++++++++++++++----------- third_party/heimdal/lib/krb5/test_pac.c | 48 ++- 4 files changed, 493 insertions(+), 221 deletions(-) Changeset truncated at 500 lines: diff --git a/VERSION b/VERSION index 99e33e33656..2184d6f7481 100644 --- a/VERSION +++ b/VERSION @@ -25,7 +25,7 @@ ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=16 -SAMBA_VERSION_RELEASE=6 +SAMBA_VERSION_RELEASE=7 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index fc386e8fb05..4f085269066 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,3 +1,50 @@ + ============================== + Release Notes for Samba 4.16.7 + November 15, 2022 + ============================== + + +This is a security release in order to address the following defects: + +o CVE-2022-42898: Samba's Kerberos libraries and AD DC failed to guard against + integer overflows when parsing a PAC on a 32-bit system, which + allowed an attacker with a forged PAC to corrupt the heap. + https://www.samba.org/samba/security/CVE-2022-42898.html + +Changes since 4.16.6 +-------------------- + +o Joseph Sutton <josephsut...@catalyst.net.nz> + * BUG 15203: CVE-2022-42898 + +o Nicolas Williams <n...@twosigma.com> + * BUG 15203: CVE-2022-42898 + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical:matrix.org matrix room, or +#samba-technical IRC channel on irc.libera.chat. + + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the Samba 4.1 and newer product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- ============================== Release Notes for Samba 4.16.6 October 25, 2022 @@ -39,8 +86,7 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- ============================== Release Notes for Samba 4.16.5 September 07, 2022 diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c index c8f355c8179..c11990a1606 100644 --- a/third_party/heimdal/lib/krb5/pac.c +++ b/third_party/heimdal/lib/krb5/pac.c @@ -37,19 +37,34 @@ #include <wind.h> #include <assert.h> +/* + * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/3341cfa2-6ef5-42e0-b7bc-4544884bf399 + */ struct PAC_INFO_BUFFER { - uint32_t type; - uint32_t buffersize; - uint32_t offset_hi; - uint32_t offset_lo; + uint32_t type; /* ULONG ulType in the original */ + uint32_t buffersize; /* ULONG cbBufferSize in the original */ + uint64_t offset; /* ULONG64 Offset in the original + * this being the offset from the beginning of the + * struct PACTYPE to the beginning of the buffer + * containing data of type ulType + */ }; +/* + * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/6655b92f-ab06-490b-845d-037e6987275f + */ struct PACTYPE { - uint32_t numbuffers; - uint32_t version; - struct PAC_INFO_BUFFER buffers[1]; + uint32_t numbuffers; /* named cBuffers of type ULONG in the original */ + uint32_t version; /* Named Version of type ULONG in the original */ + struct PAC_INFO_BUFFER buffers[1]; /* an ellipsis (...) in the original */ }; +/* + * A PAC starts with a PACTYPE header structure that is followed by an array of + * numbuffers PAC_INFO_BUFFER structures, each of which points to a buffer + * beyond the last PAC_INFO_BUFFER structures. + */ + struct krb5_pac_data { struct PACTYPE *pac; krb5_data data; @@ -133,6 +148,60 @@ struct heim_type_data pac_object = { NULL }; +/* + * Returns the size of the PACTYPE header + the PAC_INFO_BUFFER array. This is + * also the end of the whole thing, and any offsets to buffers from + * thePAC_INFO_BUFFER[] entries have to be beyond it. + */ +static krb5_error_code +pac_header_size(krb5_context context, uint32_t num_buffers, uint32_t *result) +{ + krb5_error_code ret; + uint32_t header_size; + + /* Guard against integer overflow */ + if (num_buffers > UINT32_MAX / PAC_INFO_BUFFER_SIZE) { + ret = EOVERFLOW; + krb5_set_error_message(context, ret, "PAC has too many buffers"); + return ret; + } + header_size = PAC_INFO_BUFFER_SIZE * num_buffers; + + /* Guard against integer overflow */ + if (header_size > UINT32_MAX - PACTYPE_SIZE) { + ret = EOVERFLOW; + krb5_set_error_message(context, ret, "PAC has too many buffers"); + return ret; + } + header_size += PACTYPE_SIZE; + + *result = header_size; + + return 0; +} + +/* Output `size' + `addend' + padding for alignment if it doesn't overflow */ +static krb5_error_code +pac_aligned_size(krb5_context context, + uint32_t size, + uint32_t addend, + uint32_t *aligned_size) +{ + krb5_error_code ret; + + if (size > UINT32_MAX - addend || + (size + addend) > UINT32_MAX - (PAC_ALIGNMENT - 1)) { + ret = EOVERFLOW; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } + size += addend; + size += PAC_ALIGNMENT - 1; + size &= ~(PAC_ALIGNMENT - 1); + *aligned_size = size; + return 0; +} + /* * HMAC-MD5 checksum over any key (needed for the PAC routines) */ @@ -184,165 +253,164 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_parse(krb5_context context, const void *ptr, size_t len, krb5_pac *pac) { - krb5_error_code ret; + krb5_error_code ret = 0; krb5_pac p; krb5_storage *sp = NULL; - uint32_t i, tmp, tmp2, header_end; + uint32_t i, num_buffers, version, header_size = 0; + uint32_t prev_start = 0; + uint32_t prev_end = 0; + *pac = NULL; p = _heim_alloc_object(&pac_object, sizeof(*p)); - if (p == NULL) { - ret = krb5_enomem(context); - goto out; - } - - sp = krb5_storage_from_readonly_mem(ptr, len); - if (sp == NULL) { + if (p) + sp = krb5_storage_from_readonly_mem(ptr, len); + if (sp == NULL) ret = krb5_enomem(context); - goto out; - } - krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); - - CHECK(ret, krb5_ret_uint32(sp, &tmp), out); - CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); - if (tmp < 1) { - ret = EINVAL; /* Too few buffers */ - krb5_set_error_message(context, ret, N_("PAC has too few buffers", "")); - goto out; + if (ret == 0) { + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + ret = krb5_ret_uint32(sp, &num_buffers); } - if (tmp2 != 0) { - ret = EINVAL; /* Wrong version */ - krb5_set_error_message(context, ret, + if (ret == 0) + ret = krb5_ret_uint32(sp, &version); + if (ret == 0 && num_buffers < 1) + krb5_set_error_message(context, ret = EINVAL, + N_("PAC has too few buffers", "")); + if (ret == 0 && num_buffers > 1000) + krb5_set_error_message(context, ret = EINVAL, + N_("PAC has too many buffers", "")); + if (ret == 0 && version != 0) + krb5_set_error_message(context, ret = EINVAL, N_("PAC has wrong version %d", ""), - (int)tmp2); - goto out; - } - - p->pac = calloc(1, - sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); - if (p->pac == NULL) { + (int)version); + if (ret == 0) + ret = pac_header_size(context, num_buffers, &header_size); + if (ret == 0 && header_size > len) + krb5_set_error_message(context, ret = EOVERFLOW, + N_("PAC encoding invalid, would overflow buffers", "")); + if (ret == 0) + p->pac = calloc(1, header_size); + if (ret == 0 && p->pac == NULL) ret = krb5_enomem(context); - goto out; - } - - p->pac->numbuffers = tmp; - p->pac->version = tmp2; - header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); - if (header_end > len) { - ret = EINVAL; - goto out; + if (ret == 0) { + p->pac->numbuffers = num_buffers; + p->pac->version = version; } - for (i = 0; i < p->pac->numbuffers; i++) { - CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); - CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); - CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); - CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); + for (i = 0; ret == 0 && i < p->pac->numbuffers; i++) { + ret = krb5_ret_uint32(sp, &p->pac->buffers[i].type); + if (ret == 0) + ret = krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize); + if (ret == 0) + ret = krb5_ret_uint64(sp, &p->pac->buffers[i].offset); + if (ret) + break; - /* consistency checks */ - if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { - ret = EINVAL; - krb5_set_error_message(context, ret, + /* Consistency checks (we don't check for wasted space) */ + if (p->pac->buffers[i].offset & (PAC_ALIGNMENT - 1)) { + krb5_set_error_message(context, ret = EINVAL, N_("PAC out of alignment", "")); - goto out; + break; } - if (p->pac->buffers[i].offset_hi) { - ret = EINVAL; - krb5_set_error_message(context, ret, - N_("PAC high offset set", "")); - goto out; + if (p->pac->buffers[i].offset > len || + p->pac->buffers[i].buffersize > len || + len - p->pac->buffers[i].offset < p->pac->buffers[i].buffersize) { + krb5_set_error_message(context, ret = EOVERFLOW, + N_("PAC buffer overflow", "")); + break; } - if (p->pac->buffers[i].offset_lo > len) { - ret = EINVAL; - krb5_set_error_message(context, ret, - N_("PAC offset overflow", "")); - goto out; - } - if (p->pac->buffers[i].offset_lo < header_end) { - ret = EINVAL; - krb5_set_error_message(context, ret, + if (p->pac->buffers[i].offset < header_size) { + krb5_set_error_message(context, ret = EINVAL, N_("PAC offset inside header: %lu %lu", ""), - (unsigned long)p->pac->buffers[i].offset_lo, - (unsigned long)header_end); - goto out; - } - if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ - ret = EINVAL; - krb5_set_error_message(context, ret, N_("PAC length overflow", "")); - goto out; + (unsigned long)p->pac->buffers[i].offset, + (unsigned long)header_size); + break; } - /* let save pointer to data we need later */ - if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { - if (p->server_checksum) { - ret = EINVAL; - krb5_set_error_message(context, ret, + /* + * We'd like to check for non-overlapping of buffers, but the buffers + * need not be in the same order as the PAC_INFO_BUFFER[] entries + * pointing to them! To fully check for overlap we'd have to have an + * O(N^2) loop after we parse all the PAC_INFO_BUFFER[]. + * + * But we can check that each buffer does not overlap the previous + * buffer. + */ + if (prev_start) { + if (p->pac->buffers[i].offset >= prev_start && + p->pac->buffers[i].offset < prev_end) { + krb5_set_error_message(context, ret = EINVAL, + N_("PAC overlap", "")); + break; + } + if (p->pac->buffers[i].offset < prev_start && + p->pac->buffers[i].offset + + p->pac->buffers[i].buffersize > prev_start) { + krb5_set_error_message(context, ret = EINVAL, + N_("PAC overlap", "")); + break; + } + } + prev_start = p->pac->buffers[i].offset; + prev_end = p->pac->buffers[i].offset + p->pac->buffers[i].buffersize; + + /* Let's save pointers to buffers we'll need later */ + switch (p->pac->buffers[i].type) { + case PAC_SERVER_CHECKSUM: + if (p->server_checksum) + krb5_set_error_message(context, ret = EINVAL, N_("PAC has multiple server checksums", "")); - goto out; - } - p->server_checksum = &p->pac->buffers[i]; - } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { - if (p->privsvr_checksum) { - ret = EINVAL; - krb5_set_error_message(context, ret, - N_("PAC has multiple KDC checksums", "")); - goto out; - } - p->privsvr_checksum = &p->pac->buffers[i]; - } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { - if (p->logon_name) { - ret = EINVAL; - krb5_set_error_message(context, ret, - N_("PAC has multiple logon names", "")); - goto out; - } - p->logon_name = &p->pac->buffers[i]; - } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) { - if (p->upn_dns_info) { - ret = EINVAL; - krb5_set_error_message(context, ret, + else + p->server_checksum = &p->pac->buffers[i]; + break; + case PAC_PRIVSVR_CHECKSUM: + if (p->privsvr_checksum) + krb5_set_error_message(context, ret = EINVAL, + N_("PAC has multiple KDC checksums", "")); + else + p->privsvr_checksum = &p->pac->buffers[i]; + break; + case PAC_LOGON_NAME: + if (p->logon_name) + krb5_set_error_message(context, ret = EINVAL, + N_("PAC has multiple logon names", "")); + else + p->logon_name = &p->pac->buffers[i]; + break; + case PAC_UPN_DNS_INFO: + if (p->upn_dns_info) + krb5_set_error_message(context, ret = EINVAL, N_("PAC has multiple UPN DNS info buffers", "")); - goto out; - } - p->upn_dns_info = &p->pac->buffers[i]; - } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { - if (p->ticket_checksum) { - ret = EINVAL; - krb5_set_error_message(context, ret, + else + p->upn_dns_info = &p->pac->buffers[i]; + break; + case PAC_TICKET_CHECKSUM: + if (p->ticket_checksum) + krb5_set_error_message(context, ret = EINVAL, N_("PAC has multiple ticket checksums", "")); - goto out; - } - p->ticket_checksum = &p->pac->buffers[i]; - } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) { - if (p->attributes_info) { - ret = EINVAL; - krb5_set_error_message(context, ret, + else + p->ticket_checksum = &p->pac->buffers[i]; + break; + case PAC_ATTRIBUTES_INFO: + if (p->attributes_info) + krb5_set_error_message(context, ret = EINVAL, N_("PAC has multiple attributes info buffers", "")); - goto out; - } - p->attributes_info = &p->pac->buffers[i]; - } + else + p->attributes_info = &p->pac->buffers[i]; + break; + default: break; + } } - ret = krb5_data_copy(&p->data, ptr, len); - if (ret) - goto out; - - krb5_storage_free(sp); - - *pac = p; - return 0; - -out: - if (sp) - krb5_storage_free(sp); - if (p) { - if (p->pac) - free(p->pac); - krb5_pac_free(context, p); + if (ret == 0) + ret = krb5_data_copy(&p->data, ptr, len); + if (ret == 0) { + *pac = p; + p = NULL; } - *pac = NULL; - + if (sp) + krb5_storage_free(sp); + krb5_pac_free(context, p); return ret; } @@ -369,77 +437,111 @@ krb5_pac_init(krb5_context context, krb5_pac *pac) krb5_pac_free(context, p); return krb5_enomem(context); } + memset(p->data.data, 0, p->data.length); *pac = p; return 0; } +/** + * Add a PAC buffer `nd' of type `type' to the pac `p'. + * + * @param context + * @param p + * @param type -- Samba Shared Repository