diff options
| author | Vijay Anusuri <vanusuri@mvista.com> | 2024-07-03 18:22:07 +0530 |
|---|---|---|
| committer | Armin Kuster <akuster808@gmail.com> | 2024-07-17 20:06:58 -0400 |
| commit | e532396d479fb97dd62be67bee0a640a99f76544 (patch) | |
| tree | 73d0e5efe08ae73a2c1a526cf61625b331aeb623 | |
| parent | 04d41e058acf19b82bf6559d6ccbbed73f9bc4d3 (diff) | |
| download | meta-openembedded-e532396d479fb97dd62be67bee0a640a99f76544.tar.gz | |
krb5: Fix for CVE-2024-37370 and CVE-2024-37371
Upstream-Status: Backport
[https://github.com/krb5/krb5/commit/548da160b52b25a106e9f6077d6a42c2c049586c
&
https://github.com/krb5/krb5/commit/55fbf435edbe2e92dd8101669b1ce7144bc96fef]
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
3 files changed, 708 insertions, 0 deletions
diff --git a/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371-pre1.patch b/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371-pre1.patch new file mode 100644 index 0000000000..36536461a5 --- /dev/null +++ b/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371-pre1.patch | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | From 548da160b52b25a106e9f6077d6a42c2c049586c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Greg Hudson <ghudson@mit.edu> | ||
| 3 | Date: Tue, 7 Mar 2023 00:19:33 -0500 | ||
| 4 | Subject: [PATCH] Add a simple DER support header | ||
| 5 | |||
| 6 | Upstream-Status: Backport from [https://github.com/krb5/krb5/commit/548da160b52b25a106e9f6077d6a42c2c049586c] | ||
| 7 | Comment: to backport fix for CVE-2024-37370 CVE-2024-37371, Add a simple DER support header | ||
| 8 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 9 | --- | ||
| 10 | src/include/k5-der.h | 149 +++++++++++++++++++++++++++++++++++++++++++ | ||
| 11 | 1 file changed, 149 insertions(+) | ||
| 12 | create mode 100644 src/include/k5-der.h | ||
| 13 | |||
| 14 | diff --git a/src/include/k5-der.h b/src/include/k5-der.h | ||
| 15 | new file mode 100644 | ||
| 16 | index 0000000000..b8371d9b4d | ||
| 17 | --- /dev/null | ||
| 18 | +++ b/src/include/k5-der.h | ||
| 19 | @@ -0,0 +1,149 @@ | ||
| 20 | +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ | ||
| 21 | +/* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */ | ||
| 22 | +/* | ||
| 23 | + * Copyright (C) 2023 by the Massachusetts Institute of Technology. | ||
| 24 | + * All rights reserved. | ||
| 25 | + * | ||
| 26 | + * Redistribution and use in source and binary forms, with or without | ||
| 27 | + * modification, are permitted provided that the following conditions | ||
| 28 | + * are met: | ||
| 29 | + * | ||
| 30 | + * * Redistributions of source code must retain the above copyright | ||
| 31 | + * notice, this list of conditions and the following disclaimer. | ||
| 32 | + * | ||
| 33 | + * * Redistributions in binary form must reproduce the above copyright | ||
| 34 | + * notice, this list of conditions and the following disclaimer in | ||
| 35 | + * the documentation and/or other materials provided with the | ||
| 36 | + * distribution. | ||
| 37 | + * | ||
| 38 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 39 | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 40 | + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
| 41 | + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
| 42 | + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
| 43 | + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
| 44 | + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 45 | + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 46 | + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
| 47 | + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 48 | + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
| 49 | + * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 50 | + */ | ||
| 51 | + | ||
| 52 | +/* | ||
| 53 | + * Most ASN.1 encoding and decoding is done using the table-driven framework in | ||
| 54 | + * libkrb5. When that is not an option, these helpers can be used to encode | ||
| 55 | + * and decode simple types. | ||
| 56 | + */ | ||
| 57 | + | ||
| 58 | +#ifndef K5_DER_H | ||
| 59 | +#define K5_DER_H | ||
| 60 | + | ||
| 61 | +#include <stdint.h> | ||
| 62 | +#include <stdbool.h> | ||
| 63 | +#include "k5-buf.h" | ||
| 64 | +#include "k5-input.h" | ||
| 65 | + | ||
| 66 | +/* Return the number of bytes needed to encode len as a DER encoding length. */ | ||
| 67 | +static inline size_t | ||
| 68 | +k5_der_len_len(size_t len) | ||
| 69 | +{ | ||
| 70 | + size_t llen; | ||
| 71 | + | ||
| 72 | + if (len < 128) | ||
| 73 | + return 1; | ||
| 74 | + llen = 1; | ||
| 75 | + while (len > 0) { | ||
| 76 | + len >>= 8; | ||
| 77 | + llen++; | ||
| 78 | + } | ||
| 79 | + return llen; | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +/* Return the number of bytes needed to encode a DER value (with identifier | ||
| 83 | + * byte and length) for a given contents length. */ | ||
| 84 | +static inline size_t | ||
| 85 | +k5_der_value_len(size_t contents_len) | ||
| 86 | +{ | ||
| 87 | + return 1 + k5_der_len_len(contents_len) + contents_len; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +/* Add a DER identifier byte (composed by the caller, including the ASN.1 | ||
| 91 | + * class, tag, and constructed bit) and length. */ | ||
| 92 | +static inline void | ||
| 93 | +k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len) | ||
| 94 | +{ | ||
| 95 | + uint8_t *p; | ||
| 96 | + size_t llen = k5_der_len_len(len); | ||
| 97 | + | ||
| 98 | + p = k5_buf_get_space(buf, 1 + llen); | ||
| 99 | + if (p == NULL) | ||
| 100 | + return; | ||
| 101 | + *p++ = idbyte; | ||
| 102 | + if (len < 128) { | ||
| 103 | + *p = len; | ||
| 104 | + } else { | ||
| 105 | + *p = 0x80 | (llen - 1); | ||
| 106 | + /* Encode the length bytes backwards so the most significant byte is | ||
| 107 | + * first. */ | ||
| 108 | + p += llen; | ||
| 109 | + while (len > 0) { | ||
| 110 | + *--p = len & 0xFF; | ||
| 111 | + len >>= 8; | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +/* Add a DER value (identifier byte, length, and contents). */ | ||
| 117 | +static inline void | ||
| 118 | +k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents, | ||
| 119 | + size_t len) | ||
| 120 | +{ | ||
| 121 | + k5_der_add_taglen(buf, idbyte, len); | ||
| 122 | + k5_buf_add_len(buf, contents, len); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/* | ||
| 126 | + * If the next byte in in matches idbyte and the subsequent DER length is | ||
| 127 | + * valid, advance in past the value, set *contents_out to the value contents, | ||
| 128 | + * and return true. Otherwise return false. Only set an error on in if the | ||
| 129 | + * next bytes matches idbyte but the ensuing length is invalid. contents_out | ||
| 130 | + * may be aliased to in; it will only be written to on successful decoding of a | ||
| 131 | + * value. | ||
| 132 | + */ | ||
| 133 | +static inline bool | ||
| 134 | +k5_der_get_value(struct k5input *in, uint8_t idbyte, | ||
| 135 | + struct k5input *contents_out) | ||
| 136 | +{ | ||
| 137 | + uint8_t lenbyte, i; | ||
| 138 | + size_t len; | ||
| 139 | + const void *bytes; | ||
| 140 | + | ||
| 141 | + /* Do nothing if in is empty or the next byte doesn't match idbyte. */ | ||
| 142 | + if (in->status || in->len == 0 || *in->ptr != idbyte) | ||
| 143 | + return false; | ||
| 144 | + | ||
| 145 | + /* Advance past the identifier byte and decode the length. */ | ||
| 146 | + (void)k5_input_get_byte(in); | ||
| 147 | + lenbyte = k5_input_get_byte(in); | ||
| 148 | + if (lenbyte < 128) { | ||
| 149 | + len = lenbyte; | ||
| 150 | + } else { | ||
| 151 | + len = 0; | ||
| 152 | + for (i = 0; i < (lenbyte & 0x7F); i++) { | ||
| 153 | + if (len > (SIZE_MAX >> 8)) { | ||
| 154 | + k5_input_set_status(in, EOVERFLOW); | ||
| 155 | + return false; | ||
| 156 | + } | ||
| 157 | + len = (len << 8) | k5_input_get_byte(in); | ||
| 158 | + } | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + bytes = k5_input_get_bytes(in, len); | ||
| 162 | + if (bytes == NULL) | ||
| 163 | + return false; | ||
| 164 | + k5_input_init(contents_out, bytes, len); | ||
| 165 | + return true; | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +#endif /* K5_DER_H */ | ||
diff --git a/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371.patch b/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371.patch new file mode 100644 index 0000000000..31db93c42c --- /dev/null +++ b/meta-oe/recipes-connectivity/krb5/krb5/CVE-2024-37370_37371.patch | |||
| @@ -0,0 +1,538 @@ | |||
| 1 | From 55fbf435edbe2e92dd8101669b1ce7144bc96fef Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Greg Hudson <ghudson@mit.edu> | ||
| 3 | Date: Fri, 14 Jun 2024 10:56:12 -0400 | ||
| 4 | Subject: [PATCH] Fix vulnerabilities in GSS message token handling | ||
| 5 | |||
| 6 | In gss_krb5int_unseal_token_v3() and gss_krb5int_unseal_v3_iov(), | ||
| 7 | verify the Extra Count field of CFX wrap tokens against the encrypted | ||
| 8 | header. Reported by Jacob Champion. | ||
| 9 | |||
| 10 | In gss_krb5int_unseal_token_v3(), check for a decrypted plaintext | ||
| 11 | length too short to contain the encrypted header and extra count | ||
| 12 | bytes. Reported by Jacob Champion. | ||
| 13 | |||
| 14 | In kg_unseal_iov_token(), separately track the header IOV length and | ||
| 15 | complete token length when parsing the token's ASN.1 wrapper. This | ||
| 16 | fix contains modified versions of functions from k5-der.h and | ||
| 17 | util_token.c; this duplication will be cleaned up in a future commit. | ||
| 18 | |||
| 19 | CVE-2024-37370: | ||
| 20 | |||
| 21 | In MIT krb5 release 1.3 and later, an attacker can modify the | ||
| 22 | plaintext Extra Count field of a confidential GSS krb5 wrap token, | ||
| 23 | causing the unwrapped token to appear truncated to the application. | ||
| 24 | |||
| 25 | CVE-2024-37371: | ||
| 26 | |||
| 27 | In MIT krb5 release 1.3 and later, an attacker can cause invalid | ||
| 28 | memory reads by sending message tokens with invalid length fields. | ||
| 29 | |||
| 30 | (cherry picked from commit b0a2f8a5365f2eec3e27d78907de9f9d2c80505a) | ||
| 31 | |||
| 32 | ticket: 9128 | ||
| 33 | version_fixed: 1.21.3 | ||
| 34 | |||
| 35 | Upstream-Status: Backport [https://github.com/krb5/krb5/commit/55fbf435edbe2e92dd8101669b1ce7144bc96fef] | ||
| 36 | CVE: CVE-2024-37370 CVE-2024-37371 | ||
| 37 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 38 | --- | ||
| 39 | src/lib/gssapi/krb5/k5sealv3.c | 5 + | ||
| 40 | src/lib/gssapi/krb5/k5sealv3iov.c | 3 +- | ||
| 41 | src/lib/gssapi/krb5/k5unsealiov.c | 80 +++++++++- | ||
| 42 | src/tests/gssapi/t_invalid.c | 233 +++++++++++++++++++++++++----- | ||
| 43 | 4 files changed, 275 insertions(+), 46 deletions(-) | ||
| 44 | |||
| 45 | diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c | ||
| 46 | index 25d9f27..48fc508 100644 | ||
| 47 | --- a/src/lib/gssapi/krb5/k5sealv3.c | ||
| 48 | +++ b/src/lib/gssapi/krb5/k5sealv3.c | ||
| 49 | @@ -409,10 +409,15 @@ gss_krb5int_unseal_token_v3(krb5_context *contextptr, | ||
| 50 | /* Don't use bodysize here! Use the fact that | ||
| 51 | cipher.ciphertext.length has been adjusted to the | ||
| 52 | correct length. */ | ||
| 53 | + if (plain.length < 16 + ec) { | ||
| 54 | + free(plain.data); | ||
| 55 | + goto defective; | ||
| 56 | + } | ||
| 57 | althdr = (unsigned char *)plain.data + plain.length - 16; | ||
| 58 | if (load_16_be(althdr) != KG2_TOK_WRAP_MSG | ||
| 59 | || althdr[2] != ptr[2] | ||
| 60 | || althdr[3] != ptr[3] | ||
| 61 | + || load_16_be(althdr+4) != ec | ||
| 62 | || memcmp(althdr+8, ptr+8, 8)) { | ||
| 63 | free(plain.data); | ||
| 64 | goto defective; | ||
| 65 | diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c | ||
| 66 | index a73edb6..b0b0c0f 100644 | ||
| 67 | --- a/src/lib/gssapi/krb5/k5sealv3iov.c | ||
| 68 | +++ b/src/lib/gssapi/krb5/k5sealv3iov.c | ||
| 69 | @@ -403,9 +403,10 @@ gss_krb5int_unseal_v3_iov(krb5_context context, | ||
| 70 | if (load_16_be(althdr) != KG2_TOK_WRAP_MSG | ||
| 71 | || althdr[2] != ptr[2] | ||
| 72 | || althdr[3] != ptr[3] | ||
| 73 | + || load_16_be(althdr + 4) != ec | ||
| 74 | || memcmp(althdr + 8, ptr + 8, 8) != 0) { | ||
| 75 | *minor_status = 0; | ||
| 76 | - return GSS_S_BAD_SIG; | ||
| 77 | + return GSS_S_DEFECTIVE_TOKEN; | ||
| 78 | } | ||
| 79 | } else { | ||
| 80 | /* Verify checksum: note EC is checksum size here, not padding */ | ||
| 81 | diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c | ||
| 82 | index f15d2db..5cbb896 100644 | ||
| 83 | --- a/src/lib/gssapi/krb5/k5unsealiov.c | ||
| 84 | +++ b/src/lib/gssapi/krb5/k5unsealiov.c | ||
| 85 | @@ -25,6 +25,7 @@ | ||
| 86 | */ | ||
| 87 | |||
| 88 | #include "k5-int.h" | ||
| 89 | +#include "k5-der.h" | ||
| 90 | #include "gssapiP_krb5.h" | ||
| 91 | |||
| 92 | static OM_uint32 | ||
| 93 | @@ -295,6 +296,73 @@ cleanup: | ||
| 94 | return retval; | ||
| 95 | } | ||
| 96 | |||
| 97 | +/* Similar to k5_der_get_value(), but output an unchecked content length | ||
| 98 | + * instead of a k5input containing the contents. */ | ||
| 99 | +static inline bool | ||
| 100 | +get_der_tag(struct k5input *in, uint8_t idbyte, size_t *len_out) | ||
| 101 | +{ | ||
| 102 | + uint8_t lenbyte, i; | ||
| 103 | + size_t len; | ||
| 104 | + | ||
| 105 | + /* Do nothing if in is empty or the next byte doesn't match idbyte. */ | ||
| 106 | + if (in->status || in->len == 0 || *in->ptr != idbyte) | ||
| 107 | + return false; | ||
| 108 | + | ||
| 109 | + /* Advance past the identifier byte and decode the length. */ | ||
| 110 | + (void)k5_input_get_byte(in); | ||
| 111 | + lenbyte = k5_input_get_byte(in); | ||
| 112 | + if (lenbyte < 128) { | ||
| 113 | + len = lenbyte; | ||
| 114 | + } else { | ||
| 115 | + len = 0; | ||
| 116 | + for (i = 0; i < (lenbyte & 0x7F); i++) { | ||
| 117 | + if (len > (SIZE_MAX >> 8)) { | ||
| 118 | + k5_input_set_status(in, EOVERFLOW); | ||
| 119 | + return false; | ||
| 120 | + } | ||
| 121 | + len = (len << 8) | k5_input_get_byte(in); | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + if (in->status) | ||
| 126 | + return false; | ||
| 127 | + | ||
| 128 | + *len_out = len; | ||
| 129 | + return true; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +/* | ||
| 133 | + * Similar to g_verify_token_header() without toktype or flags, but do not read | ||
| 134 | + * more than *header_len bytes of ASN.1 wrapper, and on output set *header_len | ||
| 135 | + * to the remaining number of header bytes. Verify the outer DER tag's length | ||
| 136 | + * against token_len, which may be larger (but not smaller) than *header_len. | ||
| 137 | + */ | ||
| 138 | +static gss_int32 | ||
| 139 | +verify_detached_wrapper(const gss_OID_desc *mech, size_t *header_len, | ||
| 140 | + uint8_t **header_in, size_t token_len) | ||
| 141 | +{ | ||
| 142 | + struct k5input in, mech_der; | ||
| 143 | + gss_OID_desc toid; | ||
| 144 | + size_t len; | ||
| 145 | + | ||
| 146 | + k5_input_init(&in, *header_in, *header_len); | ||
| 147 | + | ||
| 148 | + if (get_der_tag(&in, 0x60, &len)) { | ||
| 149 | + if (len != token_len - (in.ptr - *header_in)) | ||
| 150 | + return G_BAD_TOK_HEADER; | ||
| 151 | + if (!k5_der_get_value(&in, 0x06, &mech_der)) | ||
| 152 | + return G_BAD_TOK_HEADER; | ||
| 153 | + toid.elements = (uint8_t *)mech_der.ptr; | ||
| 154 | + toid.length = mech_der.len; | ||
| 155 | + if (!g_OID_equal(&toid, mech)) | ||
| 156 | + return G_WRONG_MECH; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + *header_in = (uint8_t *)in.ptr; | ||
| 160 | + *header_len = in.len; | ||
| 161 | + return 0; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | /* | ||
| 165 | * Caller must provide TOKEN | DATA | PADDING | TRAILER, except | ||
| 166 | * for DCE in which case it can just provide TOKEN | DATA (must | ||
| 167 | @@ -315,8 +383,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status, | ||
| 168 | gss_iov_buffer_t header; | ||
| 169 | gss_iov_buffer_t padding; | ||
| 170 | gss_iov_buffer_t trailer; | ||
| 171 | - size_t input_length; | ||
| 172 | - unsigned int bodysize; | ||
| 173 | + size_t input_length, hlen; | ||
| 174 | int toktype2; | ||
| 175 | |||
| 176 | header = kg_locate_header_iov(iov, iov_count, toktype); | ||
| 177 | @@ -346,15 +413,14 @@ kg_unseal_iov_token(OM_uint32 *minor_status, | ||
| 178 | input_length += trailer->buffer.length; | ||
| 179 | } | ||
| 180 | |||
| 181 | - code = g_verify_token_header(ctx->mech_used, | ||
| 182 | - &bodysize, &ptr, -1, | ||
| 183 | - input_length, 0); | ||
| 184 | + hlen = header->buffer.length; | ||
| 185 | + code = verify_detached_wrapper(ctx->mech_used, &hlen, &ptr, input_length); | ||
| 186 | if (code != 0) { | ||
| 187 | *minor_status = code; | ||
| 188 | return GSS_S_DEFECTIVE_TOKEN; | ||
| 189 | } | ||
| 190 | |||
| 191 | - if (bodysize < 2) { | ||
| 192 | + if (hlen < 2) { | ||
| 193 | *minor_status = (OM_uint32)G_BAD_TOK_HEADER; | ||
| 194 | return GSS_S_DEFECTIVE_TOKEN; | ||
| 195 | } | ||
| 196 | @@ -362,7 +428,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status, | ||
| 197 | toktype2 = load_16_be(ptr); | ||
| 198 | |||
| 199 | ptr += 2; | ||
| 200 | - bodysize -= 2; | ||
| 201 | + hlen -= 2; | ||
| 202 | |||
| 203 | switch (toktype2) { | ||
| 204 | case KG2_TOK_MIC_MSG: | ||
| 205 | diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c | ||
| 206 | index 2a332a8..e70ace6 100644 | ||
| 207 | --- a/src/tests/gssapi/t_invalid.c | ||
| 208 | +++ b/src/tests/gssapi/t_invalid.c | ||
| 209 | @@ -36,31 +36,41 @@ | ||
| 210 | * | ||
| 211 | * 1. A pre-CFX wrap or MIC token processed with a CFX-only context causes a | ||
| 212 | * null pointer dereference. (The token must use SEAL_ALG_NONE or it will | ||
| 213 | - * be rejected.) | ||
| 214 | + * be rejected.) This vulnerability also applies to IOV unwrap. | ||
| 215 | * | ||
| 216 | - * 2. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1 | ||
| 217 | + * 2. A CFX wrap token with a different value of EC between the plaintext and | ||
| 218 | + * encrypted copies will be erroneously accepted, which allows a message | ||
| 219 | + * truncation attack. This vulnerability also applies to IOV unwrap. | ||
| 220 | + * | ||
| 221 | + * 3. A CFX wrap token with a plaintext length fewer than 16 bytes causes an | ||
| 222 | + * access before the beginning of the input buffer, possibly leading to a | ||
| 223 | + * crash. | ||
| 224 | + * | ||
| 225 | + * 4. A CFX wrap token with a plaintext EC value greater than the plaintext | ||
| 226 | + * length - 16 causes an integer underflow when computing the result length, | ||
| 227 | + * likely causing a crash. | ||
| 228 | + * | ||
| 229 | + * 5. An IOV unwrap operation will overrun the header buffer if an ASN.1 | ||
| 230 | + * wrapper longer than the header buffer is present. | ||
| 231 | + * | ||
| 232 | + * 6. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1 | ||
| 233 | * header causes an input buffer overrun, usually leading to either a segv | ||
| 234 | * or a GSS_S_DEFECTIVE_TOKEN error due to garbage algorithm, filler, or | ||
| 235 | - * sequence number values. | ||
| 236 | + * sequence number values. This vulnerability also applies to IOV unwrap. | ||
| 237 | * | ||
| 238 | - * 3. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1 | ||
| 239 | + * 7. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1 | ||
| 240 | * header causes an integer underflow when computing the ciphertext length, | ||
| 241 | * leading to an allocation error on 32-bit platforms or a segv on 64-bit | ||
| 242 | * platforms. A pre-CFX MIC token of this size causes an input buffer | ||
| 243 | * overrun when comparing the checksum, perhaps leading to a segv. | ||
| 244 | * | ||
| 245 | - * 4. A pre-CFX wrap token with fewer than conflen + padlen bytes in the | ||
| 246 | + * 8. A pre-CFX wrap token with fewer than conflen + padlen bytes in the | ||
| 247 | * ciphertext (where padlen is the last byte of the decrypted ciphertext) | ||
| 248 | * causes an integer underflow when computing the original message length, | ||
| 249 | * leading to an allocation error. | ||
| 250 | * | ||
| 251 | - * 5. In the mechglue, truncated encapsulation in the initial context token can | ||
| 252 | + * 9. In the mechglue, truncated encapsulation in the initial context token can | ||
| 253 | * cause input buffer overruns in gss_accept_sec_context(). | ||
| 254 | - * | ||
| 255 | - * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with | ||
| 256 | - * fewer than 16 bytes after the ASN.1 header will be rejected. | ||
| 257 | - * Vulnerabilities #2 and #5 can only be robustly detected using a | ||
| 258 | - * memory-checking environment such as valgrind. | ||
| 259 | */ | ||
| 260 | |||
| 261 | #include "k5-int.h" | ||
| 262 | @@ -120,17 +130,25 @@ struct test { | ||
| 263 | } | ||
| 264 | }; | ||
| 265 | |||
| 266 | -/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */ | ||
| 267 | +static void * | ||
| 268 | +ealloc(size_t len) | ||
| 269 | +{ | ||
| 270 | + void *ptr = calloc(len, 1); | ||
| 271 | + | ||
| 272 | + if (ptr == NULL) | ||
| 273 | + abort(); | ||
| 274 | + return ptr; | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. | ||
| 278 | + * The context takes ownership of subkey. */ | ||
| 279 | static gss_ctx_id_t | ||
| 280 | -make_fake_cfx_context() | ||
| 281 | +make_fake_cfx_context(krb5_key subkey) | ||
| 282 | { | ||
| 283 | gss_union_ctx_id_t uctx; | ||
| 284 | krb5_gss_ctx_id_t kgctx; | ||
| 285 | - krb5_keyblock kb; | ||
| 286 | |||
| 287 | - kgctx = calloc(1, sizeof(*kgctx)); | ||
| 288 | - if (kgctx == NULL) | ||
| 289 | - abort(); | ||
| 290 | + kgctx = ealloc(sizeof(*kgctx)); | ||
| 291 | kgctx->established = 1; | ||
| 292 | kgctx->proto = 1; | ||
| 293 | if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0) | ||
| 294 | @@ -139,15 +157,10 @@ make_fake_cfx_context() | ||
| 295 | kgctx->sealalg = -1; | ||
| 296 | kgctx->signalg = -1; | ||
| 297 | |||
| 298 | - kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96; | ||
| 299 | - kb.length = 16; | ||
| 300 | - kb.contents = (unsigned char *)"1234567887654321"; | ||
| 301 | - if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0) | ||
| 302 | - abort(); | ||
| 303 | + kgctx->subkey = subkey; | ||
| 304 | + kgctx->cksumtype = CKSUMTYPE_HMAC_SHA1_96_AES128; | ||
| 305 | |||
| 306 | - uctx = calloc(1, sizeof(*uctx)); | ||
| 307 | - if (uctx == NULL) | ||
| 308 | - abort(); | ||
| 309 | + uctx = ealloc(sizeof(*uctx)); | ||
| 310 | uctx->mech_type = &mech_krb5; | ||
| 311 | uctx->internal_ctx_id = (gss_ctx_id_t)kgctx; | ||
| 312 | return (gss_ctx_id_t)uctx; | ||
| 313 | @@ -163,9 +176,7 @@ make_fake_context(const struct test *test) | ||
| 314 | unsigned char encbuf[8]; | ||
| 315 | size_t i; | ||
| 316 | |||
| 317 | - kgctx = calloc(1, sizeof(*kgctx)); | ||
| 318 | - if (kgctx == NULL) | ||
| 319 | - abort(); | ||
| 320 | + kgctx = ealloc(sizeof(*kgctx)); | ||
| 321 | kgctx->established = 1; | ||
| 322 | if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0) | ||
| 323 | abort(); | ||
| 324 | @@ -192,9 +203,7 @@ make_fake_context(const struct test *test) | ||
| 325 | if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0) | ||
| 326 | abort(); | ||
| 327 | |||
| 328 | - uctx = calloc(1, sizeof(*uctx)); | ||
| 329 | - if (uctx == NULL) | ||
| 330 | - abort(); | ||
| 331 | + uctx = ealloc(sizeof(*uctx)); | ||
| 332 | uctx->mech_type = &mech_krb5; | ||
| 333 | uctx->internal_ctx_id = (gss_ctx_id_t)kgctx; | ||
| 334 | return (gss_ctx_id_t)uctx; | ||
| 335 | @@ -224,9 +233,7 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out) | ||
| 336 | |||
| 337 | assert(mech_krb5.length == 9); | ||
| 338 | assert(len + 11 < 128); | ||
| 339 | - wrapped = malloc(len + 13); | ||
| 340 | - if (wrapped == NULL) | ||
| 341 | - abort(); | ||
| 342 | + wrapped = ealloc(len + 13); | ||
| 343 | wrapped[0] = 0x60; | ||
| 344 | wrapped[1] = len + 11; | ||
| 345 | wrapped[2] = 0x06; | ||
| 346 | @@ -237,6 +244,18 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out) | ||
| 347 | out->value = wrapped; | ||
| 348 | } | ||
| 349 | |||
| 350 | +/* Create a 16-byte header for a CFX confidential wrap token to be processed by | ||
| 351 | + * the fake CFX context. */ | ||
| 352 | +static void | ||
| 353 | +write_cfx_header(uint16_t ec, uint8_t *out) | ||
| 354 | +{ | ||
| 355 | + memset(out, 0, 16); | ||
| 356 | + store_16_be(KG2_TOK_WRAP_MSG, out); | ||
| 357 | + out[2] = FLAG_WRAP_CONFIDENTIAL; | ||
| 358 | + out[3] = 0xFF; | ||
| 359 | + store_16_be(ec, out + 4); | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | /* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with | ||
| 363 | * regular and IOV unwrap. */ | ||
| 364 | static void | ||
| 365 | @@ -268,6 +287,134 @@ test_bogus_1964_token(gss_ctx_id_t ctx) | ||
| 366 | free(in.value); | ||
| 367 | } | ||
| 368 | |||
| 369 | +static void | ||
| 370 | +test_cfx_altered_ec(gss_ctx_id_t ctx, krb5_key subkey) | ||
| 371 | +{ | ||
| 372 | + OM_uint32 major, minor; | ||
| 373 | + uint8_t tokbuf[128], plainbuf[24]; | ||
| 374 | + krb5_data plain; | ||
| 375 | + krb5_enc_data cipher; | ||
| 376 | + gss_buffer_desc in, out; | ||
| 377 | + gss_iov_buffer_desc iov[2]; | ||
| 378 | + | ||
| 379 | + /* Construct a header with a plaintext EC value of 3. */ | ||
| 380 | + write_cfx_header(3, tokbuf); | ||
| 381 | + | ||
| 382 | + /* Encrypt a plaintext and a copy of the header with the EC value 0. */ | ||
| 383 | + memcpy(plainbuf, "truncate", 8); | ||
| 384 | + memcpy(plainbuf + 8, tokbuf, 16); | ||
| 385 | + store_16_be(0, plainbuf + 12); | ||
| 386 | + plain = make_data(plainbuf, 24); | ||
| 387 | + cipher.ciphertext.data = (char *)tokbuf + 16; | ||
| 388 | + cipher.ciphertext.length = sizeof(tokbuf) - 16; | ||
| 389 | + cipher.enctype = subkey->keyblock.enctype; | ||
| 390 | + if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL, | ||
| 391 | + &plain, &cipher) != 0) | ||
| 392 | + abort(); | ||
| 393 | + | ||
| 394 | + /* Verify that the token is rejected by gss_unwrap(). */ | ||
| 395 | + in.value = tokbuf; | ||
| 396 | + in.length = 16 + cipher.ciphertext.length; | ||
| 397 | + major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); | ||
| 398 | + if (major != GSS_S_DEFECTIVE_TOKEN) | ||
| 399 | + abort(); | ||
| 400 | + (void)gss_release_buffer(&minor, &out); | ||
| 401 | + | ||
| 402 | + /* Verify that the token is rejected by gss_unwrap_iov(). */ | ||
| 403 | + iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM; | ||
| 404 | + iov[0].buffer = in; | ||
| 405 | + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; | ||
| 406 | + major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2); | ||
| 407 | + if (major != GSS_S_DEFECTIVE_TOKEN) | ||
| 408 | + abort(); | ||
| 409 | +} | ||
| 410 | + | ||
| 411 | +static void | ||
| 412 | +test_cfx_short_plaintext(gss_ctx_id_t ctx, krb5_key subkey) | ||
| 413 | +{ | ||
| 414 | + OM_uint32 major, minor; | ||
| 415 | + uint8_t tokbuf[128], zerobyte = 0; | ||
| 416 | + krb5_data plain; | ||
| 417 | + krb5_enc_data cipher; | ||
| 418 | + gss_buffer_desc in, out; | ||
| 419 | + | ||
| 420 | + write_cfx_header(0, tokbuf); | ||
| 421 | + | ||
| 422 | + /* Encrypt a single byte, with no copy of the header. */ | ||
| 423 | + plain = make_data(&zerobyte, 1); | ||
| 424 | + cipher.ciphertext.data = (char *)tokbuf + 16; | ||
| 425 | + cipher.ciphertext.length = sizeof(tokbuf) - 16; | ||
| 426 | + cipher.enctype = subkey->keyblock.enctype; | ||
| 427 | + if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL, | ||
| 428 | + &plain, &cipher) != 0) | ||
| 429 | + abort(); | ||
| 430 | + | ||
| 431 | + /* Verify that the token is rejected by gss_unwrap(). */ | ||
| 432 | + in.value = tokbuf; | ||
| 433 | + in.length = 16 + cipher.ciphertext.length; | ||
| 434 | + major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); | ||
| 435 | + if (major != GSS_S_DEFECTIVE_TOKEN) | ||
| 436 | + abort(); | ||
| 437 | + (void)gss_release_buffer(&minor, &out); | ||
| 438 | +} | ||
| 439 | + | ||
| 440 | +static void | ||
| 441 | +test_cfx_large_ec(gss_ctx_id_t ctx, krb5_key subkey) | ||
| 442 | +{ | ||
| 443 | + OM_uint32 major, minor; | ||
| 444 | + uint8_t tokbuf[128] = { 0 }, plainbuf[20]; | ||
| 445 | + krb5_data plain; | ||
| 446 | + krb5_enc_data cipher; | ||
| 447 | + gss_buffer_desc in, out; | ||
| 448 | + | ||
| 449 | + /* Construct a header with an EC value of 5. */ | ||
| 450 | + write_cfx_header(5, tokbuf); | ||
| 451 | + | ||
| 452 | + /* Encrypt a 4-byte plaintext plus the header. */ | ||
| 453 | + memcpy(plainbuf, "abcd", 4); | ||
| 454 | + memcpy(plainbuf + 4, tokbuf, 16); | ||
| 455 | + plain = make_data(plainbuf, 20); | ||
| 456 | + cipher.ciphertext.data = (char *)tokbuf + 16; | ||
| 457 | + cipher.ciphertext.length = sizeof(tokbuf) - 16; | ||
| 458 | + cipher.enctype = subkey->keyblock.enctype; | ||
| 459 | + if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL, | ||
| 460 | + &plain, &cipher) != 0) | ||
| 461 | + abort(); | ||
| 462 | + | ||
| 463 | + /* Verify that the token is rejected by gss_unwrap(). */ | ||
| 464 | + in.value = tokbuf; | ||
| 465 | + in.length = 16 + cipher.ciphertext.length; | ||
| 466 | + major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); | ||
| 467 | + if (major != GSS_S_DEFECTIVE_TOKEN) | ||
| 468 | + abort(); | ||
| 469 | + (void)gss_release_buffer(&minor, &out); | ||
| 470 | +} | ||
| 471 | + | ||
| 472 | +static void | ||
| 473 | +test_iov_large_asn1_wrapper(gss_ctx_id_t ctx) | ||
| 474 | +{ | ||
| 475 | + OM_uint32 minor, major; | ||
| 476 | + uint8_t databuf[10] = { 0 }; | ||
| 477 | + gss_iov_buffer_desc iov[2]; | ||
| 478 | + | ||
| 479 | + /* | ||
| 480 | + * In this IOV array, the header contains a DER tag with a dangling eight | ||
| 481 | + * bytes of length field. The data IOV indicates a total token length | ||
| 482 | + * sufficient to contain the length bytes. | ||
| 483 | + */ | ||
| 484 | + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; | ||
| 485 | + iov[0].buffer.value = ealloc(2); | ||
| 486 | + iov[0].buffer.length = 2; | ||
| 487 | + memcpy(iov[0].buffer.value, "\x60\x88", 2); | ||
| 488 | + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; | ||
| 489 | + iov[1].buffer.value = databuf; | ||
| 490 | + iov[1].buffer.length = 10; | ||
| 491 | + major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2); | ||
| 492 | + if (major != GSS_S_DEFECTIVE_TOKEN) | ||
| 493 | + abort(); | ||
| 494 | + free(iov[0].buffer.value); | ||
| 495 | +} | ||
| 496 | + | ||
| 497 | /* Process wrap and MIC tokens with incomplete headers. */ | ||
| 498 | static void | ||
| 499 | test_short_header(gss_ctx_id_t ctx) | ||
| 500 | @@ -417,9 +564,7 @@ try_accept(void *value, size_t len) | ||
| 501 | gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; | ||
| 502 | |||
| 503 | /* Copy the provided value to make input overruns more obvious. */ | ||
| 504 | - in.value = malloc(len); | ||
| 505 | - if (in.value == NULL) | ||
| 506 | - abort(); | ||
| 507 | + in.value = ealloc(len); | ||
| 508 | memcpy(in.value, value, len); | ||
| 509 | in.length = len; | ||
| 510 | (void)gss_accept_sec_context(&minor, &ctx, GSS_C_NO_CREDENTIAL, &in, | ||
| 511 | @@ -454,11 +599,23 @@ test_short_encapsulation() | ||
| 512 | int | ||
| 513 | main(int argc, char **argv) | ||
| 514 | { | ||
| 515 | + krb5_keyblock kb; | ||
| 516 | + krb5_key cfx_subkey; | ||
| 517 | gss_ctx_id_t ctx; | ||
| 518 | size_t i; | ||
| 519 | |||
| 520 | - ctx = make_fake_cfx_context(); | ||
| 521 | + kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96; | ||
| 522 | + kb.length = 16; | ||
| 523 | + kb.contents = (unsigned char *)"1234567887654321"; | ||
| 524 | + if (krb5_k_create_key(NULL, &kb, &cfx_subkey) != 0) | ||
| 525 | + abort(); | ||
| 526 | + | ||
| 527 | + ctx = make_fake_cfx_context(cfx_subkey); | ||
| 528 | test_bogus_1964_token(ctx); | ||
| 529 | + test_cfx_altered_ec(ctx, cfx_subkey); | ||
| 530 | + test_cfx_short_plaintext(ctx, cfx_subkey); | ||
| 531 | + test_cfx_large_ec(ctx, cfx_subkey); | ||
| 532 | + test_iov_large_asn1_wrapper(ctx); | ||
| 533 | free_fake_context(ctx); | ||
| 534 | |||
| 535 | for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { | ||
| 536 | -- | ||
| 537 | 2.25.1 | ||
| 538 | |||
diff --git a/meta-oe/recipes-connectivity/krb5/krb5_1.17.2.bb b/meta-oe/recipes-connectivity/krb5/krb5_1.17.2.bb index a92066171b..ef256179fe 100644 --- a/meta-oe/recipes-connectivity/krb5/krb5_1.17.2.bb +++ b/meta-oe/recipes-connectivity/krb5/krb5_1.17.2.bb | |||
| @@ -34,6 +34,8 @@ SRC_URI = "http://web.mit.edu/kerberos/dist/${BPN}/${SHRT_VER}/${BP}.tar.gz \ | |||
| 34 | file://CVE-2021-37750.patch;striplevel=2 \ | 34 | file://CVE-2021-37750.patch;striplevel=2 \ |
| 35 | file://CVE-2022-42898.patch;striplevel=2 \ | 35 | file://CVE-2022-42898.patch;striplevel=2 \ |
| 36 | file://CVE-2023-36054.patch;striplevel=2 \ | 36 | file://CVE-2023-36054.patch;striplevel=2 \ |
| 37 | file://CVE-2024-37370_37371-pre1.patch;striplevel=2 \ | ||
| 38 | file://CVE-2024-37370_37371.patch;striplevel=2 \ | ||
| 37 | " | 39 | " |
| 38 | SRC_URI[md5sum] = "aa4337fffa3b61f22dbd0167f708818f" | 40 | SRC_URI[md5sum] = "aa4337fffa3b61f22dbd0167f708818f" |
| 39 | SRC_URI[sha256sum] = "1a4bba94df92f6d39a197a10687653e8bfbc9a2076e129f6eb92766974f86134" | 41 | SRC_URI[sha256sum] = "1a4bba94df92f6d39a197a10687653e8bfbc9a2076e129f6eb92766974f86134" |
