Skip to content

Commit 959c59c

Browse files
committed
x509: excessive resource use verifying policy constraints
A security vulnerability has been identified in all supported versions of OpenSSL related to the verification of X.509 certificate chains that include policy constraints. Attackers may be able to exploit this vulnerability by creating a malicious certificate chain that triggers exponential use of computational resources, leading to a denial-of-service (DoS) attack on affected systems. Fixes CVE-2023-0464 Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Shane Lontis <shane.lontis@oracle.com> (Merged from #20568)
1 parent c309c4d commit 959c59c

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

crypto/x509/pcy_local.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ struct X509_POLICY_LEVEL_st {
111111
};
112112

113113
struct X509_POLICY_TREE_st {
114+
/* The number of nodes in the tree */
115+
size_t node_count;
116+
/* The maximum number of nodes in the tree */
117+
size_t node_maximum;
118+
114119
/* This is the tree 'level' data */
115120
X509_POLICY_LEVEL *levels;
116121
int nlevel;
@@ -157,7 +162,8 @@ X509_POLICY_NODE *ossl_policy_tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk,
157162
X509_POLICY_NODE *ossl_policy_level_add_node(X509_POLICY_LEVEL *level,
158163
X509_POLICY_DATA *data,
159164
X509_POLICY_NODE *parent,
160-
X509_POLICY_TREE *tree);
165+
X509_POLICY_TREE *tree,
166+
int extra_data);
161167
void ossl_policy_node_free(X509_POLICY_NODE *node);
162168
int ossl_policy_node_match(const X509_POLICY_LEVEL *lvl,
163169
const X509_POLICY_NODE *node, const ASN1_OBJECT *oid);

crypto/x509/pcy_node.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,23 @@ X509_POLICY_NODE *ossl_policy_level_find_node(const X509_POLICY_LEVEL *level,
5959
X509_POLICY_NODE *ossl_policy_level_add_node(X509_POLICY_LEVEL *level,
6060
X509_POLICY_DATA *data,
6161
X509_POLICY_NODE *parent,
62-
X509_POLICY_TREE *tree)
62+
X509_POLICY_TREE *tree,
63+
int extra_data)
6364
{
6465
X509_POLICY_NODE *node;
6566

67+
/* Verify that the tree isn't too large. This mitigates CVE-2023-0464 */
68+
if (tree->node_maximum > 0 && tree->node_count >= tree->node_maximum)
69+
return NULL;
70+
6671
node = OPENSSL_zalloc(sizeof(*node));
6772
if (node == NULL) {
6873
ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE);
6974
return NULL;
7075
}
7176
node->data = data;
7277
node->parent = parent;
73-
if (level) {
78+
if (level != NULL) {
7479
if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) {
7580
if (level->anyPolicy)
7681
goto node_error;
@@ -90,7 +95,7 @@ X509_POLICY_NODE *ossl_policy_level_add_node(X509_POLICY_LEVEL *level,
9095
}
9196
}
9297

93-
if (tree) {
98+
if (extra_data) {
9499
if (tree->extra_data == NULL)
95100
tree->extra_data = sk_X509_POLICY_DATA_new_null();
96101
if (tree->extra_data == NULL){
@@ -103,6 +108,7 @@ X509_POLICY_NODE *ossl_policy_level_add_node(X509_POLICY_LEVEL *level,
103108
}
104109
}
105110

111+
tree->node_count++;
106112
if (parent)
107113
parent->nchild++;
108114

crypto/x509/pcy_tree.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@
1414

1515
#include "pcy_local.h"
1616

17+
/*
18+
* If the maximum number of nodes in the policy tree isn't defined, set it to
19+
* a generous default of 1000 nodes.
20+
*
21+
* Defining this to be zero means unlimited policy tree growth which opens the
22+
* door on CVE-2023-0464.
23+
*/
24+
#ifndef OPENSSL_POLICY_TREE_NODES_MAX
25+
# define OPENSSL_POLICY_TREE_NODES_MAX 1000
26+
#endif
27+
1728
static void expected_print(BIO *channel,
1829
X509_POLICY_LEVEL *lev, X509_POLICY_NODE *node,
1930
int indent)
@@ -163,6 +174,9 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
163174
return X509_PCY_TREE_INTERNAL;
164175
}
165176

177+
/* Limit the growth of the tree to mitigate CVE-2023-0464 */
178+
tree->node_maximum = OPENSSL_POLICY_TREE_NODES_MAX;
179+
166180
/*
167181
* http://tools.ietf.org/html/rfc5280#section-6.1.2, figure 3.
168182
*
@@ -180,7 +194,7 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
180194
if ((data = ossl_policy_data_new(NULL,
181195
OBJ_nid2obj(NID_any_policy), 0)) == NULL)
182196
goto bad_tree;
183-
if (ossl_policy_level_add_node(level, data, NULL, tree) == NULL) {
197+
if (ossl_policy_level_add_node(level, data, NULL, tree, 1) == NULL) {
184198
ossl_policy_data_free(data);
185199
goto bad_tree;
186200
}
@@ -239,7 +253,8 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
239253
* Return value: 1 on success, 0 otherwise
240254
*/
241255
static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
242-
X509_POLICY_DATA *data)
256+
X509_POLICY_DATA *data,
257+
X509_POLICY_TREE *tree)
243258
{
244259
X509_POLICY_LEVEL *last = curr - 1;
245260
int i, matched = 0;
@@ -249,13 +264,13 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
249264
X509_POLICY_NODE *node = sk_X509_POLICY_NODE_value(last->nodes, i);
250265

251266
if (ossl_policy_node_match(last, node, data->valid_policy)) {
252-
if (ossl_policy_level_add_node(curr, data, node, NULL) == NULL)
267+
if (ossl_policy_level_add_node(curr, data, node, tree, 0) == NULL)
253268
return 0;
254269
matched = 1;
255270
}
256271
}
257272
if (!matched && last->anyPolicy) {
258-
if (ossl_policy_level_add_node(curr, data, last->anyPolicy, NULL) == NULL)
273+
if (ossl_policy_level_add_node(curr, data, last->anyPolicy, tree, 0) == NULL)
259274
return 0;
260275
}
261276
return 1;
@@ -268,15 +283,16 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
268283
* Return value: 1 on success, 0 otherwise.
269284
*/
270285
static int tree_link_nodes(X509_POLICY_LEVEL *curr,
271-
const X509_POLICY_CACHE *cache)
286+
const X509_POLICY_CACHE *cache,
287+
X509_POLICY_TREE *tree)
272288
{
273289
int i;
274290

275291
for (i = 0; i < sk_X509_POLICY_DATA_num(cache->data); i++) {
276292
X509_POLICY_DATA *data = sk_X509_POLICY_DATA_value(cache->data, i);
277293

278294
/* Look for matching nodes in previous level */
279-
if (!tree_link_matching_nodes(curr, data))
295+
if (!tree_link_matching_nodes(curr, data, tree))
280296
return 0;
281297
}
282298
return 1;
@@ -307,7 +323,7 @@ static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
307323
/* Curr may not have anyPolicy */
308324
data->qualifier_set = cache->anyPolicy->qualifier_set;
309325
data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
310-
if (ossl_policy_level_add_node(curr, data, node, tree) == NULL) {
326+
if (ossl_policy_level_add_node(curr, data, node, tree, 1) == NULL) {
311327
ossl_policy_data_free(data);
312328
return 0;
313329
}
@@ -370,7 +386,7 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
370386
/* Finally add link to anyPolicy */
371387
if (last->anyPolicy &&
372388
ossl_policy_level_add_node(curr, cache->anyPolicy,
373-
last->anyPolicy, NULL) == NULL)
389+
last->anyPolicy, tree, 0) == NULL)
374390
return 0;
375391
return 1;
376392
}
@@ -553,7 +569,7 @@ static int tree_calculate_user_set(X509_POLICY_TREE *tree,
553569
extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS
554570
| POLICY_DATA_FLAG_EXTRA_NODE;
555571
node = ossl_policy_level_add_node(NULL, extra, anyPolicy->parent,
556-
tree);
572+
tree, 1);
557573
}
558574
if (!tree->user_policies) {
559575
tree->user_policies = sk_X509_POLICY_NODE_new_null();
@@ -580,7 +596,7 @@ static int tree_evaluate(X509_POLICY_TREE *tree)
580596

581597
for (i = 1; i < tree->nlevel; i++, curr++) {
582598
cache = ossl_policy_cache_set(curr->cert);
583-
if (!tree_link_nodes(curr, cache))
599+
if (!tree_link_nodes(curr, cache, tree))
584600
return X509_PCY_TREE_INTERNAL;
585601

586602
if (!(curr->flags & X509_V_FLAG_INHIBIT_ANY)

0 commit comments

Comments
 (0)