Skip to content

Commit 2dcd4f1

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: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org>
1 parent a3b93ac commit 2dcd4f1

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

Diff for: crypto/x509v3/pcy_int.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ struct X509_POLICY_LEVEL_st {
161161
};
162162

163163
struct X509_POLICY_TREE_st {
164+
/* The number of nodes in the tree */
165+
size_t node_count;
166+
/* The maximum number of nodes in the tree */
167+
size_t node_maximum;
168+
164169
/* This is the tree 'level' data */
165170
X509_POLICY_LEVEL *levels;
166171
int nlevel;
@@ -209,7 +214,8 @@ X509_POLICY_NODE *tree_find_sk(STACK_OF(X509_POLICY_NODE) *sk,
209214
X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
210215
const X509_POLICY_DATA *data,
211216
X509_POLICY_NODE *parent,
212-
X509_POLICY_TREE *tree);
217+
X509_POLICY_TREE *tree,
218+
int extra_data);
213219
void policy_node_free(X509_POLICY_NODE *node);
214220
int policy_node_match(const X509_POLICY_LEVEL *lvl,
215221
const X509_POLICY_NODE *node, const ASN1_OBJECT *oid);

Diff for: crypto/x509v3/pcy_node.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,15 @@ X509_POLICY_NODE *level_find_node(const X509_POLICY_LEVEL *level,
111111
X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
112112
const X509_POLICY_DATA *data,
113113
X509_POLICY_NODE *parent,
114-
X509_POLICY_TREE *tree)
114+
X509_POLICY_TREE *tree,
115+
int extra_data)
115116
{
116117
X509_POLICY_NODE *node;
118+
119+
/* Verify that the tree isn't too large. This mitigates CVE-2023-0464 */
120+
if (tree->node_maximum > 0 && tree->node_count >= tree->node_maximum)
121+
return NULL;
122+
117123
node = OPENSSL_malloc(sizeof(X509_POLICY_NODE));
118124
if (!node)
119125
return NULL;
@@ -136,7 +142,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
136142
}
137143
}
138144

139-
if (tree) {
145+
if (extra_data) {
140146
if (!tree->extra_data)
141147
tree->extra_data = sk_X509_POLICY_DATA_new_null();
142148
if (!tree->extra_data)
@@ -145,6 +151,7 @@ X509_POLICY_NODE *level_add_node(X509_POLICY_LEVEL *level,
145151
goto node_error;
146152
}
147153

154+
tree->node_count++;
148155
if (parent)
149156
parent->nchild++;
150157

Diff for: crypto/x509v3/pcy_tree.c

+31-14
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@
6363

6464
#include "pcy_int.h"
6565

66+
/*
67+
* If the maximum number of nodes in the policy tree isn't defined, set it to
68+
* a generous default of 1000 nodes.
69+
*
70+
* Defining this to be zero means unlimited policy tree growth which opens the
71+
* door on CVE-2023-0464.
72+
*/
73+
74+
#ifndef OPENSSL_POLICY_TREE_NODES_MAX
75+
# define OPENSSL_POLICY_TREE_NODES_MAX 1000
76+
#endif
77+
6678
/*
6779
* Enable this to print out the complete policy tree at various point during
6880
* evaluation.
@@ -225,6 +237,10 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
225237
if (!tree)
226238
return 0;
227239

240+
/* Limit the growth of the tree to mitigate CVE-2023-0464 */
241+
tree->node_maximum = OPENSSL_POLICY_TREE_NODES_MAX;
242+
tree->node_count = 0;
243+
228244
tree->flags = 0;
229245
tree->levels = OPENSSL_malloc(sizeof(X509_POLICY_LEVEL) * n);
230246
tree->nlevel = 0;
@@ -247,7 +263,7 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
247263

248264
data = policy_data_new(NULL, OBJ_nid2obj(NID_any_policy), 0);
249265

250-
if (!data || !level_add_node(level, data, NULL, tree))
266+
if (!data || !level_add_node(level, data, NULL, tree, 1))
251267
goto bad_tree;
252268

253269
for (i = n - 2; i >= 0; i--) {
@@ -304,7 +320,8 @@ static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
304320
}
305321

306322
static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
307-
const X509_POLICY_DATA *data)
323+
const X509_POLICY_DATA *data,
324+
X509_POLICY_TREE *tree)
308325
{
309326
X509_POLICY_LEVEL *last = curr - 1;
310327
X509_POLICY_NODE *node;
@@ -313,13 +330,13 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
313330
for (i = 0; i < sk_X509_POLICY_NODE_num(last->nodes); i++) {
314331
node = sk_X509_POLICY_NODE_value(last->nodes, i);
315332
if (policy_node_match(last, node, data->valid_policy)) {
316-
if (!level_add_node(curr, data, node, NULL))
333+
if (level_add_node(curr, data, node, tree, 0) == NULL)
317334
return 0;
318335
matched = 1;
319336
}
320337
}
321338
if (!matched && last->anyPolicy) {
322-
if (!level_add_node(curr, data, last->anyPolicy, NULL))
339+
if (level_add_node(curr, data, last->anyPolicy, tree, 0) == NULL)
323340
return 0;
324341
}
325342
return 1;
@@ -331,7 +348,8 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr,
331348
*/
332349

333350
static int tree_link_nodes(X509_POLICY_LEVEL *curr,
334-
const X509_POLICY_CACHE *cache)
351+
const X509_POLICY_CACHE *cache,
352+
X509_POLICY_TREE *tree)
335353
{
336354
int i;
337355
X509_POLICY_DATA *data;
@@ -352,7 +370,7 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr,
352370
continue;
353371
#endif
354372
/* Look for matching nodes in previous level */
355-
if (!tree_link_matching_nodes(curr, data))
373+
if (!tree_link_matching_nodes(curr, data, tree))
356374
return 0;
357375
}
358376
return 1;
@@ -382,7 +400,7 @@ static int tree_add_unmatched(X509_POLICY_LEVEL *curr,
382400
/* Curr may not have anyPolicy */
383401
data->qualifier_set = cache->anyPolicy->qualifier_set;
384402
data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
385-
if (!level_add_node(curr, data, node, tree)) {
403+
if (level_add_node(curr, data, node, tree, 1) == NULL) {
386404
policy_data_free(data);
387405
return 0;
388406
}
@@ -464,18 +482,17 @@ static int tree_link_any(X509_POLICY_LEVEL *curr,
464482
/* Curr may not have anyPolicy */
465483
data->qualifier_set = cache->anyPolicy->qualifier_set;
466484
data->flags |= POLICY_DATA_FLAG_SHARED_QUALIFIERS;
467-
if (!level_add_node(curr, data, node, tree)) {
485+
if (!level_add_node(curr, data, node, tree, 1)) {
468486
policy_data_free(data);
469487
return 0;
470488
}
471489
#endif
472490

473491
}
474492
/* Finally add link to anyPolicy */
475-
if (last->anyPolicy) {
476-
if (!level_add_node(curr, cache->anyPolicy, last->anyPolicy, NULL))
477-
return 0;
478-
}
493+
if (last->anyPolicy &&
494+
level_add_node(curr, cache->anyPolicy, last->anyPolicy, tree, 0) == NULL)
495+
return 0;
479496
return 1;
480497
}
481498

@@ -646,7 +663,7 @@ static int tree_calculate_user_set(X509_POLICY_TREE *tree,
646663
extra->qualifier_set = anyPolicy->data->qualifier_set;
647664
extra->flags = POLICY_DATA_FLAG_SHARED_QUALIFIERS
648665
| POLICY_DATA_FLAG_EXTRA_NODE;
649-
node = level_add_node(NULL, extra, anyPolicy->parent, tree);
666+
node = level_add_node(NULL, extra, anyPolicy->parent, tree, 1);
650667
}
651668
if (!tree->user_policies) {
652669
tree->user_policies = sk_X509_POLICY_NODE_new_null();
@@ -668,7 +685,7 @@ static int tree_evaluate(X509_POLICY_TREE *tree)
668685

669686
for (i = 1; i < tree->nlevel; i++, curr++) {
670687
cache = policy_cache_set(curr->cert);
671-
if (!tree_link_nodes(curr, cache))
688+
if (!tree_link_nodes(curr, cache, tree))
672689
return 0;
673690

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

0 commit comments

Comments
 (0)