Fix BN_hex2bn/BN_dec2bn NULL ptr/heap corruption
authorMatt Caswell <matt@openssl.org>
Mon, 22 Feb 2016 10:27:18 +0000 (10:27 +0000)
committerMatt Caswell <matt@openssl.org>
Mon, 29 Feb 2016 16:32:18 +0000 (16:32 +0000)
In the BN_hex2bn function the number of hex digits is calculated using
an int value |i|. Later |bn_expand| is called with a value of |i * 4|.
For large values of |i| this can result in |bn_expand| not allocating any
memory because |i * 4| is negative. This leaves ret->d as NULL leading
to a subsequent NULL ptr deref. For very large values of |i|, the
calculation |i * 4| could be a positive value smaller than |i|. In this
case memory is allocated to ret->d, but it is insufficiently sized
leading to heap corruption. A similar issue exists in BN_dec2bn.

This could have security consequences if BN_hex2bn/BN_dec2bn is ever
called by user applications with very large untrusted hex/dec data. This is
anticipated to be a rare occurrence.

All OpenSSL internal usage of this function uses data that is not expected
to be untrusted, e.g. config file data or application command line
arguments. If user developed applications generate config file data based
on untrusted data then it is possible that this could also lead to security
consequences. This is also anticipated to be a rare.

Issue reported by Guido Vranken.

CVE-2016-0797

Reviewed-by: Andy Polyakov <appro@openssl.org>
crypto/bn/bn_lcl.h
crypto/bn/bn_print.c
crypto/include/internal/bn_int.h

index 4af8bfb9a3d6320dfa5da2e0f3cafd82b698fd23..412740d4ba007fdb0812e80fe18c9e9ce8f4aa02 100644 (file)
@@ -774,6 +774,17 @@ int bn_probable_prime_dh(BIGNUM *rnd, int bits,
 int bn_probable_prime_dh_retry(BIGNUM *rnd, int bits, BN_CTX *ctx);
 int bn_probable_prime_dh_coprime(BIGNUM *rnd, int bits, BN_CTX *ctx);
 
+static ossl_inline BIGNUM *bn_expand(BIGNUM *a, int bits)
+{
+    if (bits > (INT_MAX - BN_BITS2 + 1))
+        return NULL;
+
+    if(((bits+BN_BITS2-1)/BN_BITS2) <= (a)->dmax)
+        return a;
+
+    return bn_expand2((a),(bits+BN_BITS2-1)/BN_BITS2);
+}
+
 #ifdef  __cplusplus
 }
 #endif
index b73ed0a6700b9cb90fef390de6bce506f21efb1d..0c3b214f12a875e1d1e47f35af75c066e6b0fac3 100644 (file)
@@ -57,6 +57,7 @@
 
 #include <stdio.h>
 #include <ctype.h>
+#include <limits.h>
 #include "internal/cryptlib.h"
 #include <openssl/buffer.h>
 #include "bn_lcl.h"
@@ -183,7 +184,11 @@ int BN_hex2bn(BIGNUM **bn, const char *a)
         a++;
     }
 
-    for (i = 0; isxdigit((unsigned char)a[i]); i++) ;
+    for (i = 0; i <= (INT_MAX/4) && isxdigit((unsigned char)a[i]); i++)
+        continue;
+
+    if (i > INT_MAX/4)
+        goto err;
 
     num = i + neg;
     if (bn == NULL)
@@ -198,7 +203,7 @@ int BN_hex2bn(BIGNUM **bn, const char *a)
         BN_zero(ret);
     }
 
-    /* i is the number of hex digests; */
+    /* i is the number of hex digits */
     if (bn_expand(ret, i * 4) == NULL)
         goto err;
 
@@ -254,7 +259,11 @@ int BN_dec2bn(BIGNUM **bn, const char *a)
         a++;
     }
 
-    for (i = 0; isdigit((unsigned char)a[i]); i++) ;
+    for (i = 0; i <= (INT_MAX/4) && isdigit((unsigned char)a[i]); i++)
+        continue;
+
+    if (i > INT_MAX/4)
+        goto err;
 
     num = i + neg;
     if (bn == NULL)
@@ -272,7 +281,7 @@ int BN_dec2bn(BIGNUM **bn, const char *a)
         BN_zero(ret);
     }
 
-    /* i is the number of digests, a bit of an over expand; */
+    /* i is the number of digits, a bit of an over expand */
     if (bn_expand(ret, i * 4) == NULL)
         goto err;
 
index a7c0fd4879d248371b85ebaf2b985ce20940166f..8ea51936068fc4ff4dc86e5a48de0d90148796dd 100644 (file)
 # define HEADER_BN_INT_H
 
 # include <openssl/bn.h>
+# include <limits.h>
 
 #ifdef  __cplusplus
 extern "C" {
 #endif
 
-# define bn_expand(a,bits) ((((((bits+BN_BITS2-1))/BN_BITS2)) <= (a)->dmax)?\
-        (a):bn_expand2((a),(bits+BN_BITS2-1)/BN_BITS2))
 BIGNUM *bn_wexpand(BIGNUM *a, int words);
 BIGNUM *bn_expand2(BIGNUM *a, int words);