Skip to content

Commit 1d297ad

Browse files
panvaaduh95
authored andcommitted
crypto: preserve errors during X.509 issuer checks
X509View::isIssuedBy() clears the caller's OpenSSL error queue on entry and exit, including when either certificate is missing. Use a mark/pop scope to discard errors from the issuer check without losing earlier diagnostics. Cover successful checks, mismatches, malformed extensions, and null inputs with empty and populated queues. Assisted-by: Codex Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #66898 Reviewed-By: Tim Perry <pimterry@gmail.com>
1 parent 5409ed4 commit 1d297ad

2 files changed

Lines changed: 100 additions & 1 deletion

File tree

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1595,7 +1595,7 @@ bool X509View::isCA() const {
15951595
}
15961596

15971597
bool X509View::isIssuedBy(const X509View& issuer) const {
1598-
ClearErrorOnReturn clearErrorOnReturn;
1598+
MarkPopErrorOnReturn mark_pop_error_on_return;
15991599
if (cert_ == nullptr || issuer.cert_ == nullptr) return false;
16001600
return X509_check_issued(const_cast<X509*>(issuer.cert_),
16011601
const_cast<X509*>(cert_)) == X509_V_OK;

‎test/cctest/test_node_crypto.cc‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,105 @@ TEST(NodeCrypto, X509FormattingPreservesUnrelatedErrors) {
10251025
ERR_clear_error();
10261026
}
10271027

1028+
TEST(NodeCrypto, X509IssuerChecksPreserveUnrelatedErrors) {
1029+
ncrypto::ClearErrorOnReturn clear_errors;
1030+
auto ctx = EVPKeyCtxPointer::NewFromAlgorithm(KeyAlgorithm::RSA);
1031+
ASSERT_TRUE(ctx);
1032+
ASSERT_TRUE(ctx.initForKeygen());
1033+
ASSERT_TRUE(ctx.setRsaKeygenBits(2048));
1034+
EVP_PKEY* raw = nullptr;
1035+
ASSERT_EQ(EVP_PKEY_keygen(ctx.get(), &raw), 1);
1036+
EVPKeyPointer key(raw);
1037+
1038+
ncrypto::X509Pointer cert(X509_new());
1039+
ASSERT_TRUE(cert);
1040+
ASSERT_EQ(X509_set_version(cert.get(), 2), 1);
1041+
ASSERT_EQ(ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1), 1);
1042+
ASSERT_EQ(
1043+
ASN1_TIME_set_string(X509_getm_notBefore(cert.get()), "20260101000000Z"),
1044+
1);
1045+
ASSERT_EQ(
1046+
ASN1_TIME_set_string(X509_getm_notAfter(cert.get()), "20270101000000Z"),
1047+
1);
1048+
X509NamePointer name(X509_NAME_new());
1049+
ASSERT_TRUE(name);
1050+
ASSERT_EQ(X509_NAME_add_entry_by_txt(
1051+
name.get(),
1052+
"CN",
1053+
MBSTRING_ASC,
1054+
reinterpret_cast<const unsigned char*>("issuer"),
1055+
-1,
1056+
-1,
1057+
0),
1058+
1);
1059+
ASSERT_EQ(X509_set_subject_name(cert.get(), name.get()), 1);
1060+
ASSERT_EQ(X509_set_issuer_name(cert.get(), name.get()), 1);
1061+
ASSERT_EQ(X509_set_pubkey(cert.get(), key.get()), 1);
1062+
ASSERT_GT(X509_sign(cert.get(), key.get(), EVP_sha256()), 0);
1063+
1064+
X509NamePointer mismatch_name(X509_NAME_dup(name.get()));
1065+
ASSERT_TRUE(mismatch_name);
1066+
ASSERT_EQ(X509_NAME_add_entry_by_txt(
1067+
mismatch_name.get(),
1068+
"OU",
1069+
MBSTRING_ASC,
1070+
reinterpret_cast<const unsigned char*>("other"),
1071+
-1,
1072+
-1,
1073+
0),
1074+
1);
1075+
1076+
ncrypto::DeleteFnPtr<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free> invalid_value(
1077+
ASN1_OCTET_STRING_new());
1078+
ASSERT_TRUE(invalid_value);
1079+
const unsigned char truncated_sequence[] = {0x30};
1080+
ASSERT_EQ(
1081+
ASN1_OCTET_STRING_set(
1082+
invalid_value.get(), truncated_sequence, sizeof(truncated_sequence)),
1083+
1);
1084+
X509ExtensionPointer invalid_constraints(X509_EXTENSION_create_by_NID(
1085+
nullptr, NID_basic_constraints, 0, invalid_value.get()));
1086+
ASSERT_TRUE(invalid_constraints);
1087+
1088+
for (const bool seed_error : {true, false}) {
1089+
// Reparse each issuer so extension-cache failures are exercised each time.
1090+
auto matching_issuer = cert.view().clone();
1091+
auto mismatch = cert.view().clone();
1092+
auto malformed = cert.view().clone();
1093+
ASSERT_TRUE(matching_issuer);
1094+
ASSERT_TRUE(mismatch);
1095+
ASSERT_TRUE(malformed);
1096+
ASSERT_EQ(X509_set_subject_name(mismatch.get(), mismatch_name.get()), 1);
1097+
ASSERT_EQ(X509_add_ext(malformed.get(), invalid_constraints.get(), -1), 1);
1098+
1099+
const auto check = [seed_error](ncrypto::X509View subject,
1100+
ncrypto::X509View issuer,
1101+
bool issued) {
1102+
ERR_clear_error();
1103+
if (seed_error) {
1104+
#if NCRYPTO_USE_OPENSSL_PROVIDER
1105+
ERR_raise(ERR_LIB_USER, 1);
1106+
#elif NCRYPTO_USE_BORINGSSL
1107+
ERR_put_error(ERR_LIB_USER, 0, 1, __FILE__, __LINE__);
1108+
#else
1109+
#error "Unsupported crypto backend"
1110+
#endif
1111+
}
1112+
const auto expected = ERR_peek_error();
1113+
if (seed_error) ASSERT_NE(expected, 0UL);
1114+
EXPECT_EQ(subject.isIssuedBy(issuer), issued);
1115+
EXPECT_EQ(ERR_get_error(), expected);
1116+
EXPECT_EQ(ERR_get_error(), 0UL);
1117+
};
1118+
1119+
check(cert.view(), matching_issuer.view(), true);
1120+
check(cert.view(), mismatch.view(), false);
1121+
check(cert.view(), malformed.view(), false);
1122+
check({}, matching_issuer.view(), false);
1123+
check(cert.view(), {}, false);
1124+
}
1125+
}
1126+
10281127
TEST(NodeCrypto, X509KeyVisitorsHandleMissingPublicKey) {
10291128
ncrypto::X509Pointer cert(X509_new());
10301129
ASSERT_TRUE(cert);

0 commit comments

Comments
 (0)