summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2026-01-04 09:51:42 +0000
committertb <>2026-01-04 09:51:42 +0000
commitf65bd896414ed2e25bad00527fa4f1c5fabdad09 (patch)
tree5ab684b0386050cab74af4580e7fb381694ec8e2 /src
parente25ccd667e01e3a9e37f06e7cc79fb7ac0f6e3f4 (diff)
downloadopenbsd-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.c19
-rw-r--r--src/regress/lib/libcrypto/asn1/Makefile4
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
3PROGS = \ 3PROGS = \
4 asn1api \ 4 asn1api \
@@ -22,8 +22,6 @@ CFLAGS+= -Wall -Wundef -Werror
22CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/asn1 22CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/asn1
23CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/bytestring 23CFLAGS+= -I${.CURDIR}/../../../../lib/libcrypto/bytestring
24 24
25REGRESS_EXPECTED_FAILURES += run-regress-asn1basic
26
27LDADD_asn1basic = ${CRYPTO_INT} 25LDADD_asn1basic = ${CRYPTO_INT}
28LDADD_asn1object = ${CRYPTO_INT} 26LDADD_asn1object = ${CRYPTO_INT}
29LDADD_asn1time = ${CRYPTO_INT} 27LDADD_asn1time = ${CRYPTO_INT}