summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/crypt/openssl.c85
-rw-r--r--src/lib/tests/auth_test.c46
2 files changed, 92 insertions, 39 deletions
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 <ouroboros/errno.h>
#include <ouroboros/crypt.h>
#include <ouroboros/hash.h>
+#include <ouroboros/name.h>
#include <ouroboros/random.h>
#include <ouroboros/utils.h>
@@ -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 <test/test.h>
#include <ouroboros/crypt.h>
+#include <ouroboros/name.h>
#include <ouroboros/random.h>
#include <ouroboros/utils.h>
#include <test/certs/ecdsa.h>
+#include <string.h>
+
#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;