Android needs to be able to read a PKCS#7 blob from a Java InputStream. This change adds |BIO_read_asn1| which reads a single ASN.1 object from the start of a BIO without overreading. Change-Id: I74776e686529c8e58af1c26a4909f9bd4e87b707kris/onging/CECPQ3_patch15
@@ -56,6 +56,7 @@ | |||||
#include <openssl/bio.h> | #include <openssl/bio.h> | ||||
#include <assert.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <string.h> | #include <string.h> | ||||
@@ -460,3 +461,142 @@ static int print_bio(const char *str, size_t len, void *bio) { | |||||
void BIO_print_errors(BIO *bio) { | void BIO_print_errors(BIO *bio) { | ||||
ERR_print_errors_cb(print_bio, bio); | ERR_print_errors_cb(print_bio, bio); | ||||
} | } | ||||
/* bio_read_all reads everything from |bio| and prepends |prefix| to it. On | |||||
* success, |*out| is set to an allocated buffer (which should be freed with | |||||
* |OPENSSL_free|), |*out_len| is set to its length and one is returned. The | |||||
* buffer will contain |prefix| followed by the contents of |bio|. On failure, | |||||
* zero is returned. | |||||
* | |||||
* The function will fail if the size of the output would equal or exceed | |||||
* |max_len|. */ | |||||
static int bio_read_all(BIO *bio, uint8_t **out, size_t *out_len, | |||||
const uint8_t *prefix, size_t prefix_len, | |||||
size_t max_len) { | |||||
static const size_t kChunkSize = 4096; | |||||
size_t len = prefix_len + kChunkSize; | |||||
if (len > max_len) { | |||||
len = max_len; | |||||
} | |||||
if (len < prefix_len) { | |||||
return 0; | |||||
} | |||||
*out = OPENSSL_malloc(len); | |||||
if (*out == NULL) { | |||||
return 0; | |||||
} | |||||
memcpy(*out, prefix, prefix_len); | |||||
size_t done = prefix_len; | |||||
for (;;) { | |||||
if (done == len) { | |||||
OPENSSL_free(*out); | |||||
return 0; | |||||
} | |||||
const size_t todo = len - done; | |||||
assert(todo < INT_MAX); | |||||
const int n = BIO_read(bio, *out + done, todo); | |||||
if (n == 0) { | |||||
*out_len = done; | |||||
return 1; | |||||
} else if (n == -1) { | |||||
OPENSSL_free(*out); | |||||
return 0; | |||||
} | |||||
done += n; | |||||
if (len < max_len && len - done < kChunkSize / 2) { | |||||
len += kChunkSize; | |||||
if (len < kChunkSize || len > max_len) { | |||||
len = max_len; | |||||
} | |||||
uint8_t *new_buf = OPENSSL_realloc(*out, len); | |||||
if (new_buf == NULL) { | |||||
OPENSSL_free(*out); | |||||
return 0; | |||||
} | |||||
*out = new_buf; | |||||
} | |||||
} | |||||
} | |||||
int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, size_t max_len) { | |||||
uint8_t header[6]; | |||||
static const size_t kInitialHeaderLen = 2; | |||||
if (BIO_read(bio, header, kInitialHeaderLen) != kInitialHeaderLen) { | |||||
return 0; | |||||
} | |||||
const uint8_t tag = header[0]; | |||||
const uint8_t length_byte = header[1]; | |||||
if ((tag & 0x1f) == 0x1f) { | |||||
/* Long form tags are not supported. */ | |||||
return 0; | |||||
} | |||||
size_t len, header_len; | |||||
if ((length_byte & 0x80) == 0) { | |||||
/* Short form length. */ | |||||
len = length_byte; | |||||
header_len = kInitialHeaderLen; | |||||
} else { | |||||
const size_t num_bytes = length_byte & 0x7f; | |||||
if ((tag & 0x20 /* constructed */) != 0 && num_bytes == 0) { | |||||
/* indefinite length. */ | |||||
return bio_read_all(bio, out, out_len, header, kInitialHeaderLen, | |||||
max_len); | |||||
} | |||||
if (num_bytes == 0 || num_bytes > 4) { | |||||
return 0; | |||||
} | |||||
if (BIO_read(bio, header + kInitialHeaderLen, num_bytes) != num_bytes) { | |||||
return 0; | |||||
} | |||||
header_len = kInitialHeaderLen + num_bytes; | |||||
uint32_t len32 = 0; | |||||
unsigned i; | |||||
for (i = 0; i < num_bytes; i++) { | |||||
len32 <<= 8; | |||||
len32 |= header[kInitialHeaderLen + i]; | |||||
} | |||||
if (len32 < 128) { | |||||
/* Length should have used short-form encoding. */ | |||||
return 0; | |||||
} | |||||
if ((len32 >> ((num_bytes-1)*8)) == 0) { | |||||
/* Length should have been at least one byte shorter. */ | |||||
return 0; | |||||
} | |||||
len = len32; | |||||
} | |||||
if (len + header_len < len || | |||||
len + header_len > max_len) { | |||||
return 0; | |||||
} | |||||
len += header_len; | |||||
*out_len = len; | |||||
*out = OPENSSL_malloc(len); | |||||
if (*out == NULL) { | |||||
return 0; | |||||
} | |||||
memcpy(*out, header, header_len); | |||||
if (BIO_read(bio, (*out) + header_len, len - header_len) != | |||||
len - header_len) { | |||||
OPENSSL_free(*out); | |||||
return 0; | |||||
} | |||||
return 1; | |||||
} |
@@ -329,6 +329,87 @@ static bool TestPrintf() { | |||||
return true; | return true; | ||||
} | } | ||||
static bool ReadASN1(bool should_succeed, const uint8_t *data, size_t data_len, | |||||
size_t expected_len, size_t max_len) { | |||||
ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(data), data_len)); | |||||
uint8_t *out; | |||||
size_t out_len; | |||||
int ok = BIO_read_asn1(bio.get(), &out, &out_len, max_len); | |||||
if (!ok) { | |||||
out = nullptr; | |||||
} | |||||
ScopedOpenSSLBytes out_storage(out); | |||||
if (should_succeed != (ok == 1)) { | |||||
return false; | |||||
} | |||||
if (should_succeed && | |||||
(out_len != expected_len || memcmp(data, out, expected_len) != 0)) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
static bool TestASN1() { | |||||
static const uint8_t kData1[] = {0x30, 2, 1, 2, 0, 0}; | |||||
static const uint8_t kData2[] = {0x30, 3, 1, 2}; /* truncated */ | |||||
static const uint8_t kData3[] = {0x30, 0x81, 1, 1}; /* should be short len */ | |||||
static const uint8_t kData4[] = {0x30, 0x82, 0, 1, 1}; /* zero padded. */ | |||||
if (!ReadASN1(true, kData1, sizeof(kData1), 4, 100) || | |||||
!ReadASN1(false, kData2, sizeof(kData2), 0, 100) || | |||||
!ReadASN1(false, kData3, sizeof(kData3), 0, 100) || | |||||
!ReadASN1(false, kData4, sizeof(kData4), 0, 100)) { | |||||
return false; | |||||
} | |||||
static const size_t kLargePayloadLen = 8000; | |||||
static const uint8_t kLargePrefix[] = {0x30, 0x82, kLargePayloadLen >> 8, | |||||
kLargePayloadLen & 0xff}; | |||||
ScopedOpenSSLBytes large(reinterpret_cast<uint8_t *>( | |||||
OPENSSL_malloc(sizeof(kLargePrefix) + kLargePayloadLen))); | |||||
if (!large) { | |||||
return false; | |||||
} | |||||
memset(large.get() + sizeof(kLargePrefix), 0, kLargePayloadLen); | |||||
memcpy(large.get(), kLargePrefix, sizeof(kLargePrefix)); | |||||
if (!ReadASN1(true, large.get(), sizeof(kLargePrefix) + kLargePayloadLen, | |||||
sizeof(kLargePrefix) + kLargePayloadLen, | |||||
kLargePayloadLen * 2)) { | |||||
fprintf(stderr, "Large payload test failed.\n"); | |||||
return false; | |||||
} | |||||
if (!ReadASN1(false, large.get(), sizeof(kLargePrefix) + kLargePayloadLen, | |||||
sizeof(kLargePrefix) + kLargePayloadLen, | |||||
kLargePayloadLen - 1)) { | |||||
fprintf(stderr, "max_len test failed.\n"); | |||||
return false; | |||||
} | |||||
static const uint8_t kIndefPrefix[] = {0x30, 0x80}; | |||||
memcpy(large.get(), kIndefPrefix, sizeof(kIndefPrefix)); | |||||
if (!ReadASN1(true, large.get(), sizeof(kLargePrefix) + kLargePayloadLen, | |||||
sizeof(kLargePrefix) + kLargePayloadLen, | |||||
kLargePayloadLen*2)) { | |||||
fprintf(stderr, "indefinite length test failed.\n"); | |||||
return false; | |||||
} | |||||
if (!ReadASN1(false, large.get(), sizeof(kLargePrefix) + kLargePayloadLen, | |||||
sizeof(kLargePrefix) + kLargePayloadLen, | |||||
kLargePayloadLen-1)) { | |||||
fprintf(stderr, "indefinite length, max_len test failed.\n"); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
int main(void) { | int main(void) { | ||||
CRYPTO_library_init(); | CRYPTO_library_init(); | ||||
ERR_load_crypto_strings(); | ERR_load_crypto_strings(); | ||||
@@ -350,7 +431,8 @@ int main(void) { | |||||
if (!TestSocketConnect() || | if (!TestSocketConnect() || | ||||
!TestPrintf() || | !TestPrintf() || | ||||
!TestZeroCopyBioPairs()) { | |||||
!TestZeroCopyBioPairs() || | |||||
!TestASN1()) { | |||||
return 1; | return 1; | ||||
} | } | ||||
@@ -339,6 +339,21 @@ OPENSSL_EXPORT int BIO_hexdump(BIO *bio, const uint8_t *data, size_t len, | |||||
* using human readable strings where possible. */ | * using human readable strings where possible. */ | ||||
OPENSSL_EXPORT void BIO_print_errors(BIO *bio); | OPENSSL_EXPORT void BIO_print_errors(BIO *bio); | ||||
/* BIO_read_asn1 reads a single ASN.1 object from |bio|. If successful it sets | |||||
* |*out| to be an allocated buffer (that should be freed with |OPENSSL_free|), | |||||
* |*out_size| to the length, in bytes, of that buffer and returns one. | |||||
* Otherwise it returns zero. | |||||
* | |||||
* If the length of the object is greater than |max_len| or 2^32 then the | |||||
* function will fail. Long-form tags are not supported. If the length of the | |||||
* object is indefinite the full contents of |bio| are read, unless it would be | |||||
* greater than |max_len|, in which case the function fails. | |||||
* | |||||
* If the function fails then some unknown amount of data may have been read | |||||
* from |bio|. */ | |||||
OPENSSL_EXPORT int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, | |||||
size_t max_len); | |||||
/* Memory BIOs. | /* Memory BIOs. | ||||
* | * | ||||