aboutsummaryrefslogtreecommitdiff
path: root/compat/libtls/tls_verify.c
diff options
context:
space:
mode:
Diffstat (limited to 'compat/libtls/tls_verify.c')
-rw-r--r--compat/libtls/tls_verify.c101
1 files changed, 74 insertions, 27 deletions
diff --git a/compat/libtls/tls_verify.c b/compat/libtls/tls_verify.c
index 053dc2f..7bf509d 100644
--- a/compat/libtls/tls_verify.c
+++ b/compat/libtls/tls_verify.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_verify.c,v 1.23 2023/05/11 07:35:27 tb Exp $ */
+/* $OpenBSD: tls_verify.c,v 1.29 2023/11/22 18:23:09 op Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
*
@@ -94,15 +94,21 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
union tls_addr addrbuf;
int addrlen, type;
int count, i;
- int rv = 0;
+ int critical = 0;
+ int rv = -1;
*alt_match = 0;
*alt_exists = 0;
- altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
- NULL, NULL);
- if (altname_stack == NULL)
- return 0;
+ altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, &critical,
+ NULL);
+ if (altname_stack == NULL) {
+ if (critical != -1) {
+ tls_set_errorx(ctx, "error decoding subjectAltName");
+ goto err;
+ }
+ goto done;
+ }
if (inet_pton(AF_INET, name, &addrbuf) == 1) {
type = GEN_IPADD;
@@ -142,8 +148,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
"NUL byte in subjectAltName, "
"probably a malicious certificate",
name);
- rv = -1;
- break;
+ goto err;
}
/*
@@ -156,13 +161,12 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
"error verifying name '%s': "
"a dNSName of \" \" must not be "
"used", name);
- rv = -1;
- break;
+ goto err;
}
if (tls_match_name(data, name) == 0) {
*alt_match = 1;
- break;
+ goto done;
}
} else {
#ifdef DEBUG
@@ -183,8 +187,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
tls_set_errorx(ctx,
"Unexpected negative length for an "
"IP address: %d", datalen);
- rv = -1;
- break;
+ goto err;
}
/*
@@ -194,11 +197,15 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
if (datalen == addrlen &&
memcmp(data, &addrbuf, addrlen) == 0) {
*alt_match = 1;
- break;
+ goto done;
}
}
}
+ done:
+ rv = 0;
+
+ err:
sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
return rv;
}
@@ -207,10 +214,13 @@ static int
tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
int *cn_match)
{
+ unsigned char *utf8_bytes = NULL;
X509_NAME *subject_name;
char *common_name = NULL;
union tls_addr addrbuf;
int common_name_len;
+ ASN1_STRING *data;
+ int lastpos = -1;
int rv = -1;
*cn_match = 0;
@@ -219,29 +229,65 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
if (subject_name == NULL)
goto done;
- common_name_len = X509_NAME_get_text_by_NID(subject_name,
- NID_commonName, NULL, 0);
- if (common_name_len < 0)
+ lastpos = X509_NAME_get_index_by_NID(subject_name,
+ NID_commonName, lastpos);
+ if (lastpos == -1)
goto done;
-
- common_name = calloc(common_name_len + 1, 1);
- if (common_name == NULL) {
- tls_set_error(ctx, "out of memory");
+ if (lastpos < 0)
+ goto err;
+ if (X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos)
+ != -1) {
+ /*
+ * Having multiple CN's is possible, and even happened back in
+ * the glory days of mullets and Hammer pants. In anything like
+ * a modern TLS cert, CN is as close to deprecated as it gets,
+ * and having more than one is bad. We therefore fail if we have
+ * more than one CN fed to us in the subject, treating the
+ * certificate as hostile.
+ */
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Certificate subject contains multiple Common Name fields, "
+ "probably a malicious or malformed certificate", name);
goto err;
}
- X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
- common_name_len + 1);
-
- /* NUL bytes in CN? */
- if (common_name_len < 0 ||
- (size_t)common_name_len != strlen(common_name)) {
+ data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name,
+ lastpos));
+ /*
+ * Fail if we cannot encode the CN bytes as UTF-8.
+ */
+ if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) {
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Common Name field cannot be encoded as a UTF-8 string, "
+ "probably a malicious certificate", name);
+ goto err;
+ }
+ /*
+ * Fail if the CN is of invalid length. RFC 5280 specifies that a CN
+ * must be between 1 and 64 bytes long.
+ */
+ if (common_name_len < 1 || common_name_len > 64) {
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Common Name field has invalid length, "
+ "probably a malicious certificate", name);
+ goto err;
+ }
+ /*
+ * Fail if the resulting text contains a NUL byte.
+ */
+ if (memchr(utf8_bytes, 0, common_name_len) != NULL) {
tls_set_errorx(ctx, "error verifying name '%s': "
"NUL byte in Common Name field, "
"probably a malicious certificate", name);
goto err;
}
+ common_name = strndup(utf8_bytes, common_name_len);
+ if (common_name == NULL) {
+ tls_set_error(ctx, "out of memory");
+ goto err;
+ }
+
/*
* We don't want to attempt wildcard matching against IP addresses,
* so perform a simple comparison here.
@@ -260,6 +306,7 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
rv = 0;
err:
+ free(utf8_bytes);
free(common_name);
return rv;
}