From dce27129b74f906e0d1c086858f360228d5cbc83 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Fri, 12 Jun 2026 20:26:27 +0200 Subject: irmd: Reject OAP peer crt with unusable CN Added checks for CN > NAME_SIZE. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/lib/crypt/openssl.c | 85 +++++++++++++++++++++++++---------------------- src/lib/tests/auth_test.c | 46 +++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 39 deletions(-) (limited to 'src/lib') diff --git a/src/lib/crypt/openssl.c b/src/lib/crypt/openssl.c index 2ea35a17..d4ffc00b 100644 --- a/src/lib/crypt/openssl.c +++ b/src/lib/crypt/openssl.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -1552,65 +1553,71 @@ void openssl_free_key(EVP_PKEY * key) int openssl_check_crt_name(void * crt, const char * name) { - char * subj; - char * cn; - X509 * xcrt; + const unsigned char * cn; + ASN1_STRING * val; + X509_NAME * nm; + int idx; + int len; - xcrt = (X509 *) crt; + nm = X509_get_subject_name((X509 *) crt); + if (nm == NULL) + return -1; - subj = X509_NAME_oneline(X509_get_subject_name(xcrt), NULL, 0); - if (subj == NULL) - goto fail_subj; + idx = X509_NAME_get_index_by_NID(nm, NID_commonName, -1); + if (idx < 0) + return -1; - cn = strstr(subj, "CN="); - if (cn == NULL) - goto fail_cn; + val = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(nm, idx)); + cn = ASN1_STRING_get0_data(val); + len = ASN1_STRING_length(val); - if (strcmp(cn + 3, name) != 0) - goto fail_cn; + if (len < 0 || (size_t) len != strlen(name)) + return -1; - free(subj); + if (memchr(cn, '\0', (size_t) len) != NULL) + return -1; + + if (memcmp(cn, name, (size_t) len) != 0) + return -1; return 0; - fail_cn: - free(subj); - fail_subj: - return -1; } int openssl_get_crt_name(void * crt, char * name) { - char * subj; - char * cn; - char * end; - X509 * xcrt; + const unsigned char * cn; + ASN1_STRING * val; + X509_NAME * nm; + int idx; + int len; - xcrt = (X509 *) crt; + nm = X509_get_subject_name((X509 *) crt); + if (nm == NULL) + return -1; - subj = X509_NAME_oneline(X509_get_subject_name(xcrt), NULL, 0); - if (subj == NULL) - goto fail_subj; + idx = X509_NAME_get_index_by_NID(nm, NID_commonName, -1); + if (idx < 0) + return -1; - cn = strstr(subj, "CN="); - if (cn == NULL) - goto fail_cn; + val = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(nm, idx)); + cn = ASN1_STRING_get0_data(val); + len = ASN1_STRING_length(val); - cn += 3; /* Skip "CN=" */ + if (len < 0) + return -1; + + if ((size_t) len > NAME_SIZE) + return -ENAME; - /* Find end of CN (comma or slash for next field) */ - end = strpbrk(cn, ",/"); - if (end != NULL) - *end = '\0'; + /* Reject an embedded NUL that would truncate the parsed name. */ + if (memchr(cn, '\0', (size_t) len) != NULL) + return -1; - strcpy(name, cn); - free(subj); + memcpy(name, cn, (size_t) len); + name[len] = '\0'; return 0; - fail_cn: - free(subj); - fail_subj: - return -1; } int openssl_crt_str(const void * crt, diff --git a/src/lib/tests/auth_test.c b/src/lib/tests/auth_test.c index 6a7666c1..af7cf81c 100644 --- a/src/lib/tests/auth_test.c +++ b/src/lib/tests/auth_test.c @@ -24,11 +24,14 @@ #include #include +#include #include #include #include +#include + #define TEST_MSG_SIZE 1500 static int test_auth_create_destroy_ctx(void) @@ -138,6 +141,47 @@ static int test_check_crt_name(void) return TEST_RC_FAIL; } +static int test_crt_name_confusion(void) +{ + char name[NAME_SIZE + 1]; + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(confused_crt_ec, &crt) < 0) { + printf("Failed to load name-confusion certificate.\n"); + goto fail_load; + } + + /* Must extract the real CN, not the "CN=" decoy in the O field. */ + if (crypt_get_crt_name(crt, name) < 0) { + printf("Failed to extract name from certificate.\n"); + goto fail_check; + } + + if (strcmp(name, "attacker.unittest.o7s") != 0) { + printf("Extracted '%s', expected real CN.\n", name); + goto fail_check; + } + + /* The decoy name in the O field must never authenticate. */ + if (crypt_check_crt_name(crt, "victim.unittest.o7s") == 0) { + printf("Accepted spoofed name from O field.\n"); + goto fail_check; + } + + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_check: + crypt_free_crt(crt); + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + static int test_load_free_privkey(void) { void * key; @@ -665,6 +709,7 @@ int auth_test(int argc, #ifdef HAVE_OPENSSL ret |= test_load_free_crt(); ret |= test_check_crt_name(); + ret |= test_crt_name_confusion(); ret |= test_crypt_get_pubkey_crt(); ret |= test_load_free_privkey(); ret |= test_load_free_pubkey(); @@ -679,6 +724,7 @@ int auth_test(int argc, #else (void) test_load_free_crt; (void) test_check_crt_name; + (void) test_crt_name_confusion; (void) test_crypt_get_pubkey_crt; (void) test_load_free_privkey; (void) test_load_free_pubkey; -- cgit v1.2.3