• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From 879f7080d7e141f415c79eaa3a8ac4a3dad0348b Mon Sep 17 00:00:00 2001
2From: Pauli <pauli@openssl.org>
3Date: Wed, 8 Mar 2023 15:28:20 +1100
4Subject: [PATCH] x509: excessive resource use verifying policy constraints
5
6A security vulnerability has been identified in all supported versions
7of OpenSSL related to the verification of X.509 certificate chains
8that include policy constraints.  Attackers may be able to exploit this
9vulnerability by creating a malicious certificate chain that triggers
10exponential use of computational resources, leading to a denial-of-service
11(DoS) attack on affected systems.
12
13Fixes CVE-2023-0464
14
15Reviewed-by: Tomas Mraz <tomas@openssl.org>
16Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
17(Merged from https://github.com/openssl/openssl/pull/20569)
18---
19 crypto/x509v3/pcy_local.h |  8 +++++++-
20 crypto/x509v3/pcy_node.c  | 12 +++++++++---
21 crypto/x509v3/pcy_tree.c  | 37 +++++++++++++++++++++++++++----------
22 3 files changed, 43 insertions(+), 14 deletions(-)
23
24diff --git a/crypto/x509v3/pcy_local.h b/crypto/x509v3/pcy_local.h
25index 5daf78de45..344aa06765 100644
26--- a/crypto/x509v3/pcy_local.h
27+++ b/crypto/x509v3/pcy_local.h
28@@ -111,6 +111,11 @@ struct X509_POLICY_LEVEL_st {
29 };
30
31 struct X509_POLICY_TREE_st {
32+    /* The number of nodes in the tree */
33+    size_t node_count;
34+    /* The maximum number of nodes in the tree */
35+    size_t node_maximum;
36+
37     /* This is the tree 'level' data */
38     X509_POLICY_LEVEL *levels;
39     int nlevel;
40@@ -159,7 +164,8 @@ X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk,
41 X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
42                                  X509_POLICY_DATA *data,
43                                  X509_POLICY_NODE *parent,
44-                                 X509_POLICY_TREE *tree);
45+                                 X509_POLICY_TREE *tree,
46+                                 int extra_data);
47 void policy_node_free(X509_POLICY_NODE *node);
48 int policy_node_match(const X509_POLICY_LEVEL *lvl,
49                       const X509_POLICY_NODE *node, const ASN1_OBJECT *oid);
50diff --git a/crypto/x509v3/pcy_node.c b/crypto/x509v3/pcy_node.c
51index e2d7b15322..d574fb9d66 100644
52--- a/crypto/x509v3/pcy_node.c
53+++ b/crypto/x509v3/pcy_node.c
54@@ -59,10 +59,15 @@ X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level,
55 X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
56                                  X509_POLICY_DATA *data,
57                                  X509_POLICY_NODE *parent,
58-                                 X509_POLICY_TREE *tree)
59+                                 X509_POLICY_TREE *tree,
60+                                 int extra_data)
61 {
62     X509_POLICY_NODE *node;
63
64+    /* Verify that the tree isn't too large.  This mitigates CVE-2023-0464 */
65+    if (tree->node_maximum > 0 && tree->node_count >= tree->node_maximum)
66+        return NULL;
67+
68     node = OPENSSL_zalloc(sizeof(*node));
69     if (node == NULL) {
70         X509V3err(X509V3_F_LEVEL_ADD_NODE, ERR_R_MALLOC_FAILURE);
71@@ -70,7 +75,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
72     }
73     node->data = data;
74     node->parent = parent;
75-    if (level) {
76+    if (level != NULL) {
77         if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) {
78             if (level->anyPolicy)
79                 goto node_error;
80@@ -90,7 +95,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
81         }
82     }
83
84-    if (tree) {
85+    if (extra_data) {
86         if (tree->extra_data == NULL)
87             tree->extra_data = sk_X509_POLICY_DATA_new_null();
88         if (tree->extra_data == NULL){
89@@ -103,6 +108,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
90         }
91     }
92
93+    tree->node_count++;
94     if (parent)
95         parent->nchild++;
96
97diff --git a/crypto/x509v3/pcy_tree.c b/crypto/x509v3/pcy_tree.c
98index 6e8322cbc5..6c7fd35405 100644
99--- a/crypto/x509v3/pcy_tree.c
100+++ b/crypto/x509v3/pcy_tree.c
101@@ -13,6 +13,18 @@
102
103 #include "pcy_local.h"
104
105+/*
106+ * If the maximum number of nodes in the policy tree isn't defined, set it to
107+ * a generous default of 1000 nodes.
108+ *
109+ * Defining this to be zero means unlimited policy tree growth which opens the
110+ * door on CVE-2023-0464.
111+ */
112+
113+#ifndef OPENSSL_POLICY_TREE_NODES_MAX
114+# define OPENSSL_POLICY_TREE_NODES_MAX 1000
115+#endif
116+
117 /*
118  * Enable this to print out the complete policy tree at various point during
119  * evaluation.
120@@ -168,6 +180,9 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
121         return X509_PCY_TREE_INTERNAL;
122     }
123
124+    /* Limit the growth of the tree to mitigate CVE-2023-0464 */
125+    tree->node_maximum = OPENSSL_POLICY_TREE_NODES_MAX;
126+
127     /*
128      * http://tools.ietf.org/html/rfc5280#section-6.1.2, figure 3.
129      *
130@@ -184,7 +199,7 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
131     level = tree->levels;
132     if ((data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0)) == NULL)
133         goto bad_tree;
134-    if (level_add_node(level, data, NULL, tree) == NULL) {
135+    if (level_add_node(level, data, NULL, tree, 1) == NULL) {
136         policy_data_free(data);
137         goto bad_tree;
138     }
139@@ -243,7 +258,8 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
140  * Return value: 1 on success, 0 otherwise
141  */
142 static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
143-                                    X509_POLICY_DATA *data)
144+                                    X509_POLICY_DATA *data,
145+                                    X509_POLICY_TREE *tree)
146 {
147     X509_POLICY_LEVEL *last = curr - 1;
148     int i, matched = 0;
149@@ -253,13 +269,13 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
150         X509_POLICY_NODE *node = sk_X509_POLICY_NODE_value(last->nodes, i);
151
152         if (policy_node_match(last, node, data->valid_policy)) {
153-            if (level_add_node(curr, data, node, NULL) == NULL)
154+            if (level_add_node(curr, data, node, tree, 0) == NULL)
155                 return 0;
156             matched = 1;
157         }
158     }
159     if (!matched && last->anyPolicy) {
160-        if (level_add_node(curr, data, last->anyPolicy, NULL) == NULL)
161+        if (level_add_node(curr, data, last->anyPolicy, tree, 0) == NULL)
162             return 0;
163     }
164     return 1;
165@@ -272,7 +288,8 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
166  * Return value: 1 on success, 0 otherwise.
167  */
168 static int tree_link_nodes(X509_POLICY_LEVEL *curr,
169-                           const X509_POLICY_CACHE *cache)
170+                           const X509_POLICY_CACHE *cache,
171+                           X509_POLICY_TREE *tree)
172 {
173     int i;
174
175@@ -280,7 +297,7 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr,
176         X509_POLICY_DATA *data = sk_X509_POLICY_DATA_value(cache->data, i);
177
178         /* Look for matching nodes in previous level */
179-        if (!tree_link_matching_nodes(curr, data))
180+        if (!tree_link_matching_nodes(curr, data, tree))
181             return 0;
182     }
183     return 1;
184@@ -311,7 +328,7 @@ static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
185     /* Curr may not have anyPolicy */
186     data->qualifier_set = cache->anyPolicy->qualifier_set;
187     data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
188-    if (level_add_node(curr, data, node, tree) == NULL) {
189+    if (level_add_node(curr, data, node, tree, 1) == NULL) {
190         policy_data_free(data);
191         return 0;
192     }
193@@ -373,7 +390,7 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
194     }
195     /* Finally add link to anyPolicy */
196     if (last->anyPolicy &&
197-        level_add_node(curr, cache->anyPolicy, last->anyPolicy, NULL) == NULL)
198+        level_add_node(curr, cache->anyPolicy, last->anyPolicy, tree, 0) == NULL)
199         return 0;
200     return 1;
201 }
202@@ -555,7 +572,7 @@ static int tree_calculate_user_set(X509_POLICY_TREE *tree,
203             extra->qualifier_set = anyPolicy->data->qualifier_set;
204             extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS
205                 | POLICY_DATA_FLAG_EXTRA_NODE;
206-            node = level_add_node(NULL, extra, anyPolicy->parent, tree);
207+            node = level_add_node(NULL, extra, anyPolicy->parent, tree, 1);
208         }
209         if (!tree->user_policies) {
210             tree->user_policies = sk_X509_POLICY_NODE_new_null();
211@@ -582,7 +599,7 @@ static int tree_evaluate(X509_POLICY_TREE *tree)
212
213     for (i = 1; i < tree->nlevel; i++, curr++) {
214         cache = policy_cache_set(curr->cert);
215-        if (!tree_link_nodes(curr, cache))
216+        if (!tree_link_nodes(curr, cache, tree))
217             return X509_PCY_TREE_INTERNAL;
218
219         if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY)
220--
2212.34.1
222
223