From 7c7c988892faf5207cd0e069e5df4e5f20f009f5 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Fri, 7 Nov 2014 20:45:27 -0800 Subject: Bug 1064670 - (CVE-2014-1569) ASN.1 DER decoding of lengths is too permissive, allowing undetected smuggling of arbitrary data (r=wtc) --- lib/util/quickder.c | 91 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/lib/util/quickder.c b/lib/util/quickder.c index 6f518ddfd..f9776bb9d 100644 --- a/lib/util/quickder.c +++ b/lib/util/quickder.c @@ -16,55 +16,110 @@ */ static unsigned char* definite_length_decoder(const unsigned char *buf, - const unsigned int length, - unsigned int *data_length, + const unsigned int buf_length, + unsigned int *out_data_length, PRBool includeTag) { unsigned char tag; - unsigned int used_length= 0; - unsigned int data_len; + unsigned int used_length = 0; + unsigned int data_length = 0; + unsigned char length_field_len = 0; + unsigned char byte; + unsigned int i; - if (used_length >= length) + if (used_length >= buf_length) { + /* Tag field was not found! */ return NULL; } tag = buf[used_length++]; - /* blow out when we come to the end */ if (tag == 0) { + /* End-of-contents octects should not be present in DER because + DER doesn't use the indefinite length form. */ return NULL; } - if (used_length >= length) + if ((tag & 0x1F) == 0x1F) { + /* High tag number (a tag number > 30) is not supported */ return NULL; } - data_len = buf[used_length++]; - if (data_len&0x80) + if (used_length >= buf_length) { - int len_count = data_len & 0x7f; + /* Length field was not found! */ + return NULL; + } + byte = buf[used_length++]; - data_len = 0; + if (!(byte & 0x80)) + { + /* Short form: The high bit is not set. */ + data_length = byte; /* clarity; we're returning a 32-bit int. */ + } + else + { + /* Long form. Extract the field length */ + length_field_len = byte & 0x7F; + if (length_field_len == 0) + { + /* DER doesn't use the indefinite length form. */ + return NULL; + } + + if (length_field_len > sizeof(data_length)) + { + /* We don't support an extended length field longer than + 4 bytes (2^32) */ + return NULL; + } - while (len_count-- > 0) + if (length_field_len > (buf_length - used_length)) { - if (used_length >= length) + /* Extended length field was not found */ + return NULL; + } + + /* Iterate across the extended length field */ + for (i = 0; i < length_field_len; i++) + { + byte = buf[used_length++]; + data_length = (data_length << 8) | byte; + + if (i == 0) { - return NULL; + PRBool too_long = PR_FALSE; + if (length_field_len == 1) + { + too_long = ((byte & 0x80) == 0); /* Short form suffices */ + } + else + { + too_long = (byte == 0); /* This zero byte can be omitted */ + } + if (too_long) + { + /* The length is longer than needed. */ + return NULL; + } } - data_len = (data_len << 8) | buf[used_length++]; } } - if (data_len > (length-used_length) ) + if (data_length > (buf_length - used_length)) { + /* The decoded length exceeds the available buffer */ return NULL; } - if (includeTag) data_len += used_length; - *data_length = data_len; + if (includeTag) + { + data_length += used_length; + } + + *out_data_length = data_length; return ((unsigned char*)buf + (includeTag ? 0 : used_length)); } -- cgit v1.2.1