[31534] in CVS-changelog-for-Kerberos-V5
krb5 commit: Validate lengths when deserializing
daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Sat May 16 18:37:07 2026
From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20260516223659.3F658104B08@krbdev.mit.edu>
Date: Sat, 16 May 2026 18:36:59 -0400 (EDT)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu
https://github.com/krb5/krb5/commit/63ae6a8d99ce89258d732f7561233f60df533fa9
commit 63ae6a8d99ce89258d732f7561233f60df533fa9
Author: TristanInSec <tristan.mtn@gmail.com>
Date: Tue May 12 05:42:36 2026 -0400
Validate lengths when deserializing
When unmarshalling data structures using the krb5_ser_ functions,
bound-check lengths (including array counts) against the remaining
number of bytes to prevent large allocations, integer overflows, and a
potential read overrun in mspac_internalize(). Add an internal helper
function k5_ser_unpack_len() for this purpose.
[ghudson@mit.edu: added helper; added bounds checks for additional
lengths; rewrote commit message]
ticket: 9209 (new)
tags: pullup
target_version: 1.22-next
src/include/k5-int.h | 3 +++
src/lib/gssapi/krb5/ser_sctx.c | 6 ++++++
src/lib/krb5/krb/authdata.c | 5 ++---
src/lib/krb5/krb/pac.c | 13 ++++++-------
src/lib/krb5/krb/ser_actx.c | 5 ++---
src/lib/krb5/krb/ser_adata.c | 13 ++++++-------
src/lib/krb5/krb/ser_addr.c | 12 ++++++------
src/lib/krb5/krb/ser_auth.c | 15 +++++----------
src/lib/krb5/krb/ser_cksum.c | 14 ++++++--------
src/lib/krb5/krb/ser_ctx.c | 18 +++++++++---------
src/lib/krb5/krb/ser_key.c | 11 +++++------
src/lib/krb5/krb/ser_princ.c | 11 +++++------
src/lib/krb5/krb/serialize.c | 18 ++++++++++++++++++
src/lib/krb5/libkrb5.exports | 1 +
src/plugins/authdata/greet_client/greet.c | 2 ++
15 files changed, 82 insertions(+), 65 deletions(-)
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 56d9d1b20..20611d067 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1882,6 +1882,9 @@ krb5_ser_pack_int64(int64_t, krb5_octet **, size_t *);
krb5_error_code KRB5_CALLCONV
krb5_ser_unpack_int64(int64_t *, krb5_octet **, size_t *);
+krb5_error_code
+k5_ser_unpack_len(size_t *len_out, krb5_octet **bufp, size_t *remainp);
+
/* [De]serialize byte string */
krb5_error_code KRB5_CALLCONV
krb5_ser_pack_bytes(krb5_octet *, size_t, krb5_octet **, size_t *);
diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c
index 1129b6a1a..2e82be903 100644
--- a/src/lib/gssapi/krb5/ser_sctx.c
+++ b/src/lib/gssapi/krb5/ser_sctx.c
@@ -84,6 +84,10 @@ kg_oid_internalize(gss_OID *argp, krb5_octet **buffer, size_t *lenremain)
free(oid);
return EINVAL;
}
+ if (ibuf < 0 || (size_t)ibuf > remain) {
+ free(oid);
+ return EINVAL;
+ }
oid->length = ibuf;
oid->elements = malloc((size_t)ibuf);
if (oid->elements == 0) {
@@ -632,6 +636,8 @@ kg_ctx_internalize(krb5_context kcontext, krb5_gss_ctx_id_t *argp,
/* authdata */
if (!kret)
kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (!kret && (ibuf < 0 || (size_t)ibuf > remain))
+ kret = ENOMEM;
if (!kret) {
krb5_int32 nadata = ibuf, i;
diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c
index 07afe046d..30c3d618d 100644
--- a/src/lib/krb5/krb/authdata.c
+++ b/src/lib/krb5/krb/authdata.c
@@ -314,14 +314,13 @@ k5_ad_internalize(krb5_context kcontext,
size_t *lenremain)
{
krb5_error_code code = 0;
- krb5_int32 i, count;
krb5_octet *bp;
- size_t remain;
+ size_t remain, count, i;
bp = *buffer;
remain = *lenremain;
- code = krb5_ser_unpack_int32(&count, &bp, &remain);
+ code = k5_ser_unpack_len(&count, &bp, &remain);
if (code != 0)
return code;
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index 1232ce7fa..9b5c9d418 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -1196,24 +1196,23 @@ mspac_internalize(krb5_context context, krb5_authdata_context actx,
krb5_error_code ret;
int32_t ibuf;
uint8_t *bp;
- size_t remain;
+ size_t remain, len;
krb5_pac pac = NULL;
bp = *buffer;
remain = *lenremain;
- /* length */
- ret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ ret = k5_ser_unpack_len(&len, &bp, &remain);
if (ret)
return ret;
- if (ibuf != 0) {
- ret = krb5_pac_parse(context, bp, ibuf, &pac);
+ if (len > 0) {
+ ret = krb5_pac_parse(context, bp, len, &pac);
if (ret)
return ret;
- bp += ibuf;
- remain -= ibuf;
+ bp += len;
+ remain -= len;
}
/* verified */
diff --git a/src/lib/krb5/krb/ser_actx.c b/src/lib/krb5/krb/ser_actx.c
index 01089e4f7..edffe6835 100644
--- a/src/lib/krb5/krb/ser_actx.c
+++ b/src/lib/krb5/krb/ser_actx.c
@@ -255,8 +255,7 @@ k5_internalize_auth_context(krb5_auth_context *argp,
krb5_auth_context auth_context;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
- krb5_int32 cstate_len;
+ size_t remain, cstate_len;
krb5_int32 tag;
bp = *buffer;
@@ -294,7 +293,7 @@ k5_internalize_auth_context(krb5_auth_context *argp,
auth_context->safe_cksumtype = (krb5_cksumtype) ibuf;
/* Get length of cstate */
- (void) krb5_ser_unpack_int32(&cstate_len, &bp, &remain);
+ (void) k5_ser_unpack_len(&cstate_len, &bp, &remain);
if (cstate_len) {
kret = alloc_data(&auth_context->cstate, cstate_len);
diff --git a/src/lib/krb5/krb/ser_adata.c b/src/lib/krb5/krb/ser_adata.c
index 2c0094df2..9a6237f70 100644
--- a/src/lib/krb5/krb/ser_adata.c
+++ b/src/lib/krb5/krb/ser_adata.c
@@ -102,7 +102,7 @@ k5_internalize_authdata(krb5_authdata **argp,
krb5_authdata *authdata;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
+ size_t remain, len;
bp = *buffer;
remain = *lenremain;
@@ -122,14 +122,13 @@ k5_internalize_authdata(krb5_authdata **argp,
authdata->ad_type = (krb5_authdatatype) ibuf;
/* Get the length */
- (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- authdata->length = (int) ibuf;
+ (void) k5_ser_unpack_len(&len, &bp, &remain);
+ authdata->length = len;
/* Get the string */
- if ((authdata->contents = (krb5_octet *)
- malloc((size_t) (ibuf))) &&
- !(kret = krb5_ser_unpack_bytes(authdata->contents,
- (size_t) ibuf,
+ authdata->contents = malloc(len);
+ if (authdata->contents != NULL &&
+ !(kret = krb5_ser_unpack_bytes(authdata->contents, len,
&bp, &remain))) {
if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
ibuf = 0;
diff --git a/src/lib/krb5/krb/ser_addr.c b/src/lib/krb5/krb/ser_addr.c
index 52aa6f2a8..ed849b314 100644
--- a/src/lib/krb5/krb/ser_addr.c
+++ b/src/lib/krb5/krb/ser_addr.c
@@ -103,7 +103,7 @@ k5_internalize_address(krb5_address **argp,
krb5_address *address;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
+ size_t remain, len;
bp = *buffer;
remain = *lenremain;
@@ -125,13 +125,13 @@ k5_internalize_address(krb5_address **argp,
address->addrtype = (krb5_addrtype) ibuf;
/* Get the length */
- (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- address->length = (int) ibuf;
+ (void) k5_ser_unpack_len(&len, &bp, &remain);
+ address->length = len;
/* Get the string */
- if ((address->contents = (krb5_octet *) malloc((size_t) (ibuf))) &&
- !(kret = krb5_ser_unpack_bytes(address->contents,
- (size_t) ibuf,
+ address->contents = malloc(len);
+ if (address->contents != NULL &&
+ !(kret = krb5_ser_unpack_bytes(address->contents, len,
&bp, &remain))) {
/* Get the trailer */
if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
diff --git a/src/lib/krb5/krb/ser_auth.c b/src/lib/krb5/krb/ser_auth.c
index f835a793b..27d5d87e7 100644
--- a/src/lib/krb5/krb/ser_auth.c
+++ b/src/lib/krb5/krb/ser_auth.c
@@ -168,10 +168,7 @@ k5_internalize_authenticator(krb5_authenticator **argp,
krb5_authenticator *authenticator;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
- int i;
- krb5_int32 nadata;
- size_t len;
+ size_t remain, len, i;
bp = *buffer;
remain = *lenremain;
@@ -224,14 +221,12 @@ k5_internalize_authenticator(krb5_authenticator **argp,
}
/* Attempt to read in the authorization data count */
- if (!(kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) {
- nadata = ibuf;
- len = (size_t) (nadata + 1);
-
+ kret = k5_ser_unpack_len(&len, &bp, &remain);
+ if (!kret) {
/* Get memory for the authorization data pointers */
if ((authenticator->authorization_data = (krb5_authdata **)
- calloc(len, sizeof(krb5_authdata *)))) {
- for (i=0; !kret && (i<nadata); i++) {
+ calloc(len + 1, sizeof(krb5_authdata *)))) {
+ for (i = 0; !kret && i < len; i++) {
kret = k5_internalize_authdata(&authenticator->
authorization_data[i],
&bp, &remain);
diff --git a/src/lib/krb5/krb/ser_cksum.c b/src/lib/krb5/krb/ser_cksum.c
index de8cb2b9e..3bba753ae 100644
--- a/src/lib/krb5/krb/ser_cksum.c
+++ b/src/lib/krb5/krb/ser_cksum.c
@@ -103,7 +103,7 @@ k5_internalize_checksum(krb5_checksum **argp,
krb5_checksum *checksum;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
+ size_t remain, len;
bp = *buffer;
remain = *lenremain;
@@ -122,15 +122,13 @@ k5_internalize_checksum(krb5_checksum **argp,
checksum->checksum_type = (krb5_cksumtype) ibuf;
/* Get the length */
- (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- checksum->length = (int) ibuf;
+ (void) k5_ser_unpack_len(&len, &bp, &remain);
+ checksum->length = len;
/* Get the string */
- if (!ibuf ||
- ((checksum->contents = (krb5_octet *)
- malloc((size_t) (ibuf))) &&
- !(kret = krb5_ser_unpack_bytes(checksum->contents,
- (size_t) ibuf,
+ if (!len ||
+ ((checksum->contents = malloc(len)) &&
+ !(kret = krb5_ser_unpack_bytes(checksum->contents, len,
&bp, &remain)))) {
/* Get the trailer */
diff --git a/src/lib/krb5/krb/ser_ctx.c b/src/lib/krb5/krb/ser_ctx.c
index 5f266a3df..aab192083 100644
--- a/src/lib/krb5/krb/ser_ctx.c
+++ b/src/lib/krb5/krb/ser_ctx.c
@@ -211,8 +211,7 @@ k5_internalize_context(krb5_context *argp,
krb5_context context;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
- unsigned int i, count;
+ size_t remain, len, count, i;
bp = *buffer;
remain = *lenremain;
@@ -230,28 +229,29 @@ k5_internalize_context(krb5_context *argp,
return (ENOMEM);
/* Get the size of the default realm */
- if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
+ kret = k5_ser_unpack_len(&len, &bp, &remain);
+ if (kret)
goto cleanup;
- if (ibuf) {
- context->default_realm = (char *) malloc((size_t) ibuf+1);
+ if (len > 0) {
+ context->default_realm = malloc(len + 1);
if (!context->default_realm) {
kret = ENOMEM;
goto cleanup;
}
kret = krb5_ser_unpack_bytes((krb5_octet *) context->default_realm,
- (size_t) ibuf, &bp, &remain);
+ len, &bp, &remain);
if (kret)
goto cleanup;
- context->default_realm[ibuf] = '\0';
+ context->default_realm[len] = '\0';
}
/* Get the tgs_etypes */
- if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
+ kret = k5_ser_unpack_len(&count, &bp, &remain);
+ if (kret)
goto cleanup;
- count = ibuf;
if (count > 0) {
context->tgs_etypes = calloc(count + 1, sizeof(krb5_enctype));
if (!context->tgs_etypes) {
diff --git a/src/lib/krb5/krb/ser_key.c b/src/lib/krb5/krb/ser_key.c
index 7a294d680..49526cd62 100644
--- a/src/lib/krb5/krb/ser_key.c
+++ b/src/lib/krb5/krb/ser_key.c
@@ -99,7 +99,7 @@ k5_internalize_keyblock(krb5_keyblock **argp,
krb5_keyblock *keyblock;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
+ size_t remain, len;
bp = *buffer;
remain = *lenremain;
@@ -118,13 +118,12 @@ k5_internalize_keyblock(krb5_keyblock **argp,
keyblock->enctype = (krb5_enctype) ibuf;
/* Get the length */
- (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- keyblock->length = (int) ibuf;
+ (void) k5_ser_unpack_len(&len, &bp, &remain);
+ keyblock->length = len;
/* Get the string */
- if ((keyblock->contents = (krb5_octet *) malloc((size_t) (ibuf)))&&
- !(kret = krb5_ser_unpack_bytes(keyblock->contents,
- (size_t) ibuf,
+ if ((keyblock->contents = malloc(len)) &&
+ !(kret = krb5_ser_unpack_bytes(keyblock->contents, len,
&bp, &remain))) {
kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
if (!kret && (ibuf == KV5M_KEYBLOCK)) {
diff --git a/src/lib/krb5/krb/ser_princ.c b/src/lib/krb5/krb/ser_princ.c
index fc5649979..c2f7d7b28 100644
--- a/src/lib/krb5/krb/ser_princ.c
+++ b/src/lib/krb5/krb/ser_princ.c
@@ -92,7 +92,7 @@ k5_internalize_principal(krb5_principal *argp,
krb5_principal principal = NULL;
krb5_int32 ibuf;
krb5_octet *bp;
- size_t remain;
+ size_t remain, len;
char *tmpname = NULL;
*argp = NULL;
@@ -104,15 +104,14 @@ k5_internalize_principal(krb5_principal *argp,
return EINVAL;
/* Read the principal name */
- kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ kret = k5_ser_unpack_len(&len, &bp, &remain);
if (kret)
return kret;
- tmpname = malloc(ibuf + 1);
- kret = krb5_ser_unpack_bytes((krb5_octet *) tmpname, (size_t) ibuf,
- &bp, &remain);
+ tmpname = malloc(len + 1);
+ kret = krb5_ser_unpack_bytes((krb5_octet *) tmpname, len, &bp, &remain);
if (kret)
goto cleanup;
- tmpname[ibuf] = '\0';
+ tmpname[len] = '\0';
/* Parse the name to a principal structure */
kret = krb5_parse_name_flags(NULL, tmpname,
diff --git a/src/lib/krb5/krb/serialize.c b/src/lib/krb5/krb/serialize.c
index e5aee839b..707fdc1a8 100644
--- a/src/lib/krb5/krb/serialize.c
+++ b/src/lib/krb5/krb/serialize.c
@@ -108,6 +108,24 @@ krb5_ser_unpack_int64(int64_t *intp, krb5_octet **bufp, size_t *remainp)
return(ENOMEM);
}
+/* Unpack a 4-byte integer and verify that it is no larger than the number of
+ * remaining bytes. */
+krb5_error_code
+k5_ser_unpack_len(size_t *len_out, krb5_octet **bufp, size_t *remainp)
+{
+ krb5_error_code ret;
+ int32_t n;
+
+ *len_out = 0;
+ ret = krb5_ser_unpack_int32(&n, bufp, remainp);
+ if (ret)
+ return ret;
+ if (n < 0 || (size_t)n > *remainp)
+ return ENOMEM;
+ *len_out = n;
+ return 0;
+}
+
/*
* krb5_ser_unpack_bytes() - Unpack a byte string if it's there.
*/
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 1e7076d3c..107e139e2 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -177,6 +177,7 @@ k5_rc_close
k5_rc_get_name
k5_rc_resolve
k5_rc_store
+k5_ser_unpack_len
k5_size_auth_context
k5_size_authdata
k5_size_authdata_context
diff --git a/src/plugins/authdata/greet_client/greet.c b/src/plugins/authdata/greet_client/greet.c
index ec902d581..3d1a9570c 100644
--- a/src/plugins/authdata/greet_client/greet.c
+++ b/src/plugins/authdata/greet_client/greet.c
@@ -349,6 +349,8 @@ greet_internalize(krb5_context kcontext,
code = krb5_ser_unpack_int32(&length, &bp, &remain);
if (code != 0)
return code;
+ if (length < 0 || (size_t)length > remain)
+ return ENOMEM;
/* Greeting Contents */
if (length != 0) {
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5