xref: /dragonfly/crypto/libressl/tls/tls_verify.c (revision 72c33676)
172c33676SMaxim Ag /* $OpenBSD: tls_verify.c,v 1.20 2018/02/05 00:52:24 jsing Exp $ */
2f5b1c8a1SJohn Marino /*
3f5b1c8a1SJohn Marino  * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
4f5b1c8a1SJohn Marino  *
5f5b1c8a1SJohn Marino  * Permission to use, copy, modify, and distribute this software for any
6f5b1c8a1SJohn Marino  * purpose with or without fee is hereby granted, provided that the above
7f5b1c8a1SJohn Marino  * copyright notice and this permission notice appear in all copies.
8f5b1c8a1SJohn Marino  *
9f5b1c8a1SJohn Marino  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f5b1c8a1SJohn Marino  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f5b1c8a1SJohn Marino  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f5b1c8a1SJohn Marino  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f5b1c8a1SJohn Marino  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f5b1c8a1SJohn Marino  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f5b1c8a1SJohn Marino  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f5b1c8a1SJohn Marino  */
17f5b1c8a1SJohn Marino 
18f5b1c8a1SJohn Marino #include <sys/socket.h>
19f5b1c8a1SJohn Marino 
20f5b1c8a1SJohn Marino #include <arpa/inet.h>
21f5b1c8a1SJohn Marino #include <netinet/in.h>
22f5b1c8a1SJohn Marino 
23f5b1c8a1SJohn Marino #include <string.h>
24f5b1c8a1SJohn Marino 
25f5b1c8a1SJohn Marino #include <openssl/x509v3.h>
26f5b1c8a1SJohn Marino 
2772c33676SMaxim Ag #include <tls.h>
28f5b1c8a1SJohn Marino #include "tls_internal.h"
29f5b1c8a1SJohn Marino 
30f5b1c8a1SJohn Marino static int
tls_match_name(const char * cert_name,const char * name)31f5b1c8a1SJohn Marino tls_match_name(const char *cert_name, const char *name)
32f5b1c8a1SJohn Marino {
33f5b1c8a1SJohn Marino 	const char *cert_domain, *domain, *next_dot;
34f5b1c8a1SJohn Marino 
35f5b1c8a1SJohn Marino 	if (strcasecmp(cert_name, name) == 0)
36f5b1c8a1SJohn Marino 		return 0;
37f5b1c8a1SJohn Marino 
38f5b1c8a1SJohn Marino 	/* Wildcard match? */
39f5b1c8a1SJohn Marino 	if (cert_name[0] == '*') {
40f5b1c8a1SJohn Marino 		/*
41f5b1c8a1SJohn Marino 		 * Valid wildcards:
42f5b1c8a1SJohn Marino 		 * - "*.domain.tld"
43f5b1c8a1SJohn Marino 		 * - "*.sub.domain.tld"
44f5b1c8a1SJohn Marino 		 * - etc.
45f5b1c8a1SJohn Marino 		 * Reject "*.tld".
46f5b1c8a1SJohn Marino 		 * No attempt to prevent the use of eg. "*.co.uk".
47f5b1c8a1SJohn Marino 		 */
48f5b1c8a1SJohn Marino 		cert_domain = &cert_name[1];
49f5b1c8a1SJohn Marino 		/* Disallow "*"  */
50f5b1c8a1SJohn Marino 		if (cert_domain[0] == '\0')
51f5b1c8a1SJohn Marino 			return -1;
52f5b1c8a1SJohn Marino 		/* Disallow "*foo" */
53f5b1c8a1SJohn Marino 		if (cert_domain[0] != '.')
54f5b1c8a1SJohn Marino 			return -1;
55f5b1c8a1SJohn Marino 		/* Disallow "*.." */
56f5b1c8a1SJohn Marino 		if (cert_domain[1] == '.')
57f5b1c8a1SJohn Marino 			return -1;
58f5b1c8a1SJohn Marino 		next_dot = strchr(&cert_domain[1], '.');
59f5b1c8a1SJohn Marino 		/* Disallow "*.bar" */
60f5b1c8a1SJohn Marino 		if (next_dot == NULL)
61f5b1c8a1SJohn Marino 			return -1;
62f5b1c8a1SJohn Marino 		/* Disallow "*.bar.." */
63f5b1c8a1SJohn Marino 		if (next_dot[1] == '.')
64f5b1c8a1SJohn Marino 			return -1;
65f5b1c8a1SJohn Marino 
66f5b1c8a1SJohn Marino 		domain = strchr(name, '.');
67f5b1c8a1SJohn Marino 
68f5b1c8a1SJohn Marino 		/* No wildcard match against a name with no host part. */
69f5b1c8a1SJohn Marino 		if (name[0] == '.')
70f5b1c8a1SJohn Marino 			return -1;
71f5b1c8a1SJohn Marino 		/* No wildcard match against a name with no domain part. */
72f5b1c8a1SJohn Marino 		if (domain == NULL || strlen(domain) == 1)
73f5b1c8a1SJohn Marino 			return -1;
74f5b1c8a1SJohn Marino 
75f5b1c8a1SJohn Marino 		if (strcasecmp(cert_domain, domain) == 0)
76f5b1c8a1SJohn Marino 			return 0;
77f5b1c8a1SJohn Marino 	}
78f5b1c8a1SJohn Marino 
79f5b1c8a1SJohn Marino 	return -1;
80f5b1c8a1SJohn Marino }
81f5b1c8a1SJohn Marino 
8272c33676SMaxim Ag /*
8372c33676SMaxim Ag  * See RFC 5280 section 4.2.1.6 for SubjectAltName details.
8472c33676SMaxim Ag  * alt_match is set to 1 if a matching alternate name is found.
8572c33676SMaxim Ag  * alt_exists is set to 1 if any known alternate name exists in the certificate.
8672c33676SMaxim Ag  */
87f5b1c8a1SJohn Marino static int
tls_check_subject_altname(struct tls * ctx,X509 * cert,const char * name,int * alt_match,int * alt_exists)8872c33676SMaxim Ag tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
8972c33676SMaxim Ag     int *alt_match, int *alt_exists)
90f5b1c8a1SJohn Marino {
91f5b1c8a1SJohn Marino 	STACK_OF(GENERAL_NAME) *altname_stack = NULL;
92f5b1c8a1SJohn Marino 	union tls_addr addrbuf;
93f5b1c8a1SJohn Marino 	int addrlen, type;
94f5b1c8a1SJohn Marino 	int count, i;
9572c33676SMaxim Ag 	int rv = 0;
9672c33676SMaxim Ag 
9772c33676SMaxim Ag 	*alt_match = 0;
9872c33676SMaxim Ag 	*alt_exists = 0;
99f5b1c8a1SJohn Marino 
100f5b1c8a1SJohn Marino 	altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
101f5b1c8a1SJohn Marino 	    NULL, NULL);
102f5b1c8a1SJohn Marino 	if (altname_stack == NULL)
10372c33676SMaxim Ag 		return 0;
104f5b1c8a1SJohn Marino 
105f5b1c8a1SJohn Marino 	if (inet_pton(AF_INET, name, &addrbuf) == 1) {
106f5b1c8a1SJohn Marino 		type = GEN_IPADD;
107f5b1c8a1SJohn Marino 		addrlen = 4;
108f5b1c8a1SJohn Marino 	} else if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
109f5b1c8a1SJohn Marino 		type = GEN_IPADD;
110f5b1c8a1SJohn Marino 		addrlen = 16;
111f5b1c8a1SJohn Marino 	} else {
112f5b1c8a1SJohn Marino 		type = GEN_DNS;
113f5b1c8a1SJohn Marino 		addrlen = 0;
114f5b1c8a1SJohn Marino 	}
115f5b1c8a1SJohn Marino 
116f5b1c8a1SJohn Marino 	count = sk_GENERAL_NAME_num(altname_stack);
117f5b1c8a1SJohn Marino 	for (i = 0; i < count; i++) {
118f5b1c8a1SJohn Marino 		GENERAL_NAME	*altname;
119f5b1c8a1SJohn Marino 
120f5b1c8a1SJohn Marino 		altname = sk_GENERAL_NAME_value(altname_stack, i);
121f5b1c8a1SJohn Marino 
12272c33676SMaxim Ag 		if (altname->type == GEN_DNS || altname->type == GEN_IPADD)
12372c33676SMaxim Ag 			*alt_exists = 1;
12472c33676SMaxim Ag 
125f5b1c8a1SJohn Marino 		if (altname->type != type)
126f5b1c8a1SJohn Marino 			continue;
127f5b1c8a1SJohn Marino 
128f5b1c8a1SJohn Marino 		if (type == GEN_DNS) {
129f5b1c8a1SJohn Marino 			unsigned char	*data;
130f5b1c8a1SJohn Marino 			int		 format, len;
131f5b1c8a1SJohn Marino 
132f5b1c8a1SJohn Marino 			format = ASN1_STRING_type(altname->d.dNSName);
133f5b1c8a1SJohn Marino 			if (format == V_ASN1_IA5STRING) {
134f5b1c8a1SJohn Marino 				data = ASN1_STRING_data(altname->d.dNSName);
135f5b1c8a1SJohn Marino 				len = ASN1_STRING_length(altname->d.dNSName);
136f5b1c8a1SJohn Marino 
13772c33676SMaxim Ag 				if (len < 0 || (size_t)len != strlen(data)) {
138f5b1c8a1SJohn Marino 					tls_set_errorx(ctx,
139f5b1c8a1SJohn Marino 					    "error verifying name '%s': "
140f5b1c8a1SJohn Marino 					    "NUL byte in subjectAltName, "
141f5b1c8a1SJohn Marino 					    "probably a malicious certificate",
142f5b1c8a1SJohn Marino 					    name);
14372c33676SMaxim Ag 					rv = -1;
144f5b1c8a1SJohn Marino 					break;
145f5b1c8a1SJohn Marino 				}
146f5b1c8a1SJohn Marino 
147f5b1c8a1SJohn Marino 				/*
148f5b1c8a1SJohn Marino 				 * Per RFC 5280 section 4.2.1.6:
149f5b1c8a1SJohn Marino 				 * " " is a legal domain name, but that
150f5b1c8a1SJohn Marino 				 * dNSName must be rejected.
151f5b1c8a1SJohn Marino 				 */
152f5b1c8a1SJohn Marino 				if (strcmp(data, " ") == 0) {
15372c33676SMaxim Ag 					tls_set_errorx(ctx,
154f5b1c8a1SJohn Marino 					    "error verifying name '%s': "
155f5b1c8a1SJohn Marino 					    "a dNSName of \" \" must not be "
156f5b1c8a1SJohn Marino 					    "used", name);
15772c33676SMaxim Ag 					rv = -1;
158f5b1c8a1SJohn Marino 					break;
159f5b1c8a1SJohn Marino 				}
160f5b1c8a1SJohn Marino 
161f5b1c8a1SJohn Marino 				if (tls_match_name(data, name) == 0) {
16272c33676SMaxim Ag 					*alt_match = 1;
163f5b1c8a1SJohn Marino 					break;
164f5b1c8a1SJohn Marino 				}
165f5b1c8a1SJohn Marino 			} else {
166f5b1c8a1SJohn Marino #ifdef DEBUG
167f5b1c8a1SJohn Marino 				fprintf(stdout, "%s: unhandled subjectAltName "
168f5b1c8a1SJohn Marino 				    "dNSName encoding (%d)\n", getprogname(),
169f5b1c8a1SJohn Marino 				    format);
170f5b1c8a1SJohn Marino #endif
171f5b1c8a1SJohn Marino 			}
172f5b1c8a1SJohn Marino 
173f5b1c8a1SJohn Marino 		} else if (type == GEN_IPADD) {
174f5b1c8a1SJohn Marino 			unsigned char	*data;
175f5b1c8a1SJohn Marino 			int		 datalen;
176f5b1c8a1SJohn Marino 
177f5b1c8a1SJohn Marino 			datalen = ASN1_STRING_length(altname->d.iPAddress);
178f5b1c8a1SJohn Marino 			data = ASN1_STRING_data(altname->d.iPAddress);
179f5b1c8a1SJohn Marino 
180f5b1c8a1SJohn Marino 			if (datalen < 0) {
181f5b1c8a1SJohn Marino 				tls_set_errorx(ctx,
182f5b1c8a1SJohn Marino 				    "Unexpected negative length for an "
183f5b1c8a1SJohn Marino 				    "IP address: %d", datalen);
18472c33676SMaxim Ag 				rv = -1;
185f5b1c8a1SJohn Marino 				break;
186f5b1c8a1SJohn Marino 			}
187f5b1c8a1SJohn Marino 
188f5b1c8a1SJohn Marino 			/*
189f5b1c8a1SJohn Marino 			 * Per RFC 5280 section 4.2.1.6:
190f5b1c8a1SJohn Marino 			 * IPv4 must use 4 octets and IPv6 must use 16 octets.
191f5b1c8a1SJohn Marino 			 */
192f5b1c8a1SJohn Marino 			if (datalen == addrlen &&
193f5b1c8a1SJohn Marino 			    memcmp(data, &addrbuf, addrlen) == 0) {
19472c33676SMaxim Ag 				*alt_match = 1;
195f5b1c8a1SJohn Marino 				break;
196f5b1c8a1SJohn Marino 			}
197f5b1c8a1SJohn Marino 		}
198f5b1c8a1SJohn Marino 	}
199f5b1c8a1SJohn Marino 
200f5b1c8a1SJohn Marino 	sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
201f5b1c8a1SJohn Marino 	return rv;
202f5b1c8a1SJohn Marino }
203f5b1c8a1SJohn Marino 
204f5b1c8a1SJohn Marino static int
tls_check_common_name(struct tls * ctx,X509 * cert,const char * name,int * cn_match)20572c33676SMaxim Ag tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
20672c33676SMaxim Ag     int *cn_match)
207f5b1c8a1SJohn Marino {
208f5b1c8a1SJohn Marino 	X509_NAME *subject_name;
209f5b1c8a1SJohn Marino 	char *common_name = NULL;
210f5b1c8a1SJohn Marino 	union tls_addr addrbuf;
211f5b1c8a1SJohn Marino 	int common_name_len;
21272c33676SMaxim Ag 	int rv = 0;
21372c33676SMaxim Ag 
21472c33676SMaxim Ag 	*cn_match = 0;
215f5b1c8a1SJohn Marino 
216f5b1c8a1SJohn Marino 	subject_name = X509_get_subject_name(cert);
217f5b1c8a1SJohn Marino 	if (subject_name == NULL)
21872c33676SMaxim Ag 		goto done;
219f5b1c8a1SJohn Marino 
220f5b1c8a1SJohn Marino 	common_name_len = X509_NAME_get_text_by_NID(subject_name,
221f5b1c8a1SJohn Marino 	    NID_commonName, NULL, 0);
222f5b1c8a1SJohn Marino 	if (common_name_len < 0)
22372c33676SMaxim Ag 		goto done;
224f5b1c8a1SJohn Marino 
225f5b1c8a1SJohn Marino 	common_name = calloc(common_name_len + 1, 1);
226f5b1c8a1SJohn Marino 	if (common_name == NULL)
22772c33676SMaxim Ag 		goto done;
228f5b1c8a1SJohn Marino 
229f5b1c8a1SJohn Marino 	X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
230f5b1c8a1SJohn Marino 	    common_name_len + 1);
231f5b1c8a1SJohn Marino 
232f5b1c8a1SJohn Marino 	/* NUL bytes in CN? */
23372c33676SMaxim Ag 	if (common_name_len < 0 ||
23472c33676SMaxim Ag 	    (size_t)common_name_len != strlen(common_name)) {
235f5b1c8a1SJohn Marino 		tls_set_errorx(ctx, "error verifying name '%s': "
236f5b1c8a1SJohn Marino 		    "NUL byte in Common Name field, "
237f5b1c8a1SJohn Marino 		    "probably a malicious certificate", name);
23872c33676SMaxim Ag 		rv = -1;
23972c33676SMaxim Ag 		goto done;
240f5b1c8a1SJohn Marino 	}
241f5b1c8a1SJohn Marino 
24272c33676SMaxim Ag 	/*
24372c33676SMaxim Ag 	 * We don't want to attempt wildcard matching against IP addresses,
24472c33676SMaxim Ag 	 * so perform a simple comparison here.
24572c33676SMaxim Ag 	 */
246f5b1c8a1SJohn Marino 	if (inet_pton(AF_INET,  name, &addrbuf) == 1 ||
247f5b1c8a1SJohn Marino 	    inet_pton(AF_INET6, name, &addrbuf) == 1) {
248f5b1c8a1SJohn Marino 		if (strcmp(common_name, name) == 0)
24972c33676SMaxim Ag 			*cn_match = 1;
25072c33676SMaxim Ag 		goto done;
251f5b1c8a1SJohn Marino 	}
252f5b1c8a1SJohn Marino 
253f5b1c8a1SJohn Marino 	if (tls_match_name(common_name, name) == 0)
25472c33676SMaxim Ag 		*cn_match = 1;
25572c33676SMaxim Ag 
25672c33676SMaxim Ag  done:
257f5b1c8a1SJohn Marino 	free(common_name);
258f5b1c8a1SJohn Marino 	return rv;
259f5b1c8a1SJohn Marino }
260f5b1c8a1SJohn Marino 
261f5b1c8a1SJohn Marino int
tls_check_name(struct tls * ctx,X509 * cert,const char * name,int * match)26272c33676SMaxim Ag tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match)
263f5b1c8a1SJohn Marino {
26472c33676SMaxim Ag 	int alt_exists;
265f5b1c8a1SJohn Marino 
26672c33676SMaxim Ag 	*match = 0;
267f5b1c8a1SJohn Marino 
26872c33676SMaxim Ag 	if (tls_check_subject_altname(ctx, cert, name, match,
26972c33676SMaxim Ag 	    &alt_exists) == -1)
27072c33676SMaxim Ag 		return -1;
27172c33676SMaxim Ag 
27272c33676SMaxim Ag 	/*
27372c33676SMaxim Ag 	 * As per RFC 6125 section 6.4.4, if any known alternate name existed
27472c33676SMaxim Ag 	 * in the certificate, we do not attempt to match on the CN.
27572c33676SMaxim Ag 	 */
27672c33676SMaxim Ag 	if (*match || alt_exists)
27772c33676SMaxim Ag 		return 0;
27872c33676SMaxim Ag 
27972c33676SMaxim Ag 	return tls_check_common_name(ctx, cert, name, match);
280f5b1c8a1SJohn Marino }
281