From ed72761843945ff7efc5a8ba3095a0f82e8f3e45 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 25 Jul 2016 21:34:57 +0200 Subject: [PATCH] wget: run s_client helper with -servername HOST This is necessary for multi-hosted TLSed web sites. function old new delta spawn_https_helper_openssl 334 441 +107 Based on a patch by Jeremy Chadwick Signed-off-by: Denys Vlasenko --- networking/wget.c | 49 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/networking/wget.c b/networking/wget.c index 37950ed39..653d8076f 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -62,9 +62,10 @@ //config: a helper program to talk over HTTPS. //config: //config: OpenSSL has a simple SSL client for debug purposes. -//config: If you select "openssl" helper, wget will effectively call -//config: "openssl s_client -quiet -connect IP:443 2>/dev/null" -//config: and pipe its data through it. +//config: If you select "openssl" helper, wget will effectively run: +//config: "openssl s_client -quiet -connect hostname:443 +//config: -servername hostname 2>/dev/null" and pipe its data +//config: through it. -servername is not used if hostname is numeric. //config: Note inconvenient API: host resolution is done twice, //config: and there is no guarantee openssl's idea of IPv6 address //config: format is the same as ours. @@ -349,6 +350,30 @@ static void set_alarm(void) # define clear_alarm() ((void)0) #endif +#if ENABLE_FEATURE_WGET_OPENSSL +/* + * is_ip_address() attempts to verify whether or not a string + * contains an IPv4 or IPv6 address (vs. an FQDN). The result + * of inet_pton() can be used to determine this. + * + * TODO add proper error checking when inet_pton() returns -1 + * (some form of system error has occurred, and errno is set) + */ +static int is_ip_address(const char *string) +{ + struct sockaddr_in sa; + + int result = inet_pton(AF_INET, string, &(sa.sin_addr)); +# if ENABLE_FEATURE_IPV6 + if (result == 0) { + struct sockaddr_in6 sa6; + result = inet_pton(AF_INET6, string, &(sa6.sin6_addr)); + } +# endif + return (result == 1); +} +#endif + static FILE *open_socket(len_and_sockaddr *lsa) { int fd; @@ -629,6 +654,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_ static int spawn_https_helper_openssl(const char *host, unsigned port) { char *allocated = NULL; + char *servername; int sp[2]; int pid; IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;) @@ -639,12 +665,14 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) if (!strchr(host, ':')) host = allocated = xasprintf("%s:%u", host, port); + servername = xstrdup(host); + strrchr(servername, ':')[0] = '\0'; fflush_all(); pid = xvfork(); if (pid == 0) { /* Child */ - char *argv[6]; + char *argv[8]; close(sp[0]); xmove_fd(sp[1], 0); @@ -656,12 +684,22 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) */ xmove_fd(2, 3); xopen("/dev/null", O_RDWR); + memset(&argv, 0, sizeof(argv)); argv[0] = (char*)"openssl"; argv[1] = (char*)"s_client"; argv[2] = (char*)"-quiet"; argv[3] = (char*)"-connect"; argv[4] = (char*)host; - argv[5] = NULL; + /* + * Per RFC 6066 Section 3, the only permitted values in the + * TLS server_name (SNI) field are FQDNs (DNS hostnames). + * IPv4 and IPv6 addresses, port numbers are not allowed. + */ + if (!is_ip_address(servername)) { + argv[5] = (char*)"-servername"; + argv[6] = (char*)servername; + } + BB_EXECVP(argv[0], argv); xmove_fd(3, 2); # if ENABLE_FEATURE_WGET_SSL_HELPER @@ -674,6 +712,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port) } /* Parent */ + free(servername); free(allocated); close(sp[1]); # if ENABLE_FEATURE_WGET_SSL_HELPER