diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libtls/tls_verify.c | 59 | ||||
-rw-r--r-- | src/regress/lib/libtls/verify/verifytest.c | 6 |
2 files changed, 51 insertions, 14 deletions
diff --git a/src/lib/libtls/tls_verify.c b/src/lib/libtls/tls_verify.c index 0cb86f686d..420e278c99 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.23 2023/05/11 07:35:27 tb Exp $ */ | 1 | /* $OpenBSD: tls_verify.c,v 1.24 2023/05/28 09:02:01 beck 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 | * |
@@ -205,10 +205,13 @@ static int | |||
205 | 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) | 206 | int *cn_match) |
207 | { | 207 | { |
208 | unsigned char *utf8_bytes = NULL; | ||
208 | X509_NAME *subject_name; | 209 | X509_NAME *subject_name; |
209 | char *common_name = NULL; | 210 | char *common_name = NULL; |
210 | union tls_addr addrbuf; | 211 | union tls_addr addrbuf; |
211 | int common_name_len; | 212 | int common_name_len; |
213 | ASN1_STRING *data; | ||
214 | int lastpos = -1; | ||
212 | int rv = -1; | 215 | int rv = -1; |
213 | 216 | ||
214 | *cn_match = 0; | 217 | *cn_match = 0; |
@@ -217,29 +220,60 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
217 | if (subject_name == NULL) | 220 | if (subject_name == NULL) |
218 | goto done; | 221 | goto done; |
219 | 222 | ||
220 | common_name_len = X509_NAME_get_text_by_NID(subject_name, | 223 | lastpos = X509_NAME_get_index_by_NID(subject_name, |
221 | NID_commonName, NULL, 0); | 224 | NID_commonName, lastpos); |
222 | if (common_name_len < 0) | 225 | if (lastpos == -1) |
223 | goto done; | 226 | goto done; |
227 | if (X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos) | ||
228 | != -1) { | ||
229 | /* | ||
230 | * Having multiple CN's is possible, and even happened back in | ||
231 | * the glory days of mullets and Hammer pants. In anything like | ||
232 | * a modern TLS cert, CN is as close to deprecated as it gets, | ||
233 | * and having more than one is bad. We therefore fail if we have | ||
234 | * more than one CN fed to us in the subject, treating the | ||
235 | * certificate as hostile. | ||
236 | */ | ||
237 | tls_set_errorx(ctx, "error verifying name '%s': " | ||
238 | "Certificate subject contains mutiple Common Name fields, " | ||
239 | "probably a malicious or malformed certificate", name); | ||
240 | goto err; | ||
241 | } | ||
224 | 242 | ||
225 | common_name = calloc(common_name_len + 1, 1); | 243 | data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, |
226 | if (common_name == NULL) { | 244 | lastpos)); |
227 | tls_set_error(ctx, "out of memory"); | 245 | /* |
246 | * Fail if we cannot encode as UTF-8, or if the UTF-8 encoding of the | ||
247 | * string contains a 0 byte. We treat any certificate with such data | ||
248 | * in the CN as hostile and fail. | ||
249 | */ | ||
250 | if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) { | ||
251 | tls_set_errorx(ctx, "error verifying name '%s': " | ||
252 | "Common Name field cannot be encoded as a UTF-8 string, " | ||
253 | "probably a malicious certificate", name); | ||
228 | goto err; | 254 | goto err; |
229 | } | 255 | } |
230 | 256 | ||
231 | X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name, | 257 | if (common_name_len < 1 || common_name_len > 64) { |
232 | common_name_len + 1); | 258 | tls_set_errorx(ctx, "error verifying name '%s': " |
259 | "Common Name field has invalid length, " | ||
260 | "probably a malicious certificate", name); | ||
261 | goto err; | ||
262 | } | ||
233 | 263 | ||
234 | /* NUL bytes in CN? */ | 264 | if (memchr(utf8_bytes, 0, common_name_len) != NULL) { |
235 | if (common_name_len < 0 || | ||
236 | (size_t)common_name_len != strlen(common_name)) { | ||
237 | tls_set_errorx(ctx, "error verifying name '%s': " | 265 | tls_set_errorx(ctx, "error verifying name '%s': " |
238 | "NUL byte in Common Name field, " | 266 | "NUL byte in Common Name field, " |
239 | "probably a malicious certificate", name); | 267 | "probably a malicious certificate", name); |
240 | goto err; | 268 | goto err; |
241 | } | 269 | } |
242 | 270 | ||
271 | common_name = strndup(utf8_bytes, common_name_len); | ||
272 | if (common_name == NULL) { | ||
273 | tls_set_error(ctx, "out of memory"); | ||
274 | goto err; | ||
275 | } | ||
276 | |||
243 | /* | 277 | /* |
244 | * We don't want to attempt wildcard matching against IP addresses, | 278 | * We don't want to attempt wildcard matching against IP addresses, |
245 | * so perform a simple comparison here. | 279 | * so perform a simple comparison here. |
@@ -258,6 +292,7 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, | |||
258 | rv = 0; | 292 | rv = 0; |
259 | 293 | ||
260 | err: | 294 | err: |
295 | free(utf8_bytes); | ||
261 | free(common_name); | 296 | free(common_name); |
262 | return rv; | 297 | return rv; |
263 | } | 298 | } |
diff --git a/src/regress/lib/libtls/verify/verifytest.c b/src/regress/lib/libtls/verify/verifytest.c index b41b62fcfb..57aa992149 100644 --- a/src/regress/lib/libtls/verify/verifytest.c +++ b/src/regress/lib/libtls/verify/verifytest.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: verifytest.c,v 1.7 2017/04/30 03:53:31 jsing Exp $ */ | 1 | /* $OpenBSD: verifytest.c,v 1.8 2023/05/28 09:02:01 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -40,6 +40,7 @@ struct verify_test { | |||
40 | const char name[128]; | 40 | const char name[128]; |
41 | int want_return; | 41 | int want_return; |
42 | int want_match; | 42 | int want_match; |
43 | int name_type; | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | struct verify_test verify_tests[] = { | 46 | struct verify_test verify_tests[] = { |
@@ -474,7 +475,8 @@ do_verify_test(int test_no, struct verify_test *vt) | |||
474 | if ((name = X509_NAME_new()) == NULL) | 475 | if ((name = X509_NAME_new()) == NULL) |
475 | errx(1, "failed to malloc X509_NAME"); | 476 | errx(1, "failed to malloc X509_NAME"); |
476 | if (X509_NAME_add_entry_by_NID(name, NID_commonName, | 477 | if (X509_NAME_add_entry_by_NID(name, NID_commonName, |
477 | MBSTRING_ASC, (unsigned char *)vt->common_name, | 478 | vt->name_type ? vt->name_type : MBSTRING_ASC, |
479 | (unsigned char *)vt->common_name, | ||
478 | vt->common_name_len, -1, 0) == 0) | 480 | vt->common_name_len, -1, 0) == 0) |
479 | errx(1, "failed to add name entry"); | 481 | errx(1, "failed to add name entry"); |
480 | if (X509_set_subject_name(cert, name) == 0) | 482 | if (X509_set_subject_name(cert, name) == 0) |