Reimplement OBJ_obj2txt.

The old implementation had a lot of size_t/int confusion. It also
accepted non-minimally-encoded OIDs. Unlike the old implementation, the
new one does not fall back to BIGNUMs and does not attempt to
pretty-print OIDs with components which do not fit in a uint64_t. Add
tests for these cases.

With this new implementation, hopefully we'll have a much easier time
enabling MSVC's size_t truncation warning later.

Change-Id: I602102b97cf9b02d874644f8ef67fe9bac70e45e
Reviewed-on: https://boringssl-review.googlesource.com/9131
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin 2016-08-04 17:21:36 -04:00 committed by Adam Langley
parent 253c05e16b
commit 2f8ea545a6
2 changed files with 137 additions and 126 deletions

View File

@ -54,8 +54,13 @@
* copied and put under another distribution licence
* [including the GNU Public Licence.] */
#if !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS
#endif
#include <openssl/obj.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
@ -409,148 +414,109 @@ ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
return op;
}
int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj, int dont_return_name) {
int i, n = 0, len, nid, first, use_bn;
BIGNUM *bl;
unsigned long l;
const unsigned char *p;
char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2];
if (out && out_len > 0) {
out[0] = 0;
static int strlcpy_int(char *dst, const char *src, int dst_size) {
size_t ret = BUF_strlcpy(dst, src, dst_size < 0 ? 0 : (size_t)dst_size);
if (ret > INT_MAX) {
OPENSSL_PUT_ERROR(OBJ, ERR_R_OVERFLOW);
return -1;
}
return (int)ret;
}
if (obj == NULL || obj->data == NULL) {
return 0;
}
if (!dont_return_name && (nid = OBJ_obj2nid(obj)) != NID_undef) {
const char *s;
s = OBJ_nid2ln(nid);
if (s == NULL) {
s = OBJ_nid2sn(nid);
static int parse_oid_component(CBS *cbs, uint64_t *out) {
uint64_t v = 0;
uint8_t b;
do {
if (!CBS_get_u8(cbs, &b)) {
return 0;
}
if (s) {
if (out) {
BUF_strlcpy(out, s, out_len);
if ((v >> (64 - 7)) != 0) {
/* The component is too large. */
return 0;
}
if (v == 0 && b == 0x80) {
/* The component must be minimally encoded. */
return 0;
}
v = (v << 7) | (b & 0x7f);
/* Components end at an octet with the high bit cleared. */
} while (b & 0x80);
*out = v;
return 1;
}
static int add_decimal(CBB *out, uint64_t v) {
char buf[DECIMAL_SIZE(uint64_t) + 1];
BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
}
int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
int dont_return_name) {
if (!dont_return_name) {
int nid = OBJ_obj2nid(obj);
if (nid != NID_undef) {
const char *name = OBJ_nid2ln(nid);
if (name == NULL) {
name = OBJ_nid2sn(nid);
}
if (name != NULL) {
return strlcpy_int(out, name, out_len);
}
return strlen(s);
}
}
len = obj->length;
p = obj->data;
CBB cbb;
if (!CBB_init(&cbb, 32)) {
goto err;
}
first = 1;
bl = NULL;
CBS cbs;
CBS_init(&cbs, obj->data, obj->length);
while (len > 0) {
l = 0;
use_bn = 0;
for (;;) {
unsigned char c = *p++;
len--;
if (len == 0 && (c & 0x80)) {
goto err;
}
if (use_bn) {
if (!BN_add_word(bl, c & 0x7f)) {
goto err;
}
} else {
l |= c & 0x7f;
}
if (!(c & 0x80)) {
break;
}
if (!use_bn && (l > (ULONG_MAX >> 7L))) {
if (!bl && !(bl = BN_new())) {
goto err;
}
if (!BN_set_word(bl, l)) {
goto err;
}
use_bn = 1;
}
if (use_bn) {
if (!BN_lshift(bl, bl, 7)) {
goto err;
}
} else {
l <<= 7L;
}
/* The first component is 40 * value1 + value2, where value1 is 0, 1, or 2. */
uint64_t v;
if (!parse_oid_component(&cbs, &v)) {
goto err;
}
if (v >= 80) {
if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
!add_decimal(&cbb, v - 80)) {
goto err;
}
} else if (!add_decimal(&cbb, v / 40) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v % 40)) {
goto err;
}
if (first) {
first = 0;
if (l >= 80) {
i = 2;
if (use_bn) {
if (!BN_sub_word(bl, 80)) {
goto err;
}
} else {
l -= 80;
}
} else {
i = (int)(l / 40);
l -= (long)(i * 40);
}
if (out && out_len > 1) {
*out++ = i + '0';
*out = '0';
out_len--;
}
n++;
}
if (use_bn) {
char *bndec;
bndec = BN_bn2dec(bl);
if (!bndec) {
goto err;
}
i = strlen(bndec);
if (out) {
if (out_len > 1) {
*out++ = '.';
*out = 0;
out_len--;
}
BUF_strlcpy(out, bndec, out_len);
if (i > out_len) {
out += out_len;
out_len = 0;
} else {
out += i;
out_len -= i;
}
}
n++;
n += i;
OPENSSL_free(bndec);
} else {
BIO_snprintf(tbuf, sizeof(tbuf), ".%lu", l);
i = strlen(tbuf);
if (out && out_len > 0) {
BUF_strlcpy(out, tbuf, out_len);
if (i > out_len) {
out += out_len;
out_len = 0;
} else {
out += i;
out_len -= i;
}
}
n += i;
while (CBS_len(&cbs) != 0) {
if (!parse_oid_component(&cbs, &v) ||
!CBB_add_u8(&cbb, '.') ||
!add_decimal(&cbb, v)) {
goto err;
}
}
BN_free(bl);
return n;
uint8_t *txt;
size_t txt_len;
if (!CBB_add_u8(&cbb, '\0') ||
!CBB_finish(&cbb, &txt, &txt_len)) {
goto err;
}
int ret = strlcpy_int(out, (const char *)txt, out_len);
OPENSSL_free(txt);
return ret;
err:
BN_free(bl);
CBB_cleanup(&cbb);
if (out_len > 0) {
out[0] = '\0';
}
return -1;
}

View File

@ -182,6 +182,51 @@ static bool TestObj2Txt() {
return false;
}
ASN1_OBJECT obj;
memset(&obj, 0, sizeof(obj));
if (OBJ_obj2txt(NULL, 0, &obj, 0) != -1) {
fprintf(stderr, "OBJ_obj2txt accepted the empty OID.\n");
return false;
}
// kNonMinimalOID is kBasicConstraints with the final component non-minimally
// encoded.
static const uint8_t kNonMinimalOID[] = {
0x55, 0x1d, 0x80, 0x13,
};
obj.data = kNonMinimalOID;
obj.length = sizeof(kNonMinimalOID);
if (OBJ_obj2txt(NULL, 0, &obj, 0) != -1) {
fprintf(stderr, "OBJ_obj2txt accepted non-minimal OIDs.\n");
return false;
}
// kOverflowOID is the DER representation of
// 1.2.840.113554.4.1.72585.18446744073709551616. (The final value is 2^64.)
static const uint8_t kOverflowOID[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09,
0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
};
obj.data = kOverflowOID;
obj.length = sizeof(kOverflowOID);
if (OBJ_obj2txt(NULL, 0, &obj, 0) != -1) {
fprintf(stderr, "OBJ_obj2txt accepted an OID with a large component.\n");
return false;
}
// kInvalidOID is a mis-encoded version of kBasicConstraints with the final
// octet having the high bit set.
static const uint8_t kInvalidOID[] = {
0x55, 0x1d, 0x93,
};
obj.data = kInvalidOID;
obj.length = sizeof(kInvalidOID);
if (OBJ_obj2txt(NULL, 0, &obj, 0) != -1) {
fprintf(stderr, "OBJ_obj2txt accepted a mis-encoded OID.\n");
return false;
}
return true;
}