dovecot-2.1: lib-ssl-iostream: Added ssl_iostream_cert_match_name()

dovecot at dovecot.org dovecot at dovecot.org
Tue Sep 6 16:33:25 EEST 2011


details:   http://hg.dovecot.org/dovecot-2.1/rev/c3dc563c9800
changeset: 13404:c3dc563c9800
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Sep 06 16:32:20 2011 +0300
description:
lib-ssl-iostream: Added ssl_iostream_cert_match_name()

diffstat:

 src/lib-ssl-iostream/iostream-openssl.c |  107 +++++++++++++++++++++++++++++--
 src/lib-ssl-iostream/iostream-ssl.h     |    1 +
 2 files changed, 99 insertions(+), 9 deletions(-)

diffs (171 lines):

diff -r 6a3f3a5ad9a5 -r c3dc563c9800 src/lib-ssl-iostream/iostream-openssl.c
--- a/src/lib-ssl-iostream/iostream-openssl.c	Tue Sep 06 13:40:50 2011 +0300
+++ b/src/lib-ssl-iostream/iostream-openssl.c	Tue Sep 06 16:32:20 2011 +0300
@@ -6,6 +6,7 @@
 #include "iostream-openssl.h"
 
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 
 static void ssl_iostream_free(struct ssl_iostream *ssl_io);
 
@@ -94,7 +95,8 @@
 		X509_NAME *subject;
 
 		subject = X509_get_subject_name(ctx->current_cert);
-		if (X509_NAME_oneline(subject, buf, sizeof(buf)) == NULL)
+		if (subject == NULL ||
+		    X509_NAME_oneline(subject, buf, sizeof(buf)) == NULL)
 			buf[0] = '\0';
 		else
 			buf[sizeof(buf)-1] = '\0'; /* just in case.. */
@@ -118,6 +120,7 @@
 		 const struct ssl_iostream_settings *set)
 {
 	const struct ssl_iostream_settings *ctx_set = ssl_io->ctx->set;
+	int verify_flags;
 
 	if (set->verbose)
 		SSL_set_info_callback(ssl_io->ssl, ssl_info_callback);
@@ -141,8 +144,11 @@
 			return -1;
 	}
 	if (set->verify_remote_cert) {
-		SSL_set_verify(ssl_io->ssl,
-			       SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
+		if (ssl_io->ctx->client_ctx)
+			verify_flags = SSL_VERIFY_NONE;
+		else
+			verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+		SSL_set_verify(ssl_io->ssl, verify_flags,
 			       ssl_iostream_verify_client_cert);
 	}
 
@@ -345,6 +351,12 @@
 	return 1;
 }
 
+static void ssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
+{
+	i_free(ssl_io->last_error);
+	ssl_io->last_error = i_strdup(str);
+}
+
 int ssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
 			      const char *func_name)
 {
@@ -397,11 +409,89 @@
 		break;
 	}
 
-	if (errstr != NULL) {
-		i_free(ssl_io->last_error);
-		ssl_io->last_error = i_strdup(errstr);
+	if (errstr != NULL)
+		ssl_iostream_set_error(ssl_io, errstr);
+	return -1;
+}
+
+static const char *asn1_string_to_c(ASN1_STRING *asn_str)
+{
+	const char *cstr;
+	unsigned int len;
+
+	len = ASN1_STRING_length(asn_str);
+	cstr = t_strndup(ASN1_STRING_data(asn_str), len);
+	if (strlen(cstr) != len) {
+		/* NULs in the name - could be some MITM attack.
+		   never allow. */
+		return "";
 	}
-	return -1;
+	return cstr;
+}
+
+static const char *get_general_dns_name(const GENERAL_NAME *name)
+{
+	if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING)
+		return "";
+
+	return asn1_string_to_c(name->d.ia5);
+}
+
+static const char *get_cname(X509 *cert)
+{
+	X509_NAME *name;
+	X509_NAME_ENTRY *entry;
+	ASN1_STRING *str;
+	int cn_idx;
+
+	name = X509_get_subject_name(cert);
+	if (name == NULL)
+		return "";
+	cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+	if (cn_idx == -1)
+		return "";
+	entry = X509_NAME_get_entry(name, cn_idx);
+	i_assert(entry != NULL);
+	str = X509_NAME_ENTRY_get_data(entry);
+	i_assert(str != NULL);
+	return asn1_string_to_c(str);
+}
+
+int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io,
+				 const char *verify_name)
+{
+	X509 *cert;
+	STACK_OF(GENERAL_NAME) *gnames;
+	const GENERAL_NAME *gn;
+	const char *dnsname;
+	bool dns_names = FALSE;
+	unsigned int i, count;
+
+	if (!ssl_iostream_has_valid_client_cert(ssl_io))
+		return -1;
+
+	cert = SSL_get_peer_certificate(ssl_io->ssl);
+	i_assert(cert != NULL);
+
+	/* verify against SubjectAltNames */
+	gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
+	for (i = 0; i < count; i++) {
+		gn = sk_GENERAL_NAME_value(gnames, i);
+		if (gn->type == GEN_DNS) {
+			dns_names = TRUE;
+			dnsname = get_general_dns_name(gn);
+			if (strcmp(dnsname, verify_name) == 0)
+				break;
+		}
+	}
+	sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);
+	/* verify against CommonName only when there wasn't any DNS
+	   SubjectAltNames */
+	if (dns_names)
+		return i < count ? 0 : -1;
+
+	return strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
 }
 
 int ssl_iostream_handshake(struct ssl_iostream *ssl_io)
@@ -475,8 +565,7 @@
 		return NULL;
 
 	x509 = SSL_get_peer_certificate(ssl_io->ssl);
-	if (x509 == NULL)
-		return NULL; /* we should have had it.. */
+	i_assert(x509 != NULL);
 
 	len = X509_NAME_get_text_by_NID(X509_get_subject_name(x509),
 					ssl_io->username_nid, NULL, 0);
diff -r 6a3f3a5ad9a5 -r c3dc563c9800 src/lib-ssl-iostream/iostream-ssl.h
--- a/src/lib-ssl-iostream/iostream-ssl.h	Tue Sep 06 13:40:50 2011 +0300
+++ b/src/lib-ssl-iostream/iostream-ssl.h	Tue Sep 06 16:32:20 2011 +0300
@@ -32,6 +32,7 @@
 bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io);
 bool ssl_iostream_has_valid_client_cert(const struct ssl_iostream *ssl_io);
 bool ssl_iostream_has_broken_client_cert(struct ssl_iostream *ssl_io);
+int ssl_iostream_cert_match_name(struct ssl_iostream *ssl_io, const char *name);
 const char *ssl_iostream_get_peer_name(struct ssl_iostream *ssl_io);
 const char *ssl_iostream_get_security_string(struct ssl_iostream *ssl_io);
 const char *ssl_iostream_get_last_error(struct ssl_iostream *ssl_io);


More information about the dovecot-cvs mailing list