All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/8] tls: Implement l_tls_set_domain_mask
@ 2019-08-23  0:41 Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 2/8] unit: Add l_tls_set_domain_mask tests Andrew Zaborowski
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 6427 bytes --]

Allow user to set a mask that the a peer certificate's subject CN
has to match.
---
 ell/ell.sym       |   1 +
 ell/tls-private.h |   1 +
 ell/tls.c         | 143 ++++++++++++++++++++++++++++++++++++++++++++++
 ell/tls.h         |   2 +
 4 files changed, 147 insertions(+)

diff --git a/ell/ell.sym b/ell/ell.sym
index 9bf0934..fe0ff1f 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -441,6 +441,7 @@ global:
 	l_tls_set_cacert;
 	l_tls_set_auth_data;
 	l_tls_set_version_range;
+	l_tls_set_domain_mask;
 	l_tls_alert_to_str;
 	l_tls_set_debug;
 	/* uintset */
diff --git a/ell/tls-private.h b/ell/tls-private.h
index c1de257..908c622 100644
--- a/ell/tls-private.h
+++ b/ell/tls-private.h
@@ -212,6 +212,7 @@ struct l_tls {
 	struct l_certchain *cert;
 	struct l_key *priv_key;
 	size_t priv_key_size;
+	char **subject_mask;
 
 	struct tls_cipher_suite **cipher_suite_pref_list;
 
diff --git a/ell/tls.c b/ell/tls.c
index b1fec9f..3fe2ff5 100644
--- a/ell/tls.c
+++ b/ell/tls.c
@@ -629,6 +629,112 @@ static const struct tls_hash_algorithm *tls_set_prf_hmac(struct l_tls *tls)
 	return NULL;
 }
 
+static bool tls_domain_match_mask(const char *name, size_t name_len,
+					const char *mask, size_t mask_len)
+{
+	bool at_start = true;
+
+	while (1) {
+		const char *name_seg_end = memchr(name, '.', name_len);
+		const char *mask_seg_end = memchr(mask, '.', mask_len);
+		size_t name_seg_len = name_seg_end ?
+			(size_t) (name_seg_end - name) : name_len;
+		size_t mask_seg_len = mask_seg_end ?
+			(size_t) (mask_seg_end - mask) : mask_len;
+
+		if (mask_seg_len == 1 && mask[0] == '*') {
+			/*
+			 * A * at the beginning of the mask matches any
+			 * number of labels.
+			 */
+			if (at_start && name_seg_end &&
+					tls_domain_match_mask(name_seg_end + 1,
+						name_len - name_seg_len - 1,
+						mask, mask_len))
+				return true;
+
+			goto ok_next;
+		}
+
+		if (name_seg_len != mask_seg_len ||
+				memcmp(name, mask, name_seg_len))
+			return false;
+
+ok_next:
+		/* If either string ends here both must end here */
+		if (!name_seg_end || !mask_seg_end)
+			return !name_seg_end && !mask_seg_end;
+
+		at_start = false;
+		name = name_seg_end + 1;
+		name_len -= name_seg_len + 1;
+		mask = mask_seg_end + 1;
+		mask_len -= mask_seg_len + 1;
+	}
+}
+
+static const struct asn1_oid dn_common_name_oid =
+	{ 3, { 0x55, 0x04, 0x03 } };
+
+static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
+{
+	const uint8_t *dn, *end;
+	size_t dn_size;
+	const char *cn = NULL;
+	size_t cn_len;
+
+	/*
+	 * Retrieve the Common Name from the Subject DN and check if it
+	 * matches.  TODO: possibly also look at SubjectAltName.
+	 */
+
+	dn = l_cert_get_dn(cert, &dn_size);
+	if (unlikely(!dn))
+		return false;
+
+	end = dn + dn_size;
+	while (dn < end) {
+		const uint8_t *set, *seq, *oid, *name;
+		uint8_t tag;
+		size_t len, oid_len, name_len;
+
+		set = asn1_der_find_elem(dn, end - dn, 0, &tag, &len);
+		if (unlikely(!set || tag != ASN1_ID_SET))
+			return false;
+
+		dn = set + len;
+
+		seq = asn1_der_find_elem(set, len, 0, &tag, &len);
+		if (unlikely(!seq || tag != ASN1_ID_SEQUENCE))
+			return false;
+
+		oid = asn1_der_find_elem(seq, len, 0, &tag, &oid_len);
+		if (unlikely(!oid || tag != ASN1_ID_OID))
+			return false;
+
+		name = asn1_der_find_elem(seq, len, 1, &tag, &name_len);
+		if (unlikely(!name || (tag != ASN1_ID_PRINTABLESTRING &&
+					tag != ASN1_ID_UTF8STRING &&
+					tag != ASN1_ID_IA5STRING)))
+			continue;
+
+		if (asn1_oid_eq(&dn_common_name_oid, oid_len, oid)) {
+			cn = (const char *) name;
+			cn_len = name_len;
+			break;
+		}
+	}
+
+	if (!cn)
+		return false;
+
+	for (; *mask; mask++)
+		if (tls_domain_match_mask(cn, cn_len, *mask, strlen(*mask)))
+			return true;
+
+	return false;
+}
+
 #define SWITCH_ENUM_TO_STR(val) \
 	case (val):		\
 		return L_STRINGIFY(val);
@@ -1793,6 +1899,18 @@ static void tls_handle_certificate(struct l_tls *tls,
 		goto done;
 	}
 
+	if (tls->subject_mask && !tls_cert_domains_match_mask(leaf,
+							tls->subject_mask)) {
+		char *mask = l_strjoinv(tls->subject_mask, '|');
+
+		TLS_DISCONNECT(TLS_ALERT_BAD_CERT, 0,
+				"Peer certificate's subject domain "
+				"doesn't match %s", mask);
+		l_free(mask);
+
+		goto done;
+	}
+
 	/* Save the end-entity certificate and free the chain */
 	der = l_cert_get_der_data(leaf, &der_len);
 	tls->peer_cert = l_cert_new_from_der(der, der_len);
@@ -2420,6 +2538,7 @@ LIB_EXPORT void l_tls_free(struct l_tls *tls)
 
 	l_tls_set_cacert(tls, NULL);
 	l_tls_set_auth_data(tls, NULL, NULL, NULL);
+	l_tls_set_domain_mask(tls, NULL);
 
 	tls_reset_handshake(tls);
 	tls_cleanup_handshake(tls);
@@ -2766,6 +2885,30 @@ LIB_EXPORT void l_tls_set_version_range(struct l_tls *tls,
 		max_version : TLS_MAX_VERSION;
 }
 
+/**
+ * l_tls_set_domain_mask:
+ * @tls: TLS object being configured
+ * @mask: NULL-terminated array of domain masks
+ *
+ * Sets a mask for domain names contained in the peer certificate
+ * (eg. the subject Common Name) to be matched against.  If none of the
+ * domains match the any mask, authentication will fail.  At least one
+ * domain has to match at least one mask from the list.
+ *
+ * The masks are each split into segments at the dot characters and each
+ * segment must match the corresponding label of the domain name --
+ * a domain name is a sequence of labels joined by dots.  An asterisk
+ * segment in the mask matches any label.  An asterisk segment at the
+ * beginning of the mask matches one or more consecutive labels from
+ * the beginning of the domain string.
+ */
+LIB_EXPORT void l_tls_set_domain_mask(struct l_tls *tls, char **mask)
+{
+	l_strv_free(tls->subject_mask);
+
+	tls->subject_mask = l_strv_copy(mask);
+}
+
 LIB_EXPORT const char *l_tls_alert_to_str(enum l_tls_alert_desc desc)
 {
 	switch (desc) {
diff --git a/ell/tls.h b/ell/tls.h
index b0db34f..a361c37 100644
--- a/ell/tls.h
+++ b/ell/tls.h
@@ -117,6 +117,8 @@ void l_tls_set_version_range(struct l_tls *tls,
 				enum l_tls_version min_version,
 				enum l_tls_version max_version);
 
+void l_tls_set_domain_mask(struct l_tls *tls, char **mask);
+
 const char *l_tls_alert_to_str(enum l_tls_alert_desc desc);
 
 enum l_checksum_type;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/8] unit: Add l_tls_set_domain_mask tests
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 3/8] asn1-private: Handle Context-specific tag class Andrew Zaborowski
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 9274 bytes --]

---
 unit/test-tls.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)

diff --git a/unit/test-tls.c b/unit/test-tls.c
index d701f42..36f9934 100644
--- a/unit/test-tls.c
+++ b/unit/test-tls.c
@@ -309,6 +309,7 @@ struct tls_conn_test {
 	const char *client_ca_cert_path;
 	const char *client_expect_identity;
 	const char **client_cipher_suites;
+	char **client_domain_mask;
 	bool expect_alert;
 	bool expect_client_start_fail;
 	enum l_tls_alert_desc alert_desc;
@@ -566,6 +567,9 @@ static void test_tls_with_ver(const struct tls_conn_test *test,
 	assert(l_tls_set_cacert(s[0].tls, test->server_ca_cert_path));
 	assert(l_tls_set_cacert(s[1].tls, test->client_ca_cert_path));
 
+	if (test->client_domain_mask)
+		l_tls_set_domain_mask(s[1].tls, test->client_domain_mask);
+
 	assert(l_tls_start(s[0].tls));
 	assert(!!l_tls_start(s[1].tls) == !test->expect_client_start_fail);
 
@@ -616,6 +620,152 @@ static void test_tls_version_mismatch_test(const void *data)
 				L_TLS_V10, L_TLS_V11);
 }
 
+static const struct tls_conn_test tls_conn_test_domain_match1 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "Foo Example Organization", NULL },
+};
+
+static const struct tls_conn_test tls_conn_test_domain_match2 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"Foo Example Organization", "Bar Example Organization", NULL
+	},
+};
+
+static const struct tls_conn_test tls_conn_test_domain_match3 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"Bar Example Organization", "Foo Example Organization", NULL
+	},
+};
+
+static const struct tls_conn_test tls_conn_test_domain_match4 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "*", NULL },
+};
+
+static const struct tls_conn_test tls_conn_test_domain_mismatch1 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "", NULL },
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
+static const struct tls_conn_test tls_conn_test_domain_mismatch2 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "Bar Example Organization", NULL },
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
+static const struct tls_conn_test tls_conn_test_domain_mismatch3 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"Foo Example Organization.com", NULL
+	},
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
+static const struct tls_conn_test tls_conn_test_domain_mismatch4 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"Foo Example Organization.*", NULL
+	},
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
+static const struct tls_conn_test tls_conn_test_domain_mismatch5 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"*.Foo Example Organization", NULL
+	},
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
 static void test_tls_suite_test(const void *data)
 {
 	const char *suite_name = data;
@@ -720,6 +870,25 @@ int main(int argc, char *argv[])
 	l_test_add("TLS connection version mismatch",
 			test_tls_version_mismatch_test, NULL);
 
+	l_test_add("TLS connection domain match 1", test_tls_test,
+			&tls_conn_test_domain_match1);
+	l_test_add("TLS connection domain match 2", test_tls_test,
+			&tls_conn_test_domain_match2);
+	l_test_add("TLS connection domain match 3", test_tls_test,
+			&tls_conn_test_domain_match3);
+	l_test_add("TLS connection domain match 4", test_tls_test,
+			&tls_conn_test_domain_match4);
+	l_test_add("TLS connection domain mismatch 1", test_tls_test,
+			&tls_conn_test_domain_mismatch1);
+	l_test_add("TLS connection domain mismatch 2", test_tls_test,
+			&tls_conn_test_domain_mismatch2);
+	l_test_add("TLS connection domain mismatch 3", test_tls_test,
+			&tls_conn_test_domain_mismatch3);
+	l_test_add("TLS connection domain mismatch 4", test_tls_test,
+			&tls_conn_test_domain_mismatch4);
+	l_test_add("TLS connection domain mismatch 5", test_tls_test,
+			&tls_conn_test_domain_mismatch5);
+
 	for (i = 0; tls_cipher_suite_pref[i]; i++) {
 		struct tls_cipher_suite *suite = tls_cipher_suite_pref[i];
 		struct tls_bulk_encryption_algorithm *alg = suite->encryption;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/8] asn1-private: Handle Context-specific tag class
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 2/8] unit: Add l_tls_set_domain_mask tests Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 4/8] cert: Implement l_cert_get_extension Andrew Zaborowski
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 5962 bytes --]

In asn1_der_find_elem(_by_path) add a way to properly handle the
optional elements in x509 certificates.  Until now we were lucky we
never needed to handle the parts of certificates where some
OPTIONAL/DEFAULT element was absent (such as Version in
v1 certificates) but we would be misparsing the certificates in
those cases.

In the general case an asn1 parser will need the full syntax
definition to locate an element inside the structure.  To avoid adding
a lot of .asn1 files like those contained in the kernel, and a parser
compiler, assume that OPTIONAL/DEFAULT elements use Context-specific
tags which is mostly the case in the x509 syntax (this is evens noted
in ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc).  This way we
can handle Univeral tags that are always present and optional elements
that use Context-specific tags and get by with the same few #defines
we had regarding the x509 structure.
---
 ell/asn1-private.h | 74 ++++++++++++++++++++++++++++++++++++++--------
 ell/cert.c         | 20 ++++++-------
 2 files changed, 72 insertions(+), 22 deletions(-)

diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index 71a4ae6..7c382ec 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -21,6 +21,7 @@
 #define ASN1_ID(class, pc, tag)	(((class) << 6) | ((pc) << 5) | (tag))
 
 #define ASN1_CLASS_UNIVERSAL	0
+#define ASN1_CLASS_CONTEXT	2
 
 #define ASN1_ID_SEQUENCE	ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x10)
 #define ASN1_ID_SET		ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x11)
@@ -89,14 +90,24 @@ static inline void asn1_write_definite_length(uint8_t **buf, size_t len)
 		*(*buf)++ = len >> (n * 8);
 }
 
-/* Return index'th element in a DER SEQUENCE */
+#define ASN1_CONTEXT_IMPLICIT(tag) (0x1000 | (tag))
+#define ASN1_CONTEXT_EXPLICIT(tag) (0x2000 | (tag))
+
+/*
+ * Return the tag, length and value of the @index'th
+ * non-context-specific-tagged element in a DER SEQUENCE or one who's
+ * ASN1_CONTEXT_IMPLICIT(tag) matches @index or the inner element of
+ * the one who's ASN1_CONTEXT_EXPLICIT(tag) matches @index.
+ */
 static inline const uint8_t *asn1_der_find_elem(const uint8_t *buf,
 						size_t len_in, int index,
 						uint8_t *tag, size_t *len_out)
 {
-	int tlv_len;
+	int n = 0;
 
 	while (1) {
+		int tlv_len;
+
 		if (len_in < 2)
 			return NULL;
 
@@ -107,9 +118,36 @@ static inline const uint8_t *asn1_der_find_elem(const uint8_t *buf,
 		if (tlv_len < 0 || (size_t) tlv_len > len_in)
 			return NULL;
 
-		if (index-- == 0) {
-			*len_out = tlv_len;
-			return buf;
+		if (*tag >> 6 != ASN1_CLASS_CONTEXT) {
+			if (n++ == index) {
+				*len_out = tlv_len;
+				return buf;
+			}
+		} else if ((*tag & 0x1f) == (index & 0xfff)) {
+			/* Context-specific tag */
+			if (index & 0x1000) {		/* Implicit */
+				*len_out = tlv_len;
+				return buf;
+			} else if (index & 0x2000) {	/* Explicit */
+				const uint8_t *outer = buf;
+				int inner_len;
+
+				if (!(*tag & 0x20))	/* Primitive */
+					return NULL;
+
+				if (unlikely(tlv_len < 2))
+					return NULL;
+
+				*tag = *buf++;
+
+				inner_len = asn1_parse_definite_length(
+							(void *) &buf, &len_in);
+				if (outer + tlv_len != buf + inner_len)
+					return NULL;
+
+				*len_out = inner_len;
+				return buf;
+			}
 		}
 
 		buf += tlv_len;
@@ -122,20 +160,32 @@ static inline const uint8_t *asn1_der_find_elem_by_path(const uint8_t *buf,
 						size_t len_in, uint8_t tag,
 						size_t *len_out, ...)
 {
-	uint8_t elem_tag;
-	int pos;
+	int index;
 	va_list vl;
 
 	va_start(vl, len_out);
 
-	pos = va_arg(vl, int);
+	index = va_arg(vl, int);
+
+	while (index != -1) {
+		uint8_t elem_tag;
+		uint8_t expect_tag;
+		int prev_index = index;
+
+		buf = asn1_der_find_elem(buf, len_in, index,
+						&elem_tag, &len_in);
 
-	while (pos != -1) {
-		buf = asn1_der_find_elem(buf, len_in, pos, &elem_tag, &len_in);
+		index = va_arg(vl, int);
 
-		pos = va_arg(vl, int);
+		if (prev_index & 0x1000)
+			expect_tag = ASN1_ID(ASN1_CLASS_CONTEXT,
+						index != -1 ? 1 :
+						((elem_tag >> 5) & 1),
+						prev_index & 0xfff);
+		else
+			expect_tag = (index == -1) ? tag : ASN1_ID_SEQUENCE;
 
-		if (!buf || elem_tag != (pos == -1 ? tag : ASN1_ID_SEQUENCE)) {
+		if (!buf || elem_tag != expect_tag) {
 			va_end(vl);
 			return NULL;
 		}
diff --git a/ell/cert.c b/ell/cert.c
index d52bfd0..950e562 100644
--- a/ell/cert.c
+++ b/ell/cert.c
@@ -34,20 +34,20 @@
 
 #define X509_CERTIFICATE_POS			0
 #define   X509_TBSCERTIFICATE_POS		  0
-#define     X509_TBSCERT_VERSION_POS		    0
-#define     X509_TBSCERT_SERIAL_POS		    1
-#define     X509_TBSCERT_SIGNATURE_POS		    2
+#define     X509_TBSCERT_VERSION_POS		    ASN1_CONTEXT_EXPLICIT(0)
+#define     X509_TBSCERT_SERIAL_POS		    0
+#define     X509_TBSCERT_SIGNATURE_POS		    1
 #define       X509_ALGORITHM_ID_ALGORITHM_POS	      0
 #define       X509_ALGORITHM_ID_PARAMS_POS	      1
-#define     X509_TBSCERT_ISSUER_DN_POS		    3
-#define     X509_TBSCERT_VALIDITY_POS		    4
-#define     X509_TBSCERT_SUBJECT_DN_POS		    5
-#define     X509_TBSCERT_SUBJECT_KEY_POS	    6
+#define     X509_TBSCERT_ISSUER_DN_POS		    2
+#define     X509_TBSCERT_VALIDITY_POS		    3
+#define     X509_TBSCERT_SUBJECT_DN_POS		    4
+#define     X509_TBSCERT_SUBJECT_KEY_POS	    5
 #define       X509_SUBJECT_KEY_ALGORITHM_POS	      0
 #define       X509_SUBJECT_KEY_VALUE_POS	      1
-#define     X509_TBSCERT_ISSUER_UID_POS		    7
-#define     X509_TBSCERT_SUBJECT_UID_POS	    8
-#define     X509_TBSCERT_EXTENSIONS_POS		    9
+#define     X509_TBSCERT_ISSUER_UID_POS		    ASN1_CONTEXT_IMPLICIT(1)
+#define     X509_TBSCERT_SUBJECT_UID_POS	    ASN1_CONTEXT_IMPLICIT(2)
+#define     X509_TBSCERT_EXTENSIONS_POS		    ASN1_CONTEXT_EXPLICIT(3)
 #define   X509_SIGNATURE_ALGORITHM_POS		  1
 #define   X509_SIGNATURE_VALUE_POS		  2
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/8] cert: Implement l_cert_get_extension
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 2/8] unit: Add l_tls_set_domain_mask tests Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 3/8] asn1-private: Handle Context-specific tag class Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23  0:41 ` [PATCH 5/8] strv: Implement l_strv_copy Andrew Zaborowski
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3215 bytes --]

Add a utility to find the contents of a given extension.  It is internal
only because asn1_oid is currently internal.
---
 ell/asn1-private.h |  1 +
 ell/cert-private.h |  6 +++++
 ell/cert.c         | 66 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index 7c382ec..90c8913 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -25,6 +25,7 @@
 
 #define ASN1_ID_SEQUENCE	ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x10)
 #define ASN1_ID_SET		ASN1_ID(ASN1_CLASS_UNIVERSAL, 1, 0x11)
+#define ASN1_ID_BOOLEAN		ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x01)
 #define ASN1_ID_INTEGER		ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x02)
 #define ASN1_ID_BIT_STRING	ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x03)
 #define ASN1_ID_OCTET_STRING	ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x04)
diff --git a/ell/cert-private.h b/ell/cert-private.h
index f89e216..ba9a513 100644
--- a/ell/cert-private.h
+++ b/ell/cert-private.h
@@ -20,5 +20,11 @@
  *
  */
 
+struct asn1_oid;
+
 struct l_certchain *certchain_new_from_leaf(struct l_cert *leaf);
 void certchain_link_issuer(struct l_certchain *chain, struct l_cert *issuer);
+
+const uint8_t *cert_get_extension(struct l_cert *cert,
+					const struct asn1_oid *ext_id,
+					bool *out_critical, size_t *out_len);
diff --git a/ell/cert.c b/ell/cert.c
index 950e562..cf7799c 100644
--- a/ell/cert.c
+++ b/ell/cert.c
@@ -171,6 +171,72 @@ LIB_EXPORT const uint8_t *l_cert_get_dn(struct l_cert *cert, size_t *out_len)
 						-1);
 }
 
+const uint8_t *cert_get_extension(struct l_cert *cert,
+					const struct asn1_oid *ext_id,
+					bool *out_critical, size_t *out_len)
+{
+	const uint8_t *ext, *end;
+	size_t ext_len;
+
+	if (unlikely(!cert))
+		return NULL;
+
+	ext = asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len,
+						ASN1_ID_SEQUENCE, &ext_len,
+						X509_CERTIFICATE_POS,
+						X509_TBSCERTIFICATE_POS,
+						X509_TBSCERT_EXTENSIONS_POS,
+						-1);
+	if (unlikely(!ext))
+		return NULL;
+
+	end = ext + ext_len;
+	while (ext < end) {
+		const uint8_t *seq, *oid, *data;
+		uint8_t tag;
+		size_t len, oid_len, data_len;
+		bool critical;
+
+		seq = asn1_der_find_elem(ext, end - ext, 0, &tag, &len);
+		if (unlikely(!seq || tag != ASN1_ID_SEQUENCE))
+			return false;
+
+		ext = seq + len;
+
+		oid = asn1_der_find_elem(seq, len, 0, &tag, &oid_len);
+		if (unlikely(!oid || tag != ASN1_ID_OID))
+			return false;
+
+		if (!asn1_oid_eq(ext_id, oid_len, oid))
+			continue;
+
+		data = asn1_der_find_elem(seq, len, 1, &tag, &data_len);
+		critical = false;
+
+		if (data && tag == ASN1_ID_BOOLEAN) {
+			if (data_len != 1)
+				return false;
+
+			critical = *data != 0;	/* Tolerate BER booleans */
+
+			data = asn1_der_find_elem(seq, len, 2, &tag, &data_len);
+		}
+
+		if (unlikely(!data || tag != ASN1_ID_OCTET_STRING))
+			return false;
+
+		if (out_critical)
+			*out_critical = critical;
+
+		if (out_len)
+			*out_len = data_len;
+
+		return data;
+	}
+
+	return NULL;
+}
+
 LIB_EXPORT enum l_cert_key_type l_cert_get_pubkey_type(struct l_cert *cert)
 {
 	if (unlikely(!cert))
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/8] strv: Implement l_strv_copy
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2019-08-23  0:41 ` [PATCH 4/8] cert: Implement l_cert_get_extension Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23 14:16   ` Denis Kenzior
  2019-08-23  0:41 ` [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask Andrew Zaborowski
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1202 bytes --]

---
 ell/strv.c | 24 ++++++++++++++++++++++++
 ell/strv.h |  1 +
 2 files changed, 25 insertions(+)

diff --git a/ell/strv.c b/ell/strv.c
index cd6467f..7c7dbf5 100644
--- a/ell/strv.c
+++ b/ell/strv.c
@@ -335,3 +335,27 @@ LIB_EXPORT char **l_strv_append_vprintf(char **str_array,
 
 	return ret;
 }
+
+/**
+ * l_strv_copy:
+ * @str_array: a %NULL terminated array of strings or %NULL
+ *
+ * Returns: An independent copy of @str_array.
+ */
+LIB_EXPORT char **l_strv_copy(char **str_array)
+{
+	int i, len;
+	char **copy;
+
+	if (unlikely(!str_array))
+		return NULL;
+
+	for (len = 0; str_array[len]; len++);
+
+	copy = l_malloc(sizeof(char *) * (len + 1));
+
+	for (i = len; i >= 0; i--)
+		copy[i] = l_strdup(str_array[i]);
+
+	return copy;
+}
diff --git a/ell/strv.h b/ell/strv.h
index 11d023a..b0ec17c 100644
--- a/ell/strv.h
+++ b/ell/strv.h
@@ -41,6 +41,7 @@ char **l_strv_append_printf(char **str_array, const char *format, ...)
 					__attribute__((format(printf, 2, 3)));
 char **l_strv_append_vprintf(char **str_array, const char *format,
 							va_list args);
+char **l_strv_copy(char **str_array);
 
 #ifdef __cplusplus
 }
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2019-08-23  0:41 ` [PATCH 5/8] strv: Implement l_strv_copy Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23 14:27   ` Denis Kenzior
  2019-08-23  0:41 ` [PATCH 7/8] build: Add DNSNames to the test server cert Andrew Zaborowski
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 2388 bytes --]

Also return success in the domain_mask check if any of the DNSNames in
the peer certificate's subjectAltName extension matches any of the mask
strings supplied.
---
 ell/tls.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/ell/tls.c b/ell/tls.c
index 3fe2ff5..f1d73bd 100644
--- a/ell/tls.c
+++ b/ell/tls.c
@@ -675,6 +675,10 @@ ok_next:
 
 static const struct asn1_oid dn_common_name_oid =
 	{ 3, { 0x55, 0x04, 0x03 } };
+static const struct asn1_oid subject_alt_name_oid =
+	{ 3, { 0x55, 0x1d, 0x11 } };
+
+#define SAN_DNS_NAME_ID ASN1_CONTEXT_IMPLICIT(2)
 
 static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
 {
@@ -682,10 +686,14 @@ static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
 	size_t dn_size;
 	const char *cn = NULL;
 	size_t cn_len;
+	const uint8_t *san;
+	size_t san_len;
+	uint8_t san_tag;
+	char **i;
 
 	/*
 	 * Retrieve the Common Name from the Subject DN and check if it
-	 * matches.  TODO: possibly also look at SubjectAltName.
+	 * matches.
 	 */
 
 	dn = l_cert_get_dn(cert, &dn_size);
@@ -725,12 +733,43 @@ static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
 		}
 	}
 
-	if (!cn)
+	if (cn)
+		for (i = mask; *i; i++)
+			if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i)))
+				return true;
+
+	/*
+	 * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into
+	 * the sole SEQUENCE element, check if any DNSName matches.
+	 */
+	san = cert_get_extension(cert, &subject_alt_name_oid, NULL, &san_len);
+	if (!san)
 		return false;
 
-	for (; *mask; mask++)
-		if (tls_domain_match_mask(cn, cn_len, *mask, strlen(*mask)))
-			return true;
+	san = asn1_der_find_elem(san, san_len, 0, &san_tag, &san_len);
+	if (unlikely(!san || san_tag != ASN1_ID_SEQUENCE))
+		return NULL;
+
+	end = san + san_len;
+	while (san < end) {
+		const uint8_t *value;
+		uint8_t tag;
+		size_t len;
+
+		value = asn1_der_find_elem(san, end - san, SAN_DNS_NAME_ID,
+						&tag, &len);
+		if (!value)
+			return false;
+
+		/* Type is implicitly IA5STRING */
+
+		for (i = mask; *i; i++)
+			if (tls_domain_match_mask((const char *) value, len,
+							*i, strlen(*i)))
+				return true;
+
+		san = value + len;
+	}
 
 	return false;
 }
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 7/8] build: Add DNSNames to the test server cert
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2019-08-23  0:41 ` [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23 14:29   ` Denis Kenzior
  2019-08-23  0:41 ` [PATCH 8/8] unit: Add TLS tests for cert's DNSName matching Andrew Zaborowski
  2019-08-23 14:28 ` [PATCH 1/8] tls: Implement l_tls_set_domain_mask Denis Kenzior
  7 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]

---
 Makefile.am       | 2 +-
 unit/gencerts.cnf | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/Makefile.am b/Makefile.am
index 420c839..bcee4e7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -391,7 +391,7 @@ unit/cert-server.csr: unit/cert-server-key.pem unit/gencerts.cnf
 			-key $< -out $@
 
 unit/cert-server.pem: unit/cert-server.csr unit/cert-ca.pem unit/gencerts.cnf
-	$(AM_V_GEN)openssl x509 -req -extensions cert_ext \
+	$(AM_V_GEN)openssl x509 -req -extensions server_ext \
 			-extfile $(srcdir)/unit/gencerts.cnf \
 			-in $< -CA $(builddir)/unit/cert-ca.pem \
 			-CAkey $(builddir)/unit/cert-ca-key.pem \
diff --git a/unit/gencerts.cnf b/unit/gencerts.cnf
index 5328734..dc469e8 100644
--- a/unit/gencerts.cnf
+++ b/unit/gencerts.cnf
@@ -17,3 +17,9 @@ authorityKeyIdentifier = keyid:always,issuer:always
 basicConstraints = CA:FALSE
 subjectKeyIdentifier = hash
 authorityKeyIdentifier = keyid:always,issuer:always
+
+[ server_ext ]
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+subjectAltName = DNS:foo.int.example,DNS:foo.int.com
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 8/8] unit: Add TLS tests for cert's DNSName matching
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
                   ` (5 preceding siblings ...)
  2019-08-23  0:41 ` [PATCH 7/8] build: Add DNSNames to the test server cert Andrew Zaborowski
@ 2019-08-23  0:41 ` Andrew Zaborowski
  2019-08-23 14:28 ` [PATCH 1/8] tls: Implement l_tls_set_domain_mask Denis Kenzior
  7 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23  0:41 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 4755 bytes --]

---
 unit/test-tls.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/unit/test-tls.c b/unit/test-tls.c
index 36f9934..2e88235 100644
--- a/unit/test-tls.c
+++ b/unit/test-tls.c
@@ -680,6 +680,48 @@ static const struct tls_conn_test tls_conn_test_domain_match4 = {
 	.client_domain_mask = (char *[]) { "*", NULL },
 };
 
+static const struct tls_conn_test tls_conn_test_domain_match5 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "foo.int.com", NULL },
+};
+
+static const struct tls_conn_test tls_conn_test_domain_match6 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "*.*", NULL },
+};
+
+static const struct tls_conn_test tls_conn_test_domain_match7 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) { "*.*.*", NULL },
+};
+
 static const struct tls_conn_test tls_conn_test_domain_mismatch1 = {
 	.server_cert_path = CERTDIR "cert-server.pem",
 	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
@@ -766,6 +808,24 @@ static const struct tls_conn_test tls_conn_test_domain_mismatch5 = {
 	.alert_desc = TLS_ALERT_BAD_CERT,
 };
 
+static const struct tls_conn_test tls_conn_test_domain_mismatch6 = {
+	.server_cert_path = CERTDIR "cert-server.pem",
+	.server_key_path = CERTDIR "cert-server-key-pkcs8.pem",
+	.server_ca_cert_path = CERTDIR "cert-ca.pem",
+	.server_expect_identity = "/O=Bar Example Organization"
+		"/CN=Bar Example Organization/emailAddress=bar(a)mail.example",
+	.client_cert_path = CERTDIR "cert-client.pem",
+	.client_key_path = CERTDIR "cert-client-key-pkcs8.pem",
+	.client_ca_cert_path = CERTDIR "cert-ca.pem",
+	.client_expect_identity = "/O=Foo Example Organization"
+		"/CN=Foo Example Organization/emailAddress=foo(a)mail.example",
+	.client_domain_mask = (char *[]) {
+		"foo.*", NULL
+	},
+	.expect_alert = true,
+	.alert_desc = TLS_ALERT_BAD_CERT,
+};
+
 static void test_tls_suite_test(const void *data)
 {
 	const char *suite_name = data;
@@ -878,6 +938,12 @@ int main(int argc, char *argv[])
 			&tls_conn_test_domain_match3);
 	l_test_add("TLS connection domain match 4", test_tls_test,
 			&tls_conn_test_domain_match4);
+	l_test_add("TLS connection domain match 5", test_tls_test,
+			&tls_conn_test_domain_match5);
+	l_test_add("TLS connection domain match 6", test_tls_test,
+			&tls_conn_test_domain_match6);
+	l_test_add("TLS connection domain match 7", test_tls_test,
+			&tls_conn_test_domain_match7);
 	l_test_add("TLS connection domain mismatch 1", test_tls_test,
 			&tls_conn_test_domain_mismatch1);
 	l_test_add("TLS connection domain mismatch 2", test_tls_test,
@@ -888,6 +954,8 @@ int main(int argc, char *argv[])
 			&tls_conn_test_domain_mismatch4);
 	l_test_add("TLS connection domain mismatch 5", test_tls_test,
 			&tls_conn_test_domain_mismatch5);
+	l_test_add("TLS connection domain mismatch 6", test_tls_test,
+			&tls_conn_test_domain_mismatch6);
 
 	for (i = 0; tls_cipher_suite_pref[i]; i++) {
 		struct tls_cipher_suite *suite = tls_cipher_suite_pref[i];
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 5/8] strv: Implement l_strv_copy
  2019-08-23  0:41 ` [PATCH 5/8] strv: Implement l_strv_copy Andrew Zaborowski
@ 2019-08-23 14:16   ` Denis Kenzior
  0 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2019-08-23 14:16 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 246 bytes --]

Hi Andrew,

On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> ---
>   ell/strv.c | 24 ++++++++++++++++++++++++
>   ell/strv.h |  1 +
>   2 files changed, 25 insertions(+)
> 

Applied with an additional ell.sym tweak.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask
  2019-08-23  0:41 ` [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask Andrew Zaborowski
@ 2019-08-23 14:27   ` Denis Kenzior
  2019-08-23 17:51     ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2019-08-23 14:27 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 2788 bytes --]

Hi Andrew,

On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> Also return success in the domain_mask check if any of the DNSNames in
> the peer certificate's subjectAltName extension matches any of the mask
> strings supplied.
> ---
>   ell/tls.c | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 44 insertions(+), 5 deletions(-)
> 
> diff --git a/ell/tls.c b/ell/tls.c
> index 3fe2ff5..f1d73bd 100644
> --- a/ell/tls.c
> +++ b/ell/tls.c
> @@ -675,6 +675,10 @@ ok_next:
>   
>   static const struct asn1_oid dn_common_name_oid =
>   	{ 3, { 0x55, 0x04, 0x03 } };
> +static const struct asn1_oid subject_alt_name_oid =
> +	{ 3, { 0x55, 0x1d, 0x11 } };
> +
> +#define SAN_DNS_NAME_ID ASN1_CONTEXT_IMPLICIT(2)
>   
>   static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
>   {
> @@ -682,10 +686,14 @@ static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
>   	size_t dn_size;
>   	const char *cn = NULL;
>   	size_t cn_len;
> +	const uint8_t *san;
> +	size_t san_len;
> +	uint8_t san_tag;
> +	char **i;
>   
>   	/*
>   	 * Retrieve the Common Name from the Subject DN and check if it
> -	 * matches.  TODO: possibly also look at SubjectAltName.
> +	 * matches.
>   	 */
>   
>   	dn = l_cert_get_dn(cert, &dn_size);
> @@ -725,12 +733,43 @@ static bool tls_cert_domains_match_mask(struct l_cert *cert, char **mask)
>   		}
>   	}
>   
> -	if (!cn)
> +	if (cn)
> +		for (i = mask; *i; i++)
> +			if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i)))
> +				return true;
> +
> +	/*
> +	 * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into
> +	 * the sole SEQUENCE element, check if any DNSName matches.
> +	 */

So I think the recommended approach is to check all SubjectAltName 
dNSName tags first.  And only then try to match the CN, no?

> +	san = cert_get_extension(cert, &subject_alt_name_oid, NULL, &san_len);
> +	if (!san)
>   		return false;
>   
> -	for (; *mask; mask++)
> -		if (tls_domain_match_mask(cn, cn_len, *mask, strlen(*mask)))
> -			return true;
> +	san = asn1_der_find_elem(san, san_len, 0, &san_tag, &san_len);
> +	if (unlikely(!san || san_tag != ASN1_ID_SEQUENCE))
> +		return NULL;
> +
> +	end = san + san_len;
> +	while (san < end) {
> +		const uint8_t *value;
> +		uint8_t tag;
> +		size_t len;
> +
> +		value = asn1_der_find_elem(san, end - san, SAN_DNS_NAME_ID,
> +						&tag, &len);
> +		if (!value)
> +			return false;
> +
> +		/* Type is implicitly IA5STRING */
> +
> +		for (i = mask; *i; i++)
> +			if (tls_domain_match_mask((const char *) value, len,
> +							*i, strlen(*i)))
> +				return true;
> +
> +		san = value + len;
> +	}
>   
>   	return false;
>   }
> 

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/8] tls: Implement l_tls_set_domain_mask
  2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
                   ` (6 preceding siblings ...)
  2019-08-23  0:41 ` [PATCH 8/8] unit: Add TLS tests for cert's DNSName matching Andrew Zaborowski
@ 2019-08-23 14:28 ` Denis Kenzior
  7 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2019-08-23 14:28 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 419 bytes --]

Hi Andrew,

On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> Allow user to set a mask that the a peer certificate's subject CN
> has to match.
> ---
>   ell/ell.sym       |   1 +
>   ell/tls-private.h |   1 +
>   ell/tls.c         | 143 ++++++++++++++++++++++++++++++++++++++++++++++
>   ell/tls.h         |   2 +
>   4 files changed, 147 insertions(+)
> 

Patches 1-4 applied, thanks.

Regards,
Denis


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 7/8] build: Add DNSNames to the test server cert
  2019-08-23  0:41 ` [PATCH 7/8] build: Add DNSNames to the test server cert Andrew Zaborowski
@ 2019-08-23 14:29   ` Denis Kenzior
  0 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2019-08-23 14:29 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 224 bytes --]

Hi Andrew,

On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> ---
>   Makefile.am       | 2 +-
>   unit/gencerts.cnf | 6 ++++++
>   2 files changed, 7 insertions(+), 1 deletion(-)

Applied, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask
  2019-08-23 14:27   ` Denis Kenzior
@ 2019-08-23 17:51     ` Andrew Zaborowski
  2019-08-23 20:21       ` Denis Kenzior
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23 17:51 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 927 bytes --]

Hi Denis,

On Fri, 23 Aug 2019 at 18:00, Denis Kenzior <denkenz@gmail.com> wrote:
> On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> > -     if (!cn)
> > +     if (cn)
> > +             for (i = mask; *i; i++)
> > +                     if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i)))
> > +                             return true;
> > +
> > +     /*
> > +      * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into
> > +      * the sole SEQUENCE element, check if any DNSName matches.
> > +      */
>
> So I think the recommended approach is to check all SubjectAltName
> dNSName tags first.  And only then try to match the CN, no?

Maybe more optimal, so I can switch this around, although as far as
I've seen in HTTPS certificates the server name is usually right in
the CN.  The return value is going to be the same independent of the
order in which we do the checks.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask
  2019-08-23 17:51     ` Andrew Zaborowski
@ 2019-08-23 20:21       ` Denis Kenzior
  2019-08-23 23:50         ` Andrew Zaborowski
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2019-08-23 20:21 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1442 bytes --]

Hi Andrew,

On 8/23/19 12:51 PM, Andrew Zaborowski wrote:
> Hi Denis,
> 
> On Fri, 23 Aug 2019 at 18:00, Denis Kenzior <denkenz@gmail.com> wrote:
>> On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
>>> -     if (!cn)
>>> +     if (cn)
>>> +             for (i = mask; *i; i++)
>>> +                     if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i)))
>>> +                             return true;
>>> +
>>> +     /*
>>> +      * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into
>>> +      * the sole SEQUENCE element, check if any DNSName matches.
>>> +      */
>>
>> So I think the recommended approach is to check all SubjectAltName
>> dNSName tags first.  And only then try to match the CN, no?
> 
> Maybe more optimal, so I can switch this around, although as far as
> I've seen in HTTPS certificates the server name is usually right in
> the CN.  The return value is going to be the same independent of the
> order in which we do the checks.

Yes, I think you're right that the check order doesn't really matter in 
the end.  I'm just thinking the code would be more readable doing it in 
the order that the the spec wants.  You can quote the Hotspot spec if 
need-be.

Note that for certain certificate types WiFi Alliance wants us to only 
check dNSName inside SubjectAltName (without the CN check).  But we 
don't support those, so we'll deal with that later.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask
  2019-08-23 20:21       ` Denis Kenzior
@ 2019-08-23 23:50         ` Andrew Zaborowski
  0 siblings, 0 replies; 15+ messages in thread
From: Andrew Zaborowski @ 2019-08-23 23:50 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 1357 bytes --]

Hi Denis,

On Fri, 23 Aug 2019 at 23:54, Denis Kenzior <denkenz@gmail.com> wrote:
> On 8/23/19 12:51 PM, Andrew Zaborowski wrote:
> > On Fri, 23 Aug 2019 at 18:00, Denis Kenzior <denkenz@gmail.com> wrote:
> >> On 8/22/19 7:41 PM, Andrew Zaborowski wrote:
> >>> -     if (!cn)
> >>> +     if (cn)
> >>> +             for (i = mask; *i; i++)
> >>> +                     if (tls_domain_match_mask(cn, cn_len, *i, strlen(*i)))
> >>> +                             return true;
> >>> +
> >>> +     /*
> >>> +      * Locate SubjectAltName (RFC5280 Section 4.2.1.6) and descend into
> >>> +      * the sole SEQUENCE element, check if any DNSName matches.
> >>> +      */
> >>
> >> So I think the recommended approach is to check all SubjectAltName
> >> dNSName tags first.  And only then try to match the CN, no?
> >
> > Maybe more optimal, so I can switch this around, although as far as
> > I've seen in HTTPS certificates the server name is usually right in
> > the CN.  The return value is going to be the same independent of the
> > order in which we do the checks.
>
> Yes, I think you're right that the check order doesn't really matter in
> the end.  I'm just thinking the code would be more readable doing it in
> the order that the the spec wants.  You can quote the Hotspot spec if
> need-be.

Ok, will do.

Best regards

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2019-08-23 23:50 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-23  0:41 [PATCH 1/8] tls: Implement l_tls_set_domain_mask Andrew Zaborowski
2019-08-23  0:41 ` [PATCH 2/8] unit: Add l_tls_set_domain_mask tests Andrew Zaborowski
2019-08-23  0:41 ` [PATCH 3/8] asn1-private: Handle Context-specific tag class Andrew Zaborowski
2019-08-23  0:41 ` [PATCH 4/8] cert: Implement l_cert_get_extension Andrew Zaborowski
2019-08-23  0:41 ` [PATCH 5/8] strv: Implement l_strv_copy Andrew Zaborowski
2019-08-23 14:16   ` Denis Kenzior
2019-08-23  0:41 ` [PATCH 6/8] tls: Validate peer certificate's DNSNames against mask Andrew Zaborowski
2019-08-23 14:27   ` Denis Kenzior
2019-08-23 17:51     ` Andrew Zaborowski
2019-08-23 20:21       ` Denis Kenzior
2019-08-23 23:50         ` Andrew Zaborowski
2019-08-23  0:41 ` [PATCH 7/8] build: Add DNSNames to the test server cert Andrew Zaborowski
2019-08-23 14:29   ` Denis Kenzior
2019-08-23  0:41 ` [PATCH 8/8] unit: Add TLS tests for cert's DNSName matching Andrew Zaborowski
2019-08-23 14:28 ` [PATCH 1/8] tls: Implement l_tls_set_domain_mask Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.