diff options
author | beck <> | 2020-05-23 17:13:24 +0000 |
---|---|---|
committer | beck <> | 2020-05-23 17:13:24 +0000 |
commit | ce471c0da7f905a6a1c11b47e709a521f8a706af (patch) | |
tree | cc3cb487c96b80cc2e522f8bfd09a58f7d2f7ab2 /src | |
parent | 6aaa71524fb63f15a7b380ada15b019cfa250176 (diff) | |
download | openbsd-ce471c0da7f905a6a1c11b47e709a521f8a706af.tar.gz openbsd-ce471c0da7f905a6a1c11b47e709a521f8a706af.tar.bz2 openbsd-ce471c0da7f905a6a1c11b47e709a521f8a706af.zip |
Enforce that SNI hostnames be correct as per rfc 6066 and 5980.
Correct SNI alerts to differentiate between illegal parameter
and an unknown name.
ok tb@`
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libssl/ssl_tlsext.c | 94 | ||||
-rw-r--r-- | src/lib/libssl/ssl_tlsext.h | 3 | ||||
-rw-r--r-- | src/regress/lib/libssl/tlsext/tlsexttest.c | 80 |
3 files changed, 159 insertions, 18 deletions
diff --git a/src/lib/libssl/ssl_tlsext.c b/src/lib/libssl/ssl_tlsext.c index f5343c81ab..2184e65a2c 100644 --- a/src/lib/libssl/ssl_tlsext.c +++ b/src/lib/libssl/ssl_tlsext.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssl_tlsext.c,v 1.71 2020/05/23 08:47:19 tb Exp $ */ | 1 | /* $OpenBSD: ssl_tlsext.c,v 1.72 2020/05/23 17:13:24 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2016, 2017, 2019 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2016, 2017, 2019 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> | 4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> |
@@ -18,6 +18,7 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <openssl/ocsp.h> | 20 | #include <openssl/ocsp.h> |
21 | #include <ctype.h> | ||
21 | 22 | ||
22 | #include "ssl_locl.h" | 23 | #include "ssl_locl.h" |
23 | 24 | ||
@@ -672,6 +673,53 @@ tlsext_sni_client_build(SSL *s, CBB *cbb) | |||
672 | return 1; | 673 | return 1; |
673 | } | 674 | } |
674 | 675 | ||
676 | /* | ||
677 | * Does the CBS contain only of a hostname consisting of RFC 5890 | ||
678 | * compliant A-labels? (see RFC 6066 section 3). Not a complete check | ||
679 | * since we don't parse punycode to verify its validity but limits to | ||
680 | * correct structure and character set. | ||
681 | */ | ||
682 | int | ||
683 | tlsext_sni_is_valid_hostname(CBS *cbs) | ||
684 | { | ||
685 | uint8_t prev, c = 0; | ||
686 | int component = 0; | ||
687 | CBS hostname; | ||
688 | |||
689 | if (CBS_len(cbs) > TLSEXT_MAXLEN_host_name) | ||
690 | return 0; | ||
691 | |||
692 | CBS_dup(cbs, &hostname); | ||
693 | while(CBS_len(&hostname) > 0) { | ||
694 | prev = c; | ||
695 | if (!CBS_get_u8(&hostname, &c)) | ||
696 | return 0; | ||
697 | /* Everything has to be ASCII, with no NUL byte. */ | ||
698 | if (!isascii(c) || c == '\0') | ||
699 | return 0; | ||
700 | /* It must be alphanumeric, a '-', or a '.' */ | ||
701 | if (!(isalnum(c) || c == '-' || c == '.')) | ||
702 | return 0; | ||
703 | /* '-' and '.' must not start a component or be at the end. */ | ||
704 | if (component == 0 || CBS_len(&hostname) == 0) { | ||
705 | if (c == '-' || c == '.') | ||
706 | return 0; | ||
707 | } | ||
708 | if (c == '.') { | ||
709 | /* Components can not end with a dash. */ | ||
710 | if (prev == '-') | ||
711 | return 0; | ||
712 | /* Start new component */ | ||
713 | component = 0; | ||
714 | continue; | ||
715 | } | ||
716 | /* Components must be 63 chars or less. */ | ||
717 | if (++component > 63) | ||
718 | return 0; | ||
719 | } | ||
720 | return 1; | ||
721 | } | ||
722 | |||
675 | int | 723 | int |
676 | tlsext_sni_server_parse(SSL *s, CBS *cbs, int *alert) | 724 | tlsext_sni_server_parse(SSL *s, CBS *cbs, int *alert) |
677 | { | 725 | { |
@@ -681,52 +729,66 @@ tlsext_sni_server_parse(SSL *s, CBS *cbs, int *alert) | |||
681 | if (!CBS_get_u16_length_prefixed(cbs, &server_name_list)) | 729 | if (!CBS_get_u16_length_prefixed(cbs, &server_name_list)) |
682 | goto err; | 730 | goto err; |
683 | 731 | ||
684 | /* | ||
685 | * RFC 6066 section 3 forbids multiple host names with the same type. | ||
686 | * Additionally, only one type (host_name) is specified. | ||
687 | */ | ||
688 | if (!CBS_get_u8(&server_name_list, &name_type)) | 732 | if (!CBS_get_u8(&server_name_list, &name_type)) |
689 | goto err; | 733 | goto err; |
690 | if (name_type != TLSEXT_NAMETYPE_host_name) | 734 | /* |
735 | * RFC 6066 section 3, only one type (host_name) is specified. | ||
736 | * We do not tolerate unknown types, neither does BoringSSL. | ||
737 | * other implementations appear more tolerant. | ||
738 | */ | ||
739 | if (name_type != TLSEXT_NAMETYPE_host_name) { | ||
740 | *alert = SSL3_AD_ILLEGAL_PARAMETER; | ||
691 | goto err; | 741 | goto err; |
742 | } | ||
743 | |||
692 | 744 | ||
693 | if (!CBS_get_u16_length_prefixed(&server_name_list, &host_name)) | 745 | if (!CBS_get_u16_length_prefixed(&server_name_list, &host_name)) |
694 | goto err; | 746 | goto err; |
695 | if (CBS_len(&host_name) == 0 || | 747 | /* |
696 | CBS_len(&host_name) > TLSEXT_MAXLEN_host_name || | 748 | * RFC 6066 section 3 specifies a host name must be at least 1 byte |
697 | CBS_contains_zero_byte(&host_name)) { | 749 | * so 0 length is a decode error. |
698 | *alert = TLS1_AD_UNRECOGNIZED_NAME; | 750 | */ |
699 | return 0; | 751 | if (CBS_len(&host_name) == 0) |
752 | goto err; | ||
753 | |||
754 | if (!tlsext_sni_is_valid_hostname(&host_name)) { | ||
755 | *alert = SSL3_AD_ILLEGAL_PARAMETER; | ||
756 | goto err; | ||
700 | } | 757 | } |
701 | 758 | ||
702 | if (s->internal->hit || S3I(s)->hs_tls13.hrr) { | 759 | if (s->internal->hit || S3I(s)->hs_tls13.hrr) { |
703 | if (s->session->tlsext_hostname == NULL) { | 760 | if (s->session->tlsext_hostname == NULL) { |
704 | *alert = TLS1_AD_UNRECOGNIZED_NAME; | 761 | *alert = TLS1_AD_UNRECOGNIZED_NAME; |
705 | return 0; | 762 | goto err; |
706 | } | 763 | } |
707 | if (!CBS_mem_equal(&host_name, s->session->tlsext_hostname, | 764 | if (!CBS_mem_equal(&host_name, s->session->tlsext_hostname, |
708 | strlen(s->session->tlsext_hostname))) { | 765 | strlen(s->session->tlsext_hostname))) { |
709 | *alert = TLS1_AD_UNRECOGNIZED_NAME; | 766 | *alert = TLS1_AD_UNRECOGNIZED_NAME; |
710 | return 0; | 767 | goto err; |
711 | } | 768 | } |
712 | } else { | 769 | } else { |
713 | if (s->session->tlsext_hostname != NULL) | 770 | if (s->session->tlsext_hostname != NULL) |
714 | goto err; | 771 | goto err; |
715 | if (!CBS_strdup(&host_name, &s->session->tlsext_hostname)) { | 772 | if (!CBS_strdup(&host_name, &s->session->tlsext_hostname)) { |
716 | *alert = TLS1_AD_INTERNAL_ERROR; | 773 | *alert = TLS1_AD_INTERNAL_ERROR; |
717 | return 0; | 774 | goto err; |
718 | } | 775 | } |
719 | } | 776 | } |
720 | 777 | ||
721 | if (CBS_len(&server_name_list) != 0) | 778 | /* |
779 | * RFC 6066 section 3 forbids multiple host names with the same type, | ||
780 | * therefore we allow only one entry. | ||
781 | */ | ||
782 | if (CBS_len(&server_name_list) != 0) { | ||
783 | *alert = SSL3_AD_ILLEGAL_PARAMETER; | ||
722 | goto err; | 784 | goto err; |
785 | } | ||
723 | if (CBS_len(cbs) != 0) | 786 | if (CBS_len(cbs) != 0) |
724 | goto err; | 787 | goto err; |
725 | 788 | ||
726 | return 1; | 789 | return 1; |
727 | 790 | ||
728 | err: | 791 | err: |
729 | *alert = SSL_AD_DECODE_ERROR; | ||
730 | return 0; | 792 | return 0; |
731 | } | 793 | } |
732 | 794 | ||
diff --git a/src/lib/libssl/ssl_tlsext.h b/src/lib/libssl/ssl_tlsext.h index aa40f6b1a6..15e0257e63 100644 --- a/src/lib/libssl/ssl_tlsext.h +++ b/src/lib/libssl/ssl_tlsext.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssl_tlsext.h,v 1.22 2020/01/25 12:58:27 jsing Exp $ */ | 1 | /* $OpenBSD: ssl_tlsext.h,v 1.23 2020/05/23 17:13:24 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> | 4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> |
@@ -58,6 +58,7 @@ int tlsext_sni_client_parse(SSL *s, CBS *cbs, int *alert); | |||
58 | int tlsext_sni_server_needs(SSL *s); | 58 | int tlsext_sni_server_needs(SSL *s); |
59 | int tlsext_sni_server_build(SSL *s, CBB *cbb); | 59 | int tlsext_sni_server_build(SSL *s, CBB *cbb); |
60 | int tlsext_sni_server_parse(SSL *s, CBS *cbs, int *alert); | 60 | int tlsext_sni_server_parse(SSL *s, CBS *cbs, int *alert); |
61 | int tlsext_sni_is_valid_hostname(CBS *cbs); | ||
61 | 62 | ||
62 | int tlsext_supportedgroups_client_needs(SSL *s); | 63 | int tlsext_supportedgroups_client_needs(SSL *s); |
63 | int tlsext_supportedgroups_client_build(SSL *s, CBB *cbb); | 64 | int tlsext_supportedgroups_client_build(SSL *s, CBB *cbb); |
diff --git a/src/regress/lib/libssl/tlsext/tlsexttest.c b/src/regress/lib/libssl/tlsext/tlsexttest.c index 3d03c2c0d3..5cf39e63ae 100644 --- a/src/regress/lib/libssl/tlsext/tlsexttest.c +++ b/src/regress/lib/libssl/tlsext/tlsexttest.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tlsexttest.c,v 1.36 2020/05/11 18:20:01 jsing Exp $ */ | 1 | /* $OpenBSD: tlsexttest.c,v 1.37 2020/05/23 17:13:24 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2017 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2017 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> | 4 | * Copyright (c) 2017 Doug Hogan <doug@openbsd.org> |
@@ -3544,6 +3544,81 @@ done: | |||
3544 | return (failure); | 3544 | return (failure); |
3545 | } | 3545 | } |
3546 | 3546 | ||
3547 | unsigned char *valid_hostnames[] = { | ||
3548 | "openbsd.org", | ||
3549 | "op3nbsd.org", | ||
3550 | "org", | ||
3551 | "3openbsd.com", | ||
3552 | "3-0penb-d.c-m", | ||
3553 | "a", | ||
3554 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", | ||
3555 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3556 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3557 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3558 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
3559 | NULL, | ||
3560 | }; | ||
3561 | |||
3562 | static int | ||
3563 | test_tlsext_valid_hostnames(void) { | ||
3564 | int i, failure = 0; | ||
3565 | for (i = 0; valid_hostnames[i] != NULL; i++) { | ||
3566 | CBS cbs; | ||
3567 | CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i])); | ||
3568 | if (!tlsext_sni_is_valid_hostname(&cbs)) { | ||
3569 | fprintf(stderr, "FAIL: %s\n", valid_hostnames[i]); | ||
3570 | FAIL("Valid hostname rejected"); | ||
3571 | failure = 1; | ||
3572 | goto done; | ||
3573 | } | ||
3574 | } | ||
3575 | done: | ||
3576 | return failure; | ||
3577 | } | ||
3578 | |||
3579 | unsigned char *invalid_hostnames[] = { | ||
3580 | "openbsd.org.", | ||
3581 | "openbsd..org", | ||
3582 | "openbsd.org-", | ||
3583 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", | ||
3584 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3585 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3586 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | ||
3587 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", | ||
3588 | "-p3nbsd.org", | ||
3589 | "openbs-.org", | ||
3590 | "openbsd\n.org", | ||
3591 | "open_bsd.org", | ||
3592 | "open\178bsd.org", | ||
3593 | "open\255bsd.org", | ||
3594 | NULL, | ||
3595 | }; | ||
3596 | |||
3597 | static int | ||
3598 | test_tlsext_invalid_hostnames(void) { | ||
3599 | int i, failure = 0; | ||
3600 | CBS cbs; | ||
3601 | for (i = 0; invalid_hostnames[i] != NULL; i++) { | ||
3602 | CBS_init(&cbs, invalid_hostnames[i], | ||
3603 | strlen(invalid_hostnames[i])); | ||
3604 | if (tlsext_sni_is_valid_hostname(&cbs)) { | ||
3605 | fprintf(stderr, "%s\n", invalid_hostnames[i]); | ||
3606 | FAIL("Invalid hostname accepted"); | ||
3607 | failure = 1; | ||
3608 | goto done; | ||
3609 | } | ||
3610 | } | ||
3611 | CBS_init(&cbs, valid_hostnames[0], | ||
3612 | strlen(valid_hostnames[0]) + 1); | ||
3613 | if (tlsext_sni_is_valid_hostname(&cbs)) { | ||
3614 | FAIL("hostname with NUL byte accepted"); | ||
3615 | failure = 1; | ||
3616 | goto done; | ||
3617 | } | ||
3618 | done: | ||
3619 | return failure; | ||
3620 | } | ||
3621 | |||
3547 | 3622 | ||
3548 | int | 3623 | int |
3549 | main(int argc, char **argv) | 3624 | main(int argc, char **argv) |
@@ -3595,5 +3670,8 @@ main(int argc, char **argv) | |||
3595 | failed |= test_tlsext_clienthello_build(); | 3670 | failed |= test_tlsext_clienthello_build(); |
3596 | failed |= test_tlsext_serverhello_build(); | 3671 | failed |= test_tlsext_serverhello_build(); |
3597 | 3672 | ||
3673 | failed |= test_tlsext_valid_hostnames(); | ||
3674 | failed |= test_tlsext_invalid_hostnames(); | ||
3675 | |||
3598 | return (failed); | 3676 | return (failed); |
3599 | } | 3677 | } |