mirror of
https://github.com/openssl/openssl.git
synced 2024-12-27 06:21:43 +08:00
86fd4c1df9
Coverity flagged an overflow warning here that can occur if BIO_write returns an error. The overflow itself is a bit of a non-issue, but if BIO_write returns < 0, then the return from i2a_ASN1_OBJECT will be some odd value representing whatever the offset from the error code to the number of bytes the dump may or may not have written (or some larger negative error code if both fail. So lets fix it. Only do the dump if the BIO_write call returned 0 or greaater. Reviewed-by: Paul Dale <ppzgs1@gmail.com> Reviewed-by: Paul Yang <kaishen.yy@antfin.com> (Merged from https://github.com/openssl/openssl/pull/24976)
385 lines
10 KiB
C
385 lines
10 KiB
C
/*
|
|
* Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include "crypto/ctype.h"
|
|
#include "internal/cryptlib.h"
|
|
#include <openssl/buffer.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/objects.h>
|
|
#include <openssl/bn.h>
|
|
#include "crypto/asn1.h"
|
|
#include "asn1_local.h"
|
|
|
|
int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)
|
|
{
|
|
unsigned char *p, *allocated = NULL;
|
|
int objsize;
|
|
|
|
if ((a == NULL) || (a->data == NULL))
|
|
return 0;
|
|
|
|
objsize = ASN1_object_size(0, a->length, V_ASN1_OBJECT);
|
|
if (pp == NULL || objsize == -1)
|
|
return objsize;
|
|
|
|
if (*pp == NULL) {
|
|
if ((p = allocated = OPENSSL_malloc(objsize)) == NULL)
|
|
return 0;
|
|
} else {
|
|
p = *pp;
|
|
}
|
|
|
|
ASN1_put_object(&p, 0, a->length, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
|
|
memcpy(p, a->data, a->length);
|
|
|
|
/*
|
|
* If a new buffer was allocated, just return it back.
|
|
* If not, return the incremented buffer pointer.
|
|
*/
|
|
*pp = allocated != NULL ? allocated : p + a->length;
|
|
return objsize;
|
|
}
|
|
|
|
int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
|
|
{
|
|
int i, first, len = 0, c, use_bn;
|
|
char ftmp[24], *tmp = ftmp;
|
|
int tmpsize = sizeof(ftmp);
|
|
const char *p;
|
|
unsigned long l;
|
|
BIGNUM *bl = NULL;
|
|
|
|
if (num == 0)
|
|
return 0;
|
|
else if (num == -1)
|
|
num = strlen(buf);
|
|
|
|
p = buf;
|
|
c = *(p++);
|
|
num--;
|
|
if ((c >= '0') && (c <= '2')) {
|
|
first = c - '0';
|
|
} else {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_FIRST_NUM_TOO_LARGE);
|
|
goto err;
|
|
}
|
|
|
|
if (num <= 0) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_MISSING_SECOND_NUMBER);
|
|
goto err;
|
|
}
|
|
c = *(p++);
|
|
num--;
|
|
for (;;) {
|
|
if (num <= 0)
|
|
break;
|
|
if ((c != '.') && (c != ' ')) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_SEPARATOR);
|
|
goto err;
|
|
}
|
|
l = 0;
|
|
use_bn = 0;
|
|
for (;;) {
|
|
if (num <= 0)
|
|
break;
|
|
num--;
|
|
c = *(p++);
|
|
if ((c == ' ') || (c == '.'))
|
|
break;
|
|
if (!ossl_isdigit(c)) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_DIGIT);
|
|
goto err;
|
|
}
|
|
if (!use_bn && l >= ((ULONG_MAX - 80) / 10L)) {
|
|
use_bn = 1;
|
|
if (bl == NULL)
|
|
bl = BN_new();
|
|
if (bl == NULL || !BN_set_word(bl, l))
|
|
goto err;
|
|
}
|
|
if (use_bn) {
|
|
if (!BN_mul_word(bl, 10L)
|
|
|| !BN_add_word(bl, c - '0'))
|
|
goto err;
|
|
} else
|
|
l = l * 10L + (long)(c - '0');
|
|
}
|
|
if (len == 0) {
|
|
if ((first < 2) && (l >= 40)) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE);
|
|
goto err;
|
|
}
|
|
if (use_bn) {
|
|
if (!BN_add_word(bl, first * 40))
|
|
goto err;
|
|
} else
|
|
l += (long)first *40;
|
|
}
|
|
i = 0;
|
|
if (use_bn) {
|
|
int blsize;
|
|
blsize = BN_num_bits(bl);
|
|
blsize = (blsize + 6) / 7;
|
|
if (blsize > tmpsize) {
|
|
if (tmp != ftmp)
|
|
OPENSSL_free(tmp);
|
|
tmpsize = blsize + 32;
|
|
tmp = OPENSSL_malloc(tmpsize);
|
|
if (tmp == NULL)
|
|
goto err;
|
|
}
|
|
while (blsize--) {
|
|
BN_ULONG t = BN_div_word(bl, 0x80L);
|
|
if (t == (BN_ULONG)-1)
|
|
goto err;
|
|
tmp[i++] = (unsigned char)t;
|
|
}
|
|
} else {
|
|
|
|
for (;;) {
|
|
tmp[i++] = (unsigned char)l & 0x7f;
|
|
l >>= 7L;
|
|
if (l == 0L)
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (out != NULL) {
|
|
if (len + i > olen) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_BUFFER_TOO_SMALL);
|
|
goto err;
|
|
}
|
|
while (--i > 0)
|
|
out[len++] = tmp[i] | 0x80;
|
|
out[len++] = tmp[0];
|
|
} else
|
|
len += i;
|
|
}
|
|
if (tmp != ftmp)
|
|
OPENSSL_free(tmp);
|
|
BN_free(bl);
|
|
return len;
|
|
err:
|
|
if (tmp != ftmp)
|
|
OPENSSL_free(tmp);
|
|
BN_free(bl);
|
|
return 0;
|
|
}
|
|
|
|
int i2t_ASN1_OBJECT(char *buf, int buf_len, const ASN1_OBJECT *a)
|
|
{
|
|
return OBJ_obj2txt(buf, buf_len, a, 0);
|
|
}
|
|
|
|
int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a)
|
|
{
|
|
char buf[80], *p = buf;
|
|
int i;
|
|
|
|
if ((a == NULL) || (a->data == NULL))
|
|
return BIO_write(bp, "NULL", 4);
|
|
i = i2t_ASN1_OBJECT(buf, sizeof(buf), a);
|
|
if (i > (int)(sizeof(buf) - 1)) {
|
|
if (i > INT_MAX - 1) { /* catch an integer overflow */
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_LENGTH_TOO_LONG);
|
|
return -1;
|
|
}
|
|
if ((p = OPENSSL_malloc(i + 1)) == NULL)
|
|
return -1;
|
|
i2t_ASN1_OBJECT(p, i + 1, a);
|
|
}
|
|
if (i <= 0) {
|
|
i = BIO_write(bp, "<INVALID>", 9);
|
|
if (i > 0)
|
|
i += BIO_dump(bp, (const char *)a->data, a->length);
|
|
return i;
|
|
}
|
|
BIO_write(bp, p, i);
|
|
if (p != buf)
|
|
OPENSSL_free(p);
|
|
return i;
|
|
}
|
|
|
|
ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
|
|
long length)
|
|
{
|
|
const unsigned char *p;
|
|
long len;
|
|
int tag, xclass;
|
|
int inf, i;
|
|
ASN1_OBJECT *ret = NULL;
|
|
p = *pp;
|
|
inf = ASN1_get_object(&p, &len, &tag, &xclass, length);
|
|
if (inf & 0x80) {
|
|
i = ASN1_R_BAD_OBJECT_HEADER;
|
|
goto err;
|
|
}
|
|
|
|
if (tag != V_ASN1_OBJECT) {
|
|
i = ASN1_R_EXPECTING_AN_OBJECT;
|
|
goto err;
|
|
}
|
|
ret = ossl_c2i_ASN1_OBJECT(a, &p, len);
|
|
if (ret)
|
|
*pp = p;
|
|
return ret;
|
|
err:
|
|
ERR_raise(ERR_LIB_ASN1, i);
|
|
return NULL;
|
|
}
|
|
|
|
ASN1_OBJECT *ossl_c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
|
|
long len)
|
|
{
|
|
ASN1_OBJECT *ret = NULL, tobj;
|
|
const unsigned char *p;
|
|
unsigned char *data;
|
|
int i, length;
|
|
|
|
/*
|
|
* Sanity check OID encoding. Need at least one content octet. MSB must
|
|
* be clear in the last octet. can't have leading 0x80 in subidentifiers,
|
|
* see: X.690 8.19.2
|
|
*/
|
|
if (len <= 0 || len > INT_MAX || pp == NULL || (p = *pp) == NULL ||
|
|
p[len - 1] & 0x80) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
|
|
return NULL;
|
|
}
|
|
/* Now 0 < len <= INT_MAX, so the cast is safe. */
|
|
length = (int)len;
|
|
/*
|
|
* Try to lookup OID in table: these are all valid encodings so if we get
|
|
* a match we know the OID is valid.
|
|
*/
|
|
tobj.nid = NID_undef;
|
|
tobj.data = p;
|
|
tobj.length = length;
|
|
tobj.flags = 0;
|
|
i = OBJ_obj2nid(&tobj);
|
|
if (i != NID_undef) {
|
|
/*
|
|
* Return shared registered OID object: this improves efficiency
|
|
* because we don't have to return a dynamically allocated OID
|
|
* and NID lookups can use the cached value.
|
|
*/
|
|
ret = OBJ_nid2obj(i);
|
|
if (a) {
|
|
ASN1_OBJECT_free(*a);
|
|
*a = ret;
|
|
}
|
|
*pp += len;
|
|
return ret;
|
|
}
|
|
for (i = 0; i < length; i++, p++) {
|
|
if (*p == 0x80 && (!i || !(p[-1] & 0x80))) {
|
|
ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if ((a == NULL) || ((*a) == NULL) ||
|
|
!((*a)->flags & ASN1_OBJECT_FLAG_DYNAMIC)) {
|
|
if ((ret = ASN1_OBJECT_new()) == NULL)
|
|
return NULL;
|
|
} else {
|
|
ret = (*a);
|
|
}
|
|
|
|
p = *pp;
|
|
/* detach data from object */
|
|
data = (unsigned char *)ret->data;
|
|
ret->data = NULL;
|
|
/* once detached we can change it */
|
|
if ((data == NULL) || (ret->length < length)) {
|
|
ret->length = 0;
|
|
OPENSSL_free(data);
|
|
data = OPENSSL_malloc(length);
|
|
if (data == NULL)
|
|
goto err;
|
|
ret->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA;
|
|
}
|
|
memcpy(data, p, length);
|
|
/* If there are dynamic strings, free them here, and clear the flag */
|
|
if ((ret->flags & ASN1_OBJECT_FLAG_DYNAMIC_STRINGS) != 0) {
|
|
OPENSSL_free((char *)ret->sn);
|
|
OPENSSL_free((char *)ret->ln);
|
|
ret->flags &= ~ASN1_OBJECT_FLAG_DYNAMIC_STRINGS;
|
|
}
|
|
/* reattach data to object, after which it remains const */
|
|
ret->data = data;
|
|
ret->length = length;
|
|
ret->sn = NULL;
|
|
ret->ln = NULL;
|
|
/* ret->flags=ASN1_OBJECT_FLAG_DYNAMIC; we know it is dynamic */
|
|
p += length;
|
|
|
|
if (a != NULL)
|
|
(*a) = ret;
|
|
*pp = p;
|
|
return ret;
|
|
err:
|
|
ERR_raise(ERR_LIB_ASN1, i);
|
|
if ((a == NULL) || (*a != ret))
|
|
ASN1_OBJECT_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
ASN1_OBJECT *ASN1_OBJECT_new(void)
|
|
{
|
|
ASN1_OBJECT *ret;
|
|
|
|
ret = OPENSSL_zalloc(sizeof(*ret));
|
|
if (ret == NULL)
|
|
return NULL;
|
|
ret->flags = ASN1_OBJECT_FLAG_DYNAMIC;
|
|
return ret;
|
|
}
|
|
|
|
void ASN1_OBJECT_free(ASN1_OBJECT *a)
|
|
{
|
|
if (a == NULL)
|
|
return;
|
|
if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_STRINGS) {
|
|
#ifndef CONST_STRICT
|
|
/*
|
|
* Disable purely for compile-time strict const checking. Doing this
|
|
* on a "real" compile will cause memory leaks
|
|
*/
|
|
OPENSSL_free((void*)a->sn);
|
|
OPENSSL_free((void*)a->ln);
|
|
#endif
|
|
a->sn = a->ln = NULL;
|
|
}
|
|
if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC_DATA) {
|
|
OPENSSL_free((void*)a->data);
|
|
a->data = NULL;
|
|
a->length = 0;
|
|
}
|
|
if (a->flags & ASN1_OBJECT_FLAG_DYNAMIC)
|
|
OPENSSL_free(a);
|
|
}
|
|
|
|
ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data, int len,
|
|
const char *sn, const char *ln)
|
|
{
|
|
ASN1_OBJECT o;
|
|
|
|
o.sn = sn;
|
|
o.ln = ln;
|
|
o.data = data;
|
|
o.nid = nid;
|
|
o.length = len;
|
|
o.flags = ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
|
|
ASN1_OBJECT_FLAG_DYNAMIC_DATA;
|
|
return OBJ_dup(&o);
|
|
}
|