diff options
| author | beck <> | 2020-05-23 17:13:24 +0000 |
|---|---|---|
| committer | beck <> | 2020-05-23 17:13:24 +0000 |
| commit | 93f758622bc86732ccd44db068014cb9e42f9a52 (patch) | |
| tree | cc3cb487c96b80cc2e522f8bfd09a58f7d2f7ab2 /src | |
| parent | 3af5ecc52ab53ab12b55b8edb68f52096070099e (diff) | |
| download | openbsd-93f758622bc86732ccd44db068014cb9e42f9a52.tar.gz openbsd-93f758622bc86732ccd44db068014cb9e42f9a52.tar.bz2 openbsd-93f758622bc86732ccd44db068014cb9e42f9a52.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 | } |
