Packages
hackney
2.0.1
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/boringssl/crypto/x509/asn1_gen.cc
// Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/x509.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/obj.h>
#include "../conf/internal.h"
#include "../internal.h"
#include "internal.h"
// Although this file is in crypto/x509 for layering purposes, it emits
// errors from the ASN.1 module for OpenSSL compatibility.
// ASN1_GEN_MAX_DEPTH is the maximum number of nested TLVs allowed.
#define ASN1_GEN_MAX_DEPTH 50
// ASN1_GEN_MAX_OUTPUT is the maximum output, in bytes, allowed. This limit is
// necessary because the SEQUENCE and SET section reference mechanism allows the
// output length to grow super-linearly with the input length.
#define ASN1_GEN_MAX_OUTPUT (64 * 1024)
// ASN1_GEN_FORMAT_* are the values for the format modifiers.
#define ASN1_GEN_FORMAT_ASCII 1
#define ASN1_GEN_FORMAT_UTF8 2
#define ASN1_GEN_FORMAT_HEX 3
#define ASN1_GEN_FORMAT_BITLIST 4
// generate_v3 converts |str| into an ASN.1 structure and writes the result to
// |cbb|. It returns one on success and zero on error. |depth| bounds recursion,
// and |format| specifies the current format modifier.
//
// If |tag| is non-zero, the structure is implicitly tagged with |tag|. |tag|
// must not have the constructed bit set.
static int generate_v3(CBB *cbb, const char *str, const X509V3_CTX *cnf,
CBS_ASN1_TAG tag, int format, int depth);
static int bitstr_cb(const char *elem, size_t len, void *bitstr);
ASN1_TYPE *ASN1_generate_v3(const char *str, const X509V3_CTX *cnf) {
bssl::ScopedCBB cbb;
if (!CBB_init(cbb.get(), 0) || //
!generate_v3(cbb.get(), str, cnf, /*tag=*/0, ASN1_GEN_FORMAT_ASCII,
/*depth=*/0)) {
return nullptr;
}
// While not strictly necessary to avoid a DoS (we rely on any super-linear
// checks being performed internally), cap the overall output to
// |ASN1_GEN_MAX_OUTPUT| so the externally-visible behavior is consistent.
if (CBB_len(cbb.get()) > ASN1_GEN_MAX_OUTPUT) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
return nullptr;
}
const uint8_t *der = CBB_data(cbb.get());
return d2i_ASN1_TYPE(nullptr, &der, CBB_len(cbb.get()));
}
static int cbs_str_equal(const CBS *cbs, const char *str) {
return CBS_len(cbs) == strlen(str) &&
OPENSSL_memcmp(CBS_data(cbs), str, strlen(str)) == 0;
}
// parse_tag decodes a tag specifier in |cbs|. It returns the tag on success or
// zero on error.
static CBS_ASN1_TAG parse_tag(const CBS *cbs) {
CBS copy = *cbs;
uint64_t num;
if (!CBS_get_u64_decimal(©, &num) || num > CBS_ASN1_TAG_NUMBER_MASK) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_NUMBER);
return 0;
}
CBS_ASN1_TAG tag_class = CBS_ASN1_CONTEXT_SPECIFIC;
// The tag may be suffixed by a class.
uint8_t c;
if (CBS_get_u8(©, &c)) {
switch (c) {
case 'U':
tag_class = CBS_ASN1_UNIVERSAL;
break;
case 'A':
tag_class = CBS_ASN1_APPLICATION;
break;
case 'P':
tag_class = CBS_ASN1_PRIVATE;
break;
case 'C':
tag_class = CBS_ASN1_CONTEXT_SPECIFIC;
break;
default: {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_MODIFIER);
return 0;
}
}
if (CBS_len(©) != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_MODIFIER);
return 0;
}
}
// Tag [UNIVERSAL 0] is reserved for indefinite-length end-of-contents. We
// also use zero in this file to indicator no explicit tagging.
if (tag_class == CBS_ASN1_UNIVERSAL && num == 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_NUMBER);
return 0;
}
return tag_class | (CBS_ASN1_TAG)num;
}
static int generate_wrapped(CBB *cbb, const char *str, const X509V3_CTX *cnf,
CBS_ASN1_TAG tag, int padding, int format,
int depth) {
CBB child;
return CBB_add_asn1(cbb, &child, tag) &&
(!padding || CBB_add_u8(&child, 0)) &&
generate_v3(&child, str, cnf, /*tag=*/0, format, depth + 1) &&
CBB_flush(cbb);
}
static int generate_v3(CBB *cbb, const char *str, const X509V3_CTX *cnf,
CBS_ASN1_TAG tag, int format, int depth) {
assert((tag & CBS_ASN1_CONSTRUCTED) == 0);
if (depth > ASN1_GEN_MAX_DEPTH) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING);
return 0;
}
// Process modifiers. This function uses a mix of NUL-terminated strings and
// |CBS|. Several functions only work with NUL-terminated strings, so we need
// to keep track of when a slice spans the whole buffer.
for (;;) {
// Skip whitespace.
while (*str != '\0' && OPENSSL_isspace((unsigned char)*str)) {
str++;
}
// Modifiers end at commas.
const char *comma = strchr(str, ',');
if (comma == nullptr) {
break;
}
// Remove trailing whitespace.
CBS modifier;
CBS_init(&modifier, (const uint8_t *)str, comma - str);
for (;;) {
uint8_t v;
CBS copy = modifier;
if (!CBS_get_last_u8(©, &v) || !OPENSSL_isspace(v)) {
break;
}
modifier = copy;
}
// Advance the string past the modifier, but save the original value. We
// will need to rewind if this is not a recognized modifier.
const char *str_old = str;
str = comma + 1;
// Each modifier is either NAME:VALUE or NAME.
CBS name;
int has_value = CBS_get_until_first(&modifier, &name, ':');
if (has_value) {
CBS_skip(&modifier, 1); // Skip the colon.
} else {
name = modifier;
CBS_init(&modifier, nullptr, 0);
}
if (cbs_str_equal(&name, "FORMAT") || cbs_str_equal(&name, "FORM")) {
if (cbs_str_equal(&modifier, "ASCII")) {
format = ASN1_GEN_FORMAT_ASCII;
} else if (cbs_str_equal(&modifier, "UTF8")) {
format = ASN1_GEN_FORMAT_UTF8;
} else if (cbs_str_equal(&modifier, "HEX")) {
format = ASN1_GEN_FORMAT_HEX;
} else if (cbs_str_equal(&modifier, "BITLIST")) {
format = ASN1_GEN_FORMAT_BITLIST;
} else {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_FORMAT);
return 0;
}
} else if (cbs_str_equal(&name, "IMP") ||
cbs_str_equal(&name, "IMPLICIT")) {
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING);
return 0;
}
tag = parse_tag(&modifier);
if (tag == 0) {
return 0;
}
} else if (cbs_str_equal(&name, "EXP") ||
cbs_str_equal(&name, "EXPLICIT")) {
// It would actually be supportable, but OpenSSL does not allow wrapping
// an explicit tag in an implicit tag.
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING);
return 0;
}
tag = parse_tag(&modifier);
return tag != 0 &&
generate_wrapped(cbb, str, cnf, tag | CBS_ASN1_CONSTRUCTED,
/*padding=*/0, format, depth);
} else if (cbs_str_equal(&name, "OCTWRAP")) {
tag = tag == 0 ? CBS_ASN1_OCTETSTRING : tag;
return generate_wrapped(cbb, str, cnf, tag, /*padding=*/0, format, depth);
} else if (cbs_str_equal(&name, "BITWRAP")) {
tag = tag == 0 ? CBS_ASN1_BITSTRING : tag;
return generate_wrapped(cbb, str, cnf, tag, /*padding=*/1, format, depth);
} else if (cbs_str_equal(&name, "SEQWRAP")) {
tag = tag == 0 ? CBS_ASN1_SEQUENCE : (tag | CBS_ASN1_CONSTRUCTED);
tag |= CBS_ASN1_CONSTRUCTED;
return generate_wrapped(cbb, str, cnf, tag, /*padding=*/0, format, depth);
} else if (cbs_str_equal(&name, "SETWRAP")) {
tag = tag == 0 ? CBS_ASN1_SET : (tag | CBS_ASN1_CONSTRUCTED);
return generate_wrapped(cbb, str, cnf, tag, /*padding=*/0, format, depth);
} else {
// If this was not a recognized modifier, rewind |str| to before splitting
// on the comma. The type itself consumes all remaining input.
str = str_old;
break;
}
}
// The final element is, like modifiers, NAME:VALUE or NAME, but VALUE spans
// the length of the string, including any commas.
const char *colon = strchr(str, ':');
CBS name;
const char *value;
int has_value = colon != nullptr;
if (has_value) {
CBS_init(&name, (const uint8_t *)str, colon - str);
value = colon + 1;
} else {
CBS_init(&name, (const uint8_t *)str, strlen(str));
value = ""; // Most types treat missing and empty value equivalently.
}
static const struct {
const char *name;
CBS_ASN1_TAG type;
} kTypes[] = {
{"BOOL", CBS_ASN1_BOOLEAN},
{"BOOLEAN", CBS_ASN1_BOOLEAN},
{"NULL", CBS_ASN1_NULL},
{"INT", CBS_ASN1_INTEGER},
{"INTEGER", CBS_ASN1_INTEGER},
{"ENUM", CBS_ASN1_ENUMERATED},
{"ENUMERATED", CBS_ASN1_ENUMERATED},
{"OID", CBS_ASN1_OBJECT},
{"OBJECT", CBS_ASN1_OBJECT},
{"UTCTIME", CBS_ASN1_UTCTIME},
{"UTC", CBS_ASN1_UTCTIME},
{"GENERALIZEDTIME", CBS_ASN1_GENERALIZEDTIME},
{"GENTIME", CBS_ASN1_GENERALIZEDTIME},
{"OCT", CBS_ASN1_OCTETSTRING},
{"OCTETSTRING", CBS_ASN1_OCTETSTRING},
{"BITSTR", CBS_ASN1_BITSTRING},
{"BITSTRING", CBS_ASN1_BITSTRING},
{"UNIVERSALSTRING", CBS_ASN1_UNIVERSALSTRING},
{"UNIV", CBS_ASN1_UNIVERSALSTRING},
{"IA5", CBS_ASN1_IA5STRING},
{"IA5STRING", CBS_ASN1_IA5STRING},
{"UTF8", CBS_ASN1_UTF8STRING},
{"UTF8String", CBS_ASN1_UTF8STRING},
{"BMP", CBS_ASN1_BMPSTRING},
{"BMPSTRING", CBS_ASN1_BMPSTRING},
{"PRINTABLESTRING", CBS_ASN1_PRINTABLESTRING},
{"PRINTABLE", CBS_ASN1_PRINTABLESTRING},
{"T61", CBS_ASN1_T61STRING},
{"T61STRING", CBS_ASN1_T61STRING},
{"TELETEXSTRING", CBS_ASN1_T61STRING},
{"SEQUENCE", CBS_ASN1_SEQUENCE},
{"SEQ", CBS_ASN1_SEQUENCE},
{"SET", CBS_ASN1_SET},
};
CBS_ASN1_TAG type = 0;
for (const auto &t : kTypes) {
if (cbs_str_equal(&name, t.name)) {
type = t.type;
break;
}
}
if (type == 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_TAG);
return 0;
}
// If there is an implicit tag, use the constructed bit from the base type.
tag = tag == 0 ? type : (tag | (type & CBS_ASN1_CONSTRUCTED));
CBB child;
if (!CBB_add_asn1(cbb, &child, tag)) {
return 0;
}
switch (type) {
case CBS_ASN1_NULL:
if (*value != '\0') {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_NULL_VALUE);
return 0;
}
return CBB_flush(cbb);
case CBS_ASN1_BOOLEAN: {
if (format != ASN1_GEN_FORMAT_ASCII) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ASCII_FORMAT);
return 0;
}
ASN1_BOOLEAN boolean;
if (!X509V3_bool_from_string(value, &boolean)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_BOOLEAN);
return 0;
}
return CBB_add_u8(&child, boolean ? 0xff : 0x00) && CBB_flush(cbb);
}
case CBS_ASN1_INTEGER:
case CBS_ASN1_ENUMERATED: {
if (format != ASN1_GEN_FORMAT_ASCII) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INTEGER_NOT_ASCII_FORMAT);
return 0;
}
ASN1_INTEGER *obj = s2i_ASN1_INTEGER(nullptr, value);
if (obj == nullptr) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_INTEGER);
return 0;
}
int len = i2c_ASN1_INTEGER(obj, nullptr);
uint8_t *out;
int ok = len > 0 && //
CBB_add_space(&child, &out, len) &&
i2c_ASN1_INTEGER(obj, &out) == len && CBB_flush(cbb);
ASN1_INTEGER_free(obj);
return ok;
}
case CBS_ASN1_OBJECT: {
if (format != ASN1_GEN_FORMAT_ASCII) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_OBJECT_NOT_ASCII_FORMAT);
return 0;
}
ASN1_OBJECT *obj = OBJ_txt2obj(value, /*dont_search_names=*/0);
if (obj == nullptr || obj->length == 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
return 0;
}
int ok = CBB_add_bytes(&child, obj->data, obj->length) && CBB_flush(cbb);
ASN1_OBJECT_free(obj);
return ok;
}
case CBS_ASN1_UTCTIME:
case CBS_ASN1_GENERALIZEDTIME: {
if (format != ASN1_GEN_FORMAT_ASCII) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TIME_NOT_ASCII_FORMAT);
return 0;
}
CBS value_cbs;
CBS_init(&value_cbs, (const uint8_t *)value, strlen(value));
int ok = type == CBS_ASN1_UTCTIME
? CBS_parse_utc_time(&value_cbs, nullptr,
/*allow_timezone_offset=*/0)
: CBS_parse_generalized_time(&value_cbs, nullptr,
/*allow_timezone_offset=*/0);
if (!ok) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_TIME_VALUE);
return 0;
}
return CBB_add_bytes(&child, (const uint8_t *)value, strlen(value)) &&
CBB_flush(cbb);
}
case CBS_ASN1_UNIVERSALSTRING:
case CBS_ASN1_IA5STRING:
case CBS_ASN1_UTF8STRING:
case CBS_ASN1_BMPSTRING:
case CBS_ASN1_PRINTABLESTRING:
case CBS_ASN1_T61STRING: {
int encoding;
if (format == ASN1_GEN_FORMAT_ASCII) {
encoding = MBSTRING_ASC;
} else if (format == ASN1_GEN_FORMAT_UTF8) {
encoding = MBSTRING_UTF8;
} else {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_FORMAT);
return 0;
}
// |maxsize| is measured in code points, rather than bytes, but pass it in
// as a loose cap so fuzzers can exit from excessively long inputs
// earlier. This limit is not load-bearing because |ASN1_mbstring_ncopy|'s
// output is already linear in the input.
ASN1_STRING *obj = nullptr;
if (ASN1_mbstring_ncopy(&obj, (const uint8_t *)value, -1, encoding,
ASN1_tag2bit(type), /*minsize=*/0,
/*maxsize=*/ASN1_GEN_MAX_OUTPUT) <= 0) {
return 0;
}
int ok = CBB_add_bytes(&child, obj->data, obj->length) && CBB_flush(cbb);
ASN1_STRING_free(obj);
return ok;
}
case CBS_ASN1_BITSTRING:
if (format == ASN1_GEN_FORMAT_BITLIST) {
ASN1_BIT_STRING *obj = ASN1_BIT_STRING_new();
if (obj == nullptr) {
return 0;
}
if (!CONF_parse_list(value, ',', 1, bitstr_cb, obj)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_LIST_ERROR);
ASN1_BIT_STRING_free(obj);
return 0;
}
int len = i2c_ASN1_BIT_STRING(obj, nullptr);
uint8_t *out;
int ok = len > 0 && //
CBB_add_space(&child, &out, len) &&
i2c_ASN1_BIT_STRING(obj, &out) == len && //
CBB_flush(cbb);
ASN1_BIT_STRING_free(obj);
return ok;
}
// The other formats are the same as OCTET STRING, but with the leading
// zero bytes.
if (!CBB_add_u8(&child, 0)) {
return 0;
}
[[fallthrough]];
case CBS_ASN1_OCTETSTRING:
if (format == ASN1_GEN_FORMAT_ASCII) {
return CBB_add_bytes(&child, (const uint8_t *)value, strlen(value)) &&
CBB_flush(cbb);
}
if (format == ASN1_GEN_FORMAT_HEX) {
size_t len;
uint8_t *data = x509v3_hex_to_bytes(value, &len);
if (data == nullptr) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_HEX);
return 0;
}
int ok = CBB_add_bytes(&child, data, len) && CBB_flush(cbb);
OPENSSL_free(data);
return ok;
}
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT);
return 0;
case CBS_ASN1_SEQUENCE:
case CBS_ASN1_SET:
if (has_value) {
if (cnf == nullptr) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG);
return 0;
}
const STACK_OF(CONF_VALUE) *section = X509V3_get_section(cnf, value);
if (section == nullptr) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG);
return 0;
}
for (size_t i = 0; i < sk_CONF_VALUE_num(section); i++) {
const CONF_VALUE *conf = sk_CONF_VALUE_value(section, i);
if (!generate_v3(&child, conf->value, cnf, /*tag=*/0,
ASN1_GEN_FORMAT_ASCII, depth + 1)) {
return 0;
}
// This recursive call, by referencing |section|, is the one place
// where |generate_v3|'s output can be super-linear in the input.
// Check bounds here.
if (CBB_len(&child) > ASN1_GEN_MAX_OUTPUT) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
return 0;
}
}
}
if (type == CBS_ASN1_SET) {
// The SET type here is a SET OF and must be sorted.
return CBB_flush_asn1_set_of(&child) && CBB_flush(cbb);
}
return CBB_flush(cbb);
default:
OPENSSL_PUT_ERROR(ASN1, ERR_R_INTERNAL_ERROR);
return 0;
}
}
static int bitstr_cb(const char *elem, size_t len, void *bitstr) {
CBS cbs;
CBS_init(&cbs, (const uint8_t *)elem, len);
uint64_t bitnum;
if (!CBS_get_u64_decimal(&cbs, &bitnum) || CBS_len(&cbs) != 0 ||
// Cap the highest allowed bit so this mechanism cannot be used to create
// extremely large allocations with short inputs. The highest named bit in
// RFC 5280 is 8, so 256 should give comfortable margin but still only
// allow a 32-byte allocation.
//
// We do not consider this function to be safe with untrusted inputs (even
// without bugs, it is prone to string injection vulnerabilities), so DoS
// is not truly a concern, but the limit is necessary to keep fuzzing
// effective.
bitnum > 256) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_NUMBER);
return 0;
}
if (!ASN1_BIT_STRING_set_bit(reinterpret_cast<ASN1_BIT_STRING *>(bitstr),
(int)bitnum, 1)) {
return 0;
}
return 1;
}