Skip to content

Fix NUL-truncation in has_common_name#34

Open
achamayou wants to merge 3 commits into
mainfrom
fix_common_name_nul_truncation
Open

Fix NUL-truncation in has_common_name#34
achamayou wants to merge 3 commits into
mainfrom
fix_common_name_nul_truncation

Conversation

@achamayou

@achamayou achamayou commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

has_common_name treated the X.509 common-name value as a NUL-terminated C string:

const std::string common_name = (char*)ASN1_STRING_get0_data(entry_string);

This truncates at an embedded NUL byte and ignores the ASN.1 value's explicit length. It is the same class of spoofing bug the rest of the file deliberately avoids — see has_san and subject(), which compare using ASN1_STRING_length / ASN1_STRING_to_UTF8. A certificate whose CN is trusted\0evil would compare equal to trusted.

Changes

  • Decode the CN value with ASN1_STRING_to_UTF8 and compare using the explicit length, mirroring subject().
  • Add a CHECKNULL on the entry data and handle the empty-value case.
  • Replace UqX509_NAME/UqX509_NAME_ENTRY wrappers (which incorrectly called _free on borrowed internal pointers returned by X509_get_subject_name/X509_NAME_get_entry) with raw pointers, matching how subject() handles the same data.
  • Remove the now-unused UqX509_NAME and UqX509_NAME_ENTRY structs.

The function is currently unused, so there is no live exploit today; this is a hardening fix that prevents the vulnerability from being reintroduced the moment a caller is added.

Testing

  • Added TestCNEmbeddedNulNotTruncated: loads a certificate whose CN is trusted\0evil and asserts has_common_name("trusted") is false (no NUL truncation) and has_common_name("trusted\0evil") is true.
  • Added TestCNUtf8Value: loads a certificate whose CN is café Test and asserts the non-ASCII UTF-8 value is correctly decoded and matched.
  • cmake --build build && ctest — all tests pass.

Found during a security review of the codebase.

has_common_name treated the X.509 common-name value as a NUL-terminated C
string via ASN1_STRING_get0_data, which truncates at an embedded NUL and
ignores the explicit length. This is the same class of spoofing bug the
rest of the file avoids (see has_san and subject()): a value such as
"trusted\0evil" would compare equal to "trusted".

Decode the value with ASN1_STRING_to_UTF8 and compare using the explicit
length, matching subject(). The function is currently unused, so this is
a hardening fix that prevents the bug from being reintroduced via a future
caller.
@achamayou achamayou requested a review from a team as a code owner June 12, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hardens UqX509::has_common_name() against embedded-NUL truncation by decoding the X.509 CN value using explicit length semantics (UTF-8 conversion), aligning it with the rest of the file’s anti-spoofing comparisons.

Changes:

  • Replace NUL-terminated string handling of CN with ASN1_STRING_to_UTF8 + explicit-length comparison.
  • Add null-checking for the CN entry data and handle empty CN values without unsafe reads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread didx509cpp.h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants