changeset 22017:62660946454b

lib-ssl-iostream: Support IP address SANs
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Mon, 08 May 2017 13:35:35 +0300
parents 6389f6b095af
children c635141adb77
files src/lib-ssl-iostream/iostream-openssl-common.c
diffstat 1 files changed, 38 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-ssl-iostream/iostream-openssl-common.c	Fri May 05 12:35:23 2017 +0300
+++ b/src/lib-ssl-iostream/iostream-openssl-common.c	Mon May 08 13:35:35 2017 +0300
@@ -1,11 +1,13 @@
 /* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "net.h"
 #include "str.h"
 #include "iostream-openssl.h"
 
 #include <openssl/x509v3.h>
 #include <openssl/err.h>
+#include <arpa/inet.h>
 
 enum {
 	DOVECOT_SSL_PROTO_SSLv2		= 0x01,
@@ -101,6 +103,23 @@
 	return asn1_string_to_c(name->d.ia5);
 }
 
+static int get_general_ip_addr(const GENERAL_NAME *name, struct ip_addr *ip_r)
+{
+	if (ASN1_STRING_type(name->d.ip) != V_ASN1_OCTET_STRING)
+		return 0;
+	const unsigned char *data = ASN1_STRING_get0_data(name->d.ip);
+
+	if (name->d.ip->length == sizeof(ip_r->u.ip4.s_addr)) {
+		ip_r->family = AF_INET;
+		memcpy(&ip_r->u.ip4.s_addr, data, sizeof(ip_r->u.ip4.s_addr));
+	} else if (name->d.ip->length == sizeof(ip_r->u.ip6.s6_addr)) {
+		ip_r->family = AF_INET6;
+		memcpy(ip_r->u.ip6.s6_addr, data, sizeof(ip_r->u.ip6.s6_addr));
+	} else
+		return -1;
+	return 0;
+}
+
 static const char *get_cname(X509 *cert)
 {
 	X509_NAME *name;
@@ -140,6 +159,7 @@
 	X509 *cert;
 	STACK_OF(GENERAL_NAME) *gnames;
 	const GENERAL_NAME *gn;
+	struct ip_addr ip;
 	const char *dnsname;
 	bool dns_names = FALSE;
 	unsigned int i, count;
@@ -151,13 +171,31 @@
 	/* verify against SubjectAltNames */
 	gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 	count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
+
+	i_zero(&ip);
+	/* try to convert verify_name to IP */
+	if (inet_pton(AF_INET6, verify_name, &ip.u.ip6) == 1)
+		ip.family = AF_INET6;
+	else if (inet_pton(AF_INET, verify_name, &ip.u.ip4) == 1)
+		ip.family = AF_INET;
+	else
+		i_zero(&ip);
+
 	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 (openssl_hostname_equals(dnsname, verify_name))
 				break;
+		} else if (gn->type == GEN_IPADD) {
+			struct ip_addr ip_2;
+			i_zero(&ip_2);
+			dns_names = TRUE;
+			if (get_general_ip_addr(gn, &ip_2) == 0 &&
+			    net_ip_compare(&ip, &ip_2))
+				break;
 		}
 	}
 	sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);