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 /src | |
| 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
Diffstat (limited to 'src')
| -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} |
