summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ.C. Jones <jjones@mozilla.com>2014-11-07 20:45:27 -0800
committerJ.C. Jones <jjones@mozilla.com>2014-11-07 20:45:27 -0800
commit7c7c988892faf5207cd0e069e5df4e5f20f009f5 (patch)
tree780b1c1671b2c627acfbbe0a697d4e08f90fe660
parentd6f22c616f6e7d788004d5c52bc62826292d90d8 (diff)
downloadnss-hg-NSS_3_16_2_BRANCH.tar.gz
Bug 1064670 - (CVE-2014-1569) ASN.1 DER decoding of lengths is too permissive, allowing undetected smuggling of arbitrary data (r=wtc)NSS_3_16_2_BRANCH
-rw-r--r--lib/util/quickder.c91
1 files 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));
}