diff options
| author | tb <> | 2026-01-04 09:51:42 +0000 |
|---|---|---|
| committer | tb <> | 2026-01-04 09:51:42 +0000 |
| commit | f65bd896414ed2e25bad00527fa4f1c5fabdad09 (patch) | |
| tree | 5ab684b0386050cab74af4580e7fb381694ec8e2 | |
| parent | e25ccd667e01e3a9e37f06e7cc79fb7ac0f6e3f4 (diff) | |
| download | openbsd-f65bd896414ed2e25bad00527fa4f1c5fabdad09.tar.gz openbsd-f65bd896414ed2e25bad00527fa4f1c5fabdad09.tar.bz2 openbsd-f65bd896414ed2e25bad00527fa4f1c5fabdad09.zip | |
i2c_ASN1_BIT_STRING() vs ASN1_STRING_FLAG_BITS_LEFT
A nasty quirk in the bit string handling is that the serialization
produced by i2d_ASN1_BIT_STRING() depends on whether the the magic
ASN1_STRING_FLAG_BITS_LEFT is set.
If ASN1_STRING_FLAG_BITS_LEFT is set, the number of unused bits is
carried in a->flags & 0x07 and the remainder of the bit string is
in a->data. This is terrible and undocumented but handled correctly.
If ASN1_STRING_FLAG_BITS_LEFT is not set, all trailing zero bits are
(intended to be) chopped off with all sorts of hilarious side effects.
I broke this quite thoroughly when I incorrectly ported an overflow
check from BoringSSL in:
https://github.com/openbsd/src/commit/f81cc285d2aed8b36615119a306533696f3eb66c
The result is that we currently return ret = a->length + 1 for both NULL
and non-NULL pp. The calls to asn1_ex_i2c() in asn1_i2d_ex_primitive()
thus report consistent lengths back, making it succeed.
asn1_i2d_ex_primitive() therefore skips a->length + 1 bytes, while
i2c_ASN1_BIT_STRING() only overwrites len + 1 bytes, which are possibly
fewer. So a caller passing in an output buffer containing garbage
(malloc) will get some of that garbage back in the encoding. Further,
i2c_ASN1_BIT_STRING() also advances that pointer by the possibly reduced
len + 1, but that fortunately doesn't matter since that's an effect
local to asn1_ex_i2c(), the only caller of i2c_ASN1_BIT_STRING().
The last bit is that the current behavior may set bogus unused bits
coming from the scanning backward madness. I added such an example in
the parent commit.
The fix is simple: use len after the truncation effect was established,
not the original a->length, turning this commit into what my backport
should have been.
This fixes the two currently failing regress tests, so remove expected
failure marker again.
ok jsing kenjiro
| -rw-r--r-- | src/lib/libcrypto/asn1/a_bitstr.c | 19 | ||||
| -rw-r--r-- | src/regress/lib/libcrypto/asn1/Makefile | 4 |
2 files changed, 10 insertions, 13 deletions
diff --git a/src/lib/libcrypto/asn1/a_bitstr.c b/src/lib/libcrypto/asn1/a_bitstr.c index 82fd53231f..9eee97e33d 100644 --- a/src/lib/libcrypto/asn1/a_bitstr.c +++ b/src/lib/libcrypto/asn1/a_bitstr.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: a_bitstr.c,v 1.46 2026/01/04 09:30:57 tb Exp $ */ | 1 | /* $OpenBSD: a_bitstr.c,v 1.47 2026/01/04 09:51:42 tb Exp $ */ |
| 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
| 3 | * All rights reserved. | 3 | * All rights reserved. |
| 4 | * | 4 | * |
| @@ -184,16 +184,7 @@ i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp) | |||
| 184 | if (a == NULL) | 184 | if (a == NULL) |
| 185 | return (0); | 185 | return (0); |
| 186 | 186 | ||
| 187 | if (a->length == INT_MAX) | ||
| 188 | return (0); | ||
| 189 | |||
| 190 | ret = a->length + 1; | ||
| 191 | |||
| 192 | if (pp == NULL) | ||
| 193 | return (ret); | ||
| 194 | |||
| 195 | len = a->length; | 187 | len = a->length; |
| 196 | |||
| 197 | if (len > 0) { | 188 | if (len > 0) { |
| 198 | if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) { | 189 | if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) { |
| 199 | bits = (int)a->flags & 0x07; | 190 | bits = (int)a->flags & 0x07; |
| @@ -227,6 +218,14 @@ i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp) | |||
| 227 | } else | 218 | } else |
| 228 | bits = 0; | 219 | bits = 0; |
| 229 | 220 | ||
| 221 | if (len > INT_MAX - 1) | ||
| 222 | return (0); | ||
| 223 | |||
| 224 | ret = len + 1; | ||
| 225 | |||
| 226 | if (pp == NULL) | ||
| 227 | return (ret); | ||
| 228 | |||
| 230 | p = *pp; | 229 | p = *pp; |
| 231 | 230 | ||
| 232 | *(p++) = (unsigned char)bits; | 231 | *(p++) = (unsigned char)bits; |
diff --git a/src/regress/lib/libcrypto/asn1/Makefile b/src/regress/lib/libcrypto/asn1/Makefile index 1bb9da8585..23a01ab600 100644 --- a/src/regress/lib/libcrypto/asn1/Makefile +++ b/src/regress/lib/libcrypto/asn1/Makefile | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | # $OpenBSD: Makefile,v 1.29 2026/01/04 09:42:32 tb Exp $ | 1 | # $OpenBSD: Makefile,v 1.30 2026/01/04 09:51:42 tb Exp $ |
| 2 | 2 | ||
| 3 | PROGS = \ | 3 | PROGS = \ |
| 4 | asn1api \ | 4 | asn1api \ |
| @@ -22,8 +22,6 @@ CFLAGS+= -Wall -Wundef -Werror | |||
| 22 | CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/asn1 | 22 | CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/asn1 |
| 23 | CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/bytestring | 23 | CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/bytestring |
| 24 | 24 | ||
| 25 | REGRESS_EXPECTED_FAILURES += run-regress-asn1basic | ||
| 26 | |||
| 27 | LDADD_asn1basic = ${CRYPTO_INT} | 25 | LDADD_asn1basic = ${CRYPTO_INT} |
| 28 | LDADD_asn1object = ${CRYPTO_INT} | 26 | LDADD_asn1object = ${CRYPTO_INT} |
| 29 | LDADD_asn1time = ${CRYPTO_INT} | 27 | LDADD_asn1time = ${CRYPTO_INT} |
