diff options
author | jsing <> | 2017-04-10 17:11:13 +0000 |
---|---|---|
committer | jsing <> | 2017-04-10 17:11:13 +0000 |
commit | 1fb5784eee903ab9b8621581b6128aaccf2d3120 (patch) | |
tree | 2ba4db6e1d15d0e16b83f40c86378539156871c3 /src | |
parent | a887f273016c6b1a211de9fd477d86b2b8c26792 (diff) | |
download | openbsd-1fb5784eee903ab9b8621581b6128aaccf2d3120.tar.gz openbsd-1fb5784eee903ab9b8621581b6128aaccf2d3120.tar.bz2 openbsd-1fb5784eee903ab9b8621581b6128aaccf2d3120.zip |
Rework name verification code so that a match is indicated via an argument,
rather than return codes. More strictly follow RFC 6125, in particular only
check the CN if there are no SAN identifiers present in the certificate
(per section 6.4.4).
Previous behaviour questioned by Daniel Stenberg <daniel at haxx dot se>.
ok beck@ jca@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libtls/tls_client.c | 14 | ||||
-rw-r--r-- | src/lib/libtls/tls_internal.h | 5 | ||||
-rw-r--r-- | src/lib/libtls/tls_peer.c | 9 | ||||
-rw-r--r-- | src/lib/libtls/tls_server.c | 11 | ||||
-rw-r--r-- | src/lib/libtls/tls_verify.c | 84 |
5 files changed, 76 insertions, 47 deletions
diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c index a1e2caa717..0e519684ef 100644 --- a/src/lib/libtls/tls_client.c +++ b/src/lib/libtls/tls_client.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_client.c,v 1.40 2017/01/26 12:56:37 jsing Exp $ */ | 1 | /* $OpenBSD: tls_client.c,v 1.41 2017/04/10 17:11:13 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -289,7 +289,7 @@ int | |||
289 | tls_handshake_client(struct tls *ctx) | 289 | tls_handshake_client(struct tls *ctx) |
290 | { | 290 | { |
291 | X509 *cert = NULL; | 291 | X509 *cert = NULL; |
292 | int ssl_ret; | 292 | int match, ssl_ret; |
293 | int rv = -1; | 293 | int rv = -1; |
294 | 294 | ||
295 | if ((ctx->flags & TLS_CLIENT) == 0) { | 295 | if ((ctx->flags & TLS_CLIENT) == 0) { |
@@ -311,11 +311,11 @@ tls_handshake_client(struct tls *ctx) | |||
311 | tls_set_errorx(ctx, "no server certificate"); | 311 | tls_set_errorx(ctx, "no server certificate"); |
312 | goto err; | 312 | goto err; |
313 | } | 313 | } |
314 | if ((rv = tls_check_name(ctx, cert, | 314 | if (tls_check_name(ctx, cert, ctx->servername, &match) == -1) |
315 | ctx->servername)) != 0) { | 315 | goto err; |
316 | if (rv != -2) | 316 | if (!match) { |
317 | tls_set_errorx(ctx, "name `%s' not present in" | 317 | tls_set_errorx(ctx, "name `%s' not present in" |
318 | " server certificate", ctx->servername); | 318 | " server certificate", ctx->servername); |
319 | goto err; | 319 | goto err; |
320 | } | 320 | } |
321 | } | 321 | } |
diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h index 7bbc14ca86..bd23249e57 100644 --- a/src/lib/libtls/tls_internal.h +++ b/src/lib/libtls/tls_internal.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_internal.h,v 1.56 2017/04/07 08:48:30 jsing Exp $ */ | 1 | /* $OpenBSD: tls_internal.h,v 1.57 2017/04/10 17:11:13 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> |
4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
@@ -186,7 +186,8 @@ void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx); | |||
186 | struct tls *tls_new(void); | 186 | struct tls *tls_new(void); |
187 | struct tls *tls_server_conn(struct tls *ctx); | 187 | struct tls *tls_server_conn(struct tls *ctx); |
188 | 188 | ||
189 | int tls_check_name(struct tls *ctx, X509 *cert, const char *servername); | 189 | int tls_check_name(struct tls *ctx, X509 *cert, const char *servername, |
190 | int *match); | ||
190 | int tls_configure_server(struct tls *ctx); | 191 | int tls_configure_server(struct tls *ctx); |
191 | 192 | ||
192 | int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx); | 193 | int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx); |
diff --git a/src/lib/libtls/tls_peer.c b/src/lib/libtls/tls_peer.c index 1a9065dfb1..ec97a30838 100644 --- a/src/lib/libtls/tls_peer.c +++ b/src/lib/libtls/tls_peer.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_peer.c,v 1.7 2017/04/05 03:19:22 beck Exp $ */ | 1 | /* $OpenBSD: tls_peer.c,v 1.8 2017/04/10 17:11:13 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> | 4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> |
@@ -55,10 +55,15 @@ tls_peer_cert_provided(struct tls *ctx) | |||
55 | int | 55 | int |
56 | tls_peer_cert_contains_name(struct tls *ctx, const char *name) | 56 | tls_peer_cert_contains_name(struct tls *ctx, const char *name) |
57 | { | 57 | { |
58 | int match; | ||
59 | |||
58 | if (ctx->ssl_peer_cert == NULL) | 60 | if (ctx->ssl_peer_cert == NULL) |
59 | return (0); | 61 | return (0); |
60 | 62 | ||
61 | return (tls_check_name(ctx, ctx->ssl_peer_cert, name) == 0); | 63 | if (tls_check_name(ctx, ctx->ssl_peer_cert, name, &match) == -1) |
64 | return (0); | ||
65 | |||
66 | return (match); | ||
62 | } | 67 | } |
63 | 68 | ||
64 | time_t | 69 | time_t |
diff --git a/src/lib/libtls/tls_server.c b/src/lib/libtls/tls_server.c index 51deff2510..39c6ca79e9 100644 --- a/src/lib/libtls/tls_server.c +++ b/src/lib/libtls/tls_server.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_server.c,v 1.35 2017/01/31 15:57:43 jsing Exp $ */ | 1 | /* $OpenBSD: tls_server.c,v 1.36 2017/04/10 17:11:13 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -75,11 +75,13 @@ tls_servername_cb(SSL *ssl, int *al, void *arg) | |||
75 | union tls_addr addrbuf; | 75 | union tls_addr addrbuf; |
76 | struct tls *conn_ctx; | 76 | struct tls *conn_ctx; |
77 | const char *name; | 77 | const char *name; |
78 | int match; | ||
78 | 79 | ||
79 | if ((conn_ctx = SSL_get_app_data(ssl)) == NULL) | 80 | if ((conn_ctx = SSL_get_app_data(ssl)) == NULL) |
80 | goto err; | 81 | goto err; |
81 | 82 | ||
82 | if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) == NULL) { | 83 | if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) == |
84 | NULL) { | ||
83 | /* | 85 | /* |
84 | * The servername callback gets called even when there is no | 86 | * The servername callback gets called even when there is no |
85 | * TLS servername extension provided by the client. Sigh! | 87 | * TLS servername extension provided by the client. Sigh! |
@@ -98,7 +100,10 @@ tls_servername_cb(SSL *ssl, int *al, void *arg) | |||
98 | 100 | ||
99 | /* Find appropriate SSL context for requested servername. */ | 101 | /* Find appropriate SSL context for requested servername. */ |
100 | for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) { | 102 | for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) { |
101 | if (tls_check_name(ctx, sni_ctx->ssl_cert, name) == 0) { | 103 | if (tls_check_name(ctx, sni_ctx->ssl_cert, name, |
104 | &match) == -1) | ||
105 | goto err; | ||
106 | if (match) { | ||
102 | SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx); | 107 | SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx); |
103 | return (SSL_TLSEXT_ERR_OK); | 108 | return (SSL_TLSEXT_ERR_OK); |
104 | } | 109 | } |
diff --git a/src/lib/libtls/tls_verify.c b/src/lib/libtls/tls_verify.c index 23e58ebef7..3bd1057d0c 100644 --- a/src/lib/libtls/tls_verify.c +++ b/src/lib/libtls/tls_verify.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_verify.c,v 1.18 2016/11/04 15:32:40 jsing Exp $ */ | 1 | /* $OpenBSD: tls_verify.c,v 1.19 2017/04/10 17:11:13 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> |
4 | * | 4 | * |
@@ -27,11 +27,6 @@ | |||
27 | #include <tls.h> | 27 | #include <tls.h> |
28 | #include "tls_internal.h" | 28 | #include "tls_internal.h" |
29 | 29 | ||
30 | static int tls_match_name(const char *cert_name, const char *name); | ||
31 | static int tls_check_subject_altname(struct tls *ctx, X509 *cert, | ||
32 | const char *name); | ||
33 | static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name); | ||
34 | |||
35 | static int | 30 | static int |
36 | tls_match_name(const char *cert_name, const char *name) | 31 | tls_match_name(const char *cert_name, const char *name) |
37 | { | 32 | { |
@@ -84,20 +79,28 @@ tls_match_name(const char *cert_name, const char *name) | |||
84 | return -1; | 79 | return -1; |
85 | } | 80 | } |
86 | 81 | ||
87 | /* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ | 82 | /* |
83 | * See RFC 5280 section 4.2.1.6 for SubjectAltName details. | ||
84 | * alt_match is set to 1 if a matching alternate name is found. | ||
85 | * alt_exists is set to 1 if any known alternate name exists in the certificate. | ||
86 | */ | ||
88 | static int | 87 | static int |
89 | tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | 88 | tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name, |
89 | int *alt_match, int *alt_exists) | ||
90 | { | 90 | { |
91 | STACK_OF(GENERAL_NAME) *altname_stack = NULL; | 91 | STACK_OF(GENERAL_NAME) *altname_stack = NULL; |
92 | union tls_addr addrbuf; | 92 | union tls_addr addrbuf; |
93 | int addrlen, type; | 93 | int addrlen, type; |
94 | int count, i; | 94 | int count, i; |
95 | int rv = -1; | 95 | int rv = 0; |
96 | |||
97 | *alt_match = 0; | ||
98 | *alt_exists = 0; | ||
96 | 99 | ||
97 | altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, | 100 | altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, |
98 | NULL, NULL); | 101 | NULL, NULL); |
99 | if (altname_stack == NULL) | 102 | if (altname_stack == NULL) |
100 | return -1; | 103 | return 0; |
101 | 104 | ||
102 | if (inet_pton(AF_INET, name, &addrbuf) == 1) { | 105 | if (inet_pton(AF_INET, name, &addrbuf) == 1) { |
103 | type = GEN_IPADD; | 106 | type = GEN_IPADD; |
@@ -115,6 +118,10 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
115 | GENERAL_NAME *altname; | 118 | GENERAL_NAME *altname; |
116 | 119 | ||
117 | altname = sk_GENERAL_NAME_value(altname_stack, i); | 120 | altname = sk_GENERAL_NAME_value(altname_stack, i); |
121 | |||
122 | if (altname->type == GEN_DNS || altname->type == GEN_IPADD) | ||
123 | *alt_exists = 1; | ||
124 | |||
118 | if (altname->type != type) | 125 | if (altname->type != type) |
119 | continue; | 126 | continue; |
120 | 127 | ||
@@ -133,7 +140,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
133 | "NUL byte in subjectAltName, " | 140 | "NUL byte in subjectAltName, " |
134 | "probably a malicious certificate", | 141 | "probably a malicious certificate", |
135 | name); | 142 | name); |
136 | rv = -2; | 143 | rv = -1; |
137 | break; | 144 | break; |
138 | } | 145 | } |
139 | 146 | ||
@@ -143,16 +150,16 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
143 | * dNSName must be rejected. | 150 | * dNSName must be rejected. |
144 | */ | 151 | */ |
145 | if (strcmp(data, " ") == 0) { | 152 | if (strcmp(data, " ") == 0) { |
146 | tls_set_error(ctx, | 153 | tls_set_errorx(ctx, |
147 | "error verifying name '%s': " | 154 | "error verifying name '%s': " |
148 | "a dNSName of \" \" must not be " | 155 | "a dNSName of \" \" must not be " |
149 | "used", name); | 156 | "used", name); |
150 | rv = -2; | 157 | rv = -1; |
151 | break; | 158 | break; |
152 | } | 159 | } |
153 | 160 | ||
154 | if (tls_match_name(data, name) == 0) { | 161 | if (tls_match_name(data, name) == 0) { |
155 | rv = 0; | 162 | *alt_match = 1; |
156 | break; | 163 | break; |
157 | } | 164 | } |
158 | } else { | 165 | } else { |
@@ -174,7 +181,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
174 | tls_set_errorx(ctx, | 181 | tls_set_errorx(ctx, |
175 | "Unexpected negative length for an " | 182 | "Unexpected negative length for an " |
176 | "IP address: %d", datalen); | 183 | "IP address: %d", datalen); |
177 | rv = -2; | 184 | rv = -1; |
178 | break; | 185 | break; |
179 | } | 186 | } |
180 | 187 | ||
@@ -184,7 +191,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
184 | */ | 191 | */ |
185 | if (datalen == addrlen && | 192 | if (datalen == addrlen && |
186 | memcmp(data, &addrbuf, addrlen) == 0) { | 193 | memcmp(data, &addrbuf, addrlen) == 0) { |
187 | rv = 0; | 194 | *alt_match = 1; |
188 | break; | 195 | break; |
189 | } | 196 | } |
190 | } | 197 | } |
@@ -195,13 +202,16 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) | |||
195 | } | 202 | } |
196 | 203 | ||
197 | static int | 204 | static int |
198 | tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) | 205 | tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, |
206 | int *cn_match) | ||
199 | { | 207 | { |
200 | X509_NAME *subject_name; | 208 | X509_NAME *subject_name; |
201 | char *common_name = NULL; | 209 | char *common_name = NULL; |
202 | union tls_addr addrbuf; | 210 | union tls_addr addrbuf; |
203 | int common_name_len; | 211 | int common_name_len; |
204 | int rv = -1; | 212 | int rv = 0; |
213 | |||
214 | *cn_match = 0; | ||
205 | 215 | ||
206 | subject_name = X509_get_subject_name(cert); | 216 | subject_name = X509_get_subject_name(cert); |
207 | if (subject_name == NULL) | 217 | if (subject_name == NULL) |
@@ -225,38 +235,46 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) | |||
225 | tls_set_errorx(ctx, "error verifying name '%s': " | 235 | tls_set_errorx(ctx, "error verifying name '%s': " |
226 | "NUL byte in Common Name field, " | 236 | "NUL byte in Common Name field, " |
227 | "probably a malicious certificate", name); | 237 | "probably a malicious certificate", name); |
228 | rv = -2; | 238 | rv = -1; |
229 | goto out; | 239 | goto out; |
230 | } | 240 | } |
231 | 241 | ||
242 | /* | ||
243 | * We don't want to attempt wildcard matching against IP addresses, | ||
244 | * so perform a simple comparison here. | ||
245 | */ | ||
232 | if (inet_pton(AF_INET, name, &addrbuf) == 1 || | 246 | if (inet_pton(AF_INET, name, &addrbuf) == 1 || |
233 | inet_pton(AF_INET6, name, &addrbuf) == 1) { | 247 | inet_pton(AF_INET6, name, &addrbuf) == 1) { |
234 | /* | ||
235 | * We don't want to attempt wildcard matching against IP | ||
236 | * addresses, so perform a simple comparison here. | ||
237 | */ | ||
238 | if (strcmp(common_name, name) == 0) | 248 | if (strcmp(common_name, name) == 0) |
239 | rv = 0; | 249 | *cn_match = 1; |
240 | else | ||
241 | rv = -1; | ||
242 | goto out; | 250 | goto out; |
243 | } | 251 | } |
244 | 252 | ||
245 | if (tls_match_name(common_name, name) == 0) | 253 | if (tls_match_name(common_name, name) == 0) |
246 | rv = 0; | 254 | *cn_match = 1; |
255 | |||
247 | out: | 256 | out: |
248 | free(common_name); | 257 | free(common_name); |
249 | return rv; | 258 | return rv; |
250 | } | 259 | } |
251 | 260 | ||
252 | int | 261 | int |
253 | tls_check_name(struct tls *ctx, X509 *cert, const char *name) | 262 | tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match) |
254 | { | 263 | { |
255 | int rv; | 264 | int alt_exists; |
256 | 265 | ||
257 | rv = tls_check_subject_altname(ctx, cert, name); | 266 | *match = 0; |
258 | if (rv == 0 || rv == -2) | 267 | |
259 | return rv; | 268 | if (tls_check_subject_altname(ctx, cert, name, match, |
269 | &alt_exists) == -1) | ||
270 | return -1; | ||
271 | |||
272 | /* | ||
273 | * As per RFC 6125 section 6.4.4, if any known alternate name existed | ||
274 | * in the certificate, we do not attempt to match on the CN. | ||
275 | */ | ||
276 | if (*match || alt_exists) | ||
277 | return 0; | ||
260 | 278 | ||
261 | return tls_check_common_name(ctx, cert, name); | 279 | return tls_check_common_name(ctx, cert, name, match); |
262 | } | 280 | } |