Commit 1b25e3fca5 for openssl.org
commit 1b25e3fca5772fce2edd71a6adfc306dbe254c3e
Author: Greensi7 <adam.tabak04@gmail.com>
Date: Thu Aug 6 15:33:27 2026 +0200
Reject duplicate fields in X509v3 config
Reject repeated fields in policyConstraints, basicConstraints
and basicAttConstraints instead of silently accepting the last value.
Assisted-by: ChatGPT:gpt-5.6
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Andrew Dinh <andrewd@openssl.org>
MergeDate: Sun Aug 9 04:06:37 2026
(Merged from https://github.com/openssl/openssl/pull/32181)
diff --git a/CHANGES.md b/CHANGES.md
index 139bacbbc4..1bfca49b15 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -31,6 +31,12 @@ OpenSSL Releases
### Changes between 4.0 and 4.1 [xx XXX xxxx]
+ * Repeated fields in the `basicConstraints`, `basicAttConstraints`,
+ and `policyConstraints` X.509v3 extension configurations are now rejected
+ instead of silently using the last value.
+
+ *Adam Tabak*
+
* The `tsget` utility now uses `Net::Curl::Easy` (from the `Net-Curl` CPAN
distribution) instead of the abandoned `WWW::Curl::Easy`. Users who relied
on `tsget` must install `Net::Curl::Easy` before upgrading.
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 8b6c912977..de4b47ebb7 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1775,6 +1775,7 @@ X509V3_R_BN_DEC2BN_ERROR:100:bn dec2bn error
X509V3_R_BN_TO_ASN1_INTEGER_ERROR:101:bn to asn1 integer error
X509V3_R_DIRNAME_ERROR:149:dirname error
X509V3_R_DISTPOINT_ALREADY_SET:160:distpoint already set
+X509V3_R_DUPLICATE_FIELD:174:duplicate field
X509V3_R_DUPLICATE_ZONE_ID:133:duplicate zone id
X509V3_R_EMPTY_KEY_USAGE:169:empty key usage
X509V3_R_ERROR_CONVERTING_ZONE:131:error converting zone
diff --git a/crypto/x509/v3_battcons.c b/crypto/x509/v3_battcons.c
index 7bcca27537..fd2fd87f7b 100644
--- a/crypto/x509/v3_battcons.c
+++ b/crypto/x509/v3_battcons.c
@@ -61,7 +61,7 @@ static OSSL_BASIC_ATTR_CONSTRAINTS *v2i_OSSL_BASIC_ATTR_CONSTRAINTS(
{
OSSL_BASIC_ATTR_CONSTRAINTS *battcons = NULL;
CONF_VALUE *val;
- int i;
+ int i, authority_seen = 0;
if ((battcons = OSSL_BASIC_ATTR_CONSTRAINTS_new()) == NULL) {
ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB);
@@ -70,11 +70,20 @@ static OSSL_BASIC_ATTR_CONSTRAINTS *v2i_OSSL_BASIC_ATTR_CONSTRAINTS(
for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
val = sk_CONF_VALUE_value(values, i);
if (strcmp(val->name, "authority") == 0) {
+ if (authority_seen) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_bool(val, &battcons->authority))
goto err;
+ authority_seen = 1;
} else if (strcmp(val->name, "pathlen") == 0) {
- ASN1_INTEGER_free(battcons->pathlen);
- battcons->pathlen = NULL;
+ if (battcons->pathlen != NULL) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_int(val, &battcons->pathlen))
goto err;
} else {
diff --git a/crypto/x509/v3_bcons.c b/crypto/x509/v3_bcons.c
index da438346b8..88ecc35793 100644
--- a/crypto/x509/v3_bcons.c
+++ b/crypto/x509/v3_bcons.c
@@ -60,7 +60,7 @@ static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method,
{
BASIC_CONSTRAINTS *bcons = NULL;
CONF_VALUE *val;
- int i;
+ int i, ca_seen = 0;
if ((bcons = BASIC_CONSTRAINTS_new()) == NULL) {
ERR_raise(ERR_LIB_X509V3, ERR_R_ASN1_LIB);
@@ -69,11 +69,20 @@ static BASIC_CONSTRAINTS *v2i_BASIC_CONSTRAINTS(X509V3_EXT_METHOD *method,
for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
val = sk_CONF_VALUE_value(values, i);
if (strcmp(val->name, "CA") == 0) {
+ if (ca_seen) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_bool(val, &bcons->ca))
goto err;
+ ca_seen = 1;
} else if (strcmp(val->name, "pathlen") == 0) {
- ASN1_INTEGER_free(bcons->pathlen);
- bcons->pathlen = NULL;
+ if (bcons->pathlen != NULL) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_int(val, &bcons->pathlen))
goto err;
} else {
diff --git a/crypto/x509/v3_pcons.c b/crypto/x509/v3_pcons.c
index 376be44608..db5d3e9acb 100644
--- a/crypto/x509/v3_pcons.c
+++ b/crypto/x509/v3_pcons.c
@@ -67,13 +67,19 @@ static void *v2i_POLICY_CONSTRAINTS(const X509V3_EXT_METHOD *method,
for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
val = sk_CONF_VALUE_value(values, i);
if (strcmp(val->name, "requireExplicitPolicy") == 0) {
- ASN1_INTEGER_free(pcons->requireExplicitPolicy);
- pcons->requireExplicitPolicy = NULL;
+ if (pcons->requireExplicitPolicy != NULL) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_int(val, &pcons->requireExplicitPolicy))
goto err;
} else if (strcmp(val->name, "inhibitPolicyMapping") == 0) {
- ASN1_INTEGER_free(pcons->inhibitPolicyMapping);
- pcons->inhibitPolicyMapping = NULL;
+ if (pcons->inhibitPolicyMapping != NULL) {
+ ERR_raise_data(ERR_LIB_X509V3, X509V3_R_DUPLICATE_FIELD,
+ "field=%s", val->name);
+ goto err;
+ }
if (!X509V3_get_value_int(val, &pcons->inhibitPolicyMapping))
goto err;
} else {
diff --git a/crypto/x509/v3err.c b/crypto/x509/v3err.c
index 96a27aaf48..9368da4906 100644
--- a/crypto/x509/v3err.c
+++ b/crypto/x509/v3err.c
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -25,6 +25,7 @@ static const ERR_STRING_DATA X509V3_str_reasons[] = {
{ ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DIRNAME_ERROR), "dirname error" },
{ ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DISTPOINT_ALREADY_SET),
"distpoint already set" },
+ { ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DUPLICATE_FIELD), "duplicate field" },
{ ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DUPLICATE_ZONE_ID),
"duplicate zone id" },
{ ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EMPTY_KEY_USAGE), "empty key usage" },
diff --git a/include/crypto/x509v3err.h b/include/crypto/x509v3err.h
index b3874fc472..279acb7e61 100644
--- a/include/crypto/x509v3err.h
+++ b/include/crypto/x509v3err.h
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
diff --git a/include/openssl/x509v3err.h b/include/openssl/x509v3err.h
index 19f938626b..75d9e7d3e4 100644
--- a/include/openssl/x509v3err.h
+++ b/include/openssl/x509v3err.h
@@ -1,6 +1,6 @@
/*
* Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -27,6 +27,7 @@
#define X509V3_R_BN_TO_ASN1_INTEGER_ERROR 101
#define X509V3_R_DIRNAME_ERROR 149
#define X509V3_R_DISTPOINT_ALREADY_SET 160
+#define X509V3_R_DUPLICATE_FIELD 174
#define X509V3_R_DUPLICATE_ZONE_ID 133
#define X509V3_R_EMPTY_KEY_USAGE 169
#define X509V3_R_ERROR_CONVERTING_ZONE 131