Guard against DoS in name constraints handling.
[openssl.git] / crypto / x509v3 / v3_ncons.c
index e6775ebaf912bf3fbaaa70b7e742601e5d0df5aa..561128e3faddeb8bef86fd90a8e5e135e4373db5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2003-2017 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the OpenSSL license (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -7,8 +7,11 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include <stdio.h>
+#include "e_os.h"               /* for strncasecmp */
 #include "internal/cryptlib.h"
+#include <limits.h>
+#include <stdio.h>
+#include "internal/asn1_int.h"
 #include <openssl/asn1t.h>
 #include <openssl/conf.h>
 #include <openssl/x509v3.h>
@@ -23,7 +26,7 @@ static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
                                 BIO *bp, int ind);
 static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
                                    STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp,
-                                   int ind, char *name);
+                                   int ind, const char *name);
 static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
 
 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
@@ -122,7 +125,7 @@ static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
 
 static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
                                    STACK_OF(GENERAL_SUBTREE) *trees,
-                                   BIO *bp, int ind, char *name)
+                                   BIO *bp, int ind, const char *name)
 {
     GENERAL_SUBTREE *tree;
     int i;
@@ -164,6 +167,22 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
     return 1;
 }
 
+#define NAME_CHECK_MAX (1 << 20)
+
+static int add_lengths(int *out, int a, int b)
+{
+    /* sk_FOO_num(NULL) returns -1 but is effectively 0 when iterating. */
+    if (a < 0)
+        a = 0;
+    if (b < 0)
+        b = 0;
+
+    if (a > INT_MAX - b)
+        return 0;
+    *out = a + b;
+    return 1;
+}
+
 /*-
  * Check a certificate conforms to a specified set of constraints.
  * Return values:
@@ -178,11 +197,23 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
 
 int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
 {
-    int r, i;
+    int r, i, name_count, constraint_count;
     X509_NAME *nm;
 
     nm = X509_get_subject_name(x);
 
+    /*
+     * Guard against certificates with an excessive number of names or
+     * constraints causing a computationally expensive name constraints check.
+     */
+    if (!add_lengths(&name_count, X509_NAME_entry_count(nm),
+                     sk_GENERAL_NAME_num(x->altname))
+        || !add_lengths(&constraint_count,
+                        sk_GENERAL_SUBTREE_num(nc->permittedSubtrees),
+                        sk_GENERAL_SUBTREE_num(nc->excludedSubtrees))
+        || (name_count > 0 && constraint_count > NAME_CHECK_MAX / name_count))
+        return X509_V_ERR_UNSPECIFIED;
+
     if (X509_NAME_entry_count(nm) > 0) {
         GENERAL_NAME gntmp;
         gntmp.type = GEN_DIRNAME;
@@ -198,7 +229,8 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
         /* Process any email address attributes in subject name */
 
         for (i = -1;;) {
-            X509_NAME_ENTRY *ne;
+            const X509_NAME_ENTRY *ne;
+
             i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i);
             if (i == -1)
                 break;
@@ -226,6 +258,52 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
 
 }
 
+int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc)
+{
+    int r, i;
+    X509_NAME *nm;
+
+    ASN1_STRING stmp;
+    GENERAL_NAME gntmp;
+    stmp.flags = 0;
+    stmp.type = V_ASN1_IA5STRING;
+    gntmp.type = GEN_DNS;
+    gntmp.d.dNSName = &stmp;
+
+    nm = X509_get_subject_name(x);
+
+    /* Process any commonName attributes in subject name */
+
+    for (i = -1;;) {
+        X509_NAME_ENTRY *ne;
+        ASN1_STRING *hn;
+
+        i = X509_NAME_get_index_by_NID(nm, NID_commonName, i);
+        if (i == -1)
+            break;
+        ne = X509_NAME_get_entry(nm, i);
+        hn = X509_NAME_ENTRY_get_data(ne);
+        /* Only process attributes that look like host names */
+        if (asn1_valid_host(hn)) {
+            unsigned char *h;
+            int hlen = ASN1_STRING_to_UTF8(&h, hn);
+            if (hlen <= 0)
+                return X509_V_ERR_OUT_OF_MEM;
+
+            stmp.length = hlen;
+            stmp.data = h;
+
+            r = nc_match(&gntmp, nc);
+
+            OPENSSL_free(h);
+
+            if (r != X509_V_OK)
+                    return r;
+        }
+    }
+    return X509_V_OK;
+}
+
 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
 {
     GENERAL_SUBTREE *sub;