From 09f572b1e859bfb103f654ac86541710d964d9aa Mon Sep 17 00:00:00 2001
From: Cem Keylan <cem@ckyln.com>
Date: Thu, 18 Feb 2021 03:00:31 +0300
Subject: wget: use bearssl instead of openssl

---
 networking/wget.c | 117 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 63 insertions(+), 54 deletions(-)

(limited to 'networking')

diff --git a/networking/wget.c b/networking/wget.c
index e660c279c..575eb43da 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -91,36 +91,40 @@
 //config:	patches, but do want to waste bandwidth expaining how wrong
 //config:	it is, you will be ignored.
 //config:
-//config:	FEATURE_WGET_OPENSSL does implement TLS verification
-//config:	using the certificates available to OpenSSL.
+//config:	FEATURE_WGET_BEARSSL does implement TLS verification
+//config:	using the certificates available to BEARSSL.
 //config:
-//config:config FEATURE_WGET_OPENSSL
-//config:	bool "Try to connect to HTTPS using openssl"
+//config:config FEATURE_WGET_BEARSSL
+//config:	bool "Try to connect to HTTPS using brssl"
 //config:	default y
 //config:	depends on WGET
 //config:	help
-//config:	Try to use openssl to handle HTTPS.
+//config:	Try to use brssl to handle HTTPS.
 //config:
-//config:	OpenSSL has a simple SSL client for debug purposes.
+//config:	BearSSL has a simple SSL client for certificate verification.
 //config:	If you select this option, 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:	"bearssl client -q hostname:443 2>/dev/null" and pipe its data
+//config:	through it. -sni 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:	and there is no guarantee bearssl's idea of IPv6 address
 //config:	format is the same as ours.
-//config:	Another problem is that s_client prints debug information
-//config:	to stderr, and it needs to be suppressed. This means
-//config:	all error messages get suppressed too.
-//config:	openssl is also a big binary, often dynamically linked
-//config:	against ~15 libraries.
 //config:
-//config:	If openssl can't be executed, internal TLS code will be used
-//config:	(if you enabled it); if openssl can be executed but fails later,
+//config:	If bearssl can't be executed, internal TLS code will be used
+//config:	(if you enabled it); if bearssl can be executed but fails later,
 //config:	wget can't detect this, and download will fail.
 //config:
 //config:	By default TLS verification is performed, unless
 //config:	--no-check-certificate option is passed.
+//config:
+//config:config WGET_BEARSSL_CA_CERTIFICATE
+//config:    bool "Enable passing a CA certificate file to brssl"
+//config:    default n
+//config:    depends on FEATURE_WGET_BEARSSL
+//config:
+//config:config WGET_BEARSSL_CA_CERTIFICATE_FILE
+//config:    string "Absolute path to CA certificate file to pass to brssl"
+//config:    default ""
+//config:    depends on WGET_BEARSSL_CA_CERTIFICATE
 
 //applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
 
@@ -132,7 +136,7 @@
 /* Since we ignore these opts, we don't show them in --help */
 /* //usage:    "	[--no-cache] [--passive-ftp] [-t TRIES]" */
 /* //usage:    "	[-nv] [-nc] [-nH] [-np]" */
-//usage:       "	"IF_FEATURE_WGET_OPENSSL("[--no-check-certificate] ")"[-P DIR] [-U AGENT]"IF_FEATURE_WGET_TIMEOUT(" [-T SEC]")" URL..."
+//usage:       "	"IF_FEATURE_WGET_BEARSSL("[--no-check-certificate] ")"[-P DIR] [-U AGENT]"IF_FEATURE_WGET_TIMEOUT(" [-T SEC]")" URL..."
 //usage:	)
 //usage:	IF_NOT_FEATURE_WGET_LONG_OPTIONS(
 //usage:       "[-cqS] [-O FILE] [-o LOGFILE] [-Y on/off] [-P DIR] [-U AGENT]"IF_FEATURE_WGET_TIMEOUT(" [-T SEC]")" URL..."
@@ -141,7 +145,7 @@
 //usage:       "Retrieve files via HTTP or FTP\n"
 //usage:	IF_FEATURE_WGET_LONG_OPTIONS(
 //usage:     "\n	--spider	Only check URL existence: $? is 0 if exists"
-//usage:	IF_FEATURE_WGET_OPENSSL(
+//usage:	IF_FEATURE_WGET_BEARSSL(
 //usage:     "\n	--no-check-certificate	Don't validate the server's certificate"
 //usage:	)
 //usage:	)
@@ -172,7 +176,8 @@
 #endif
 
 
-#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_HTTPS)
+#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_BEARSSL || ENABLE_FEATURE_WGET_HTTPS)
+#define CA_FILE CONFIG_WGET_BEARSSL_CA_CERTIFICATE_FILE
 
 struct host_info {
 	char *allocated;
@@ -391,7 +396,7 @@ static void set_alarm(void)
 # define clear_alarm() ((void)0)
 #endif
 
-#if ENABLE_FEATURE_WGET_OPENSSL
+#if ENABLE_FEATURE_WGET_BEARSSL
 /*
  * is_ip_address() attempts to verify whether or not a string
  * contains an IPv4 or IPv6 address (vs. an FQDN).  The result
@@ -648,8 +653,8 @@ static void reset_beg_range_to_zero(void)
 	/* ftruncate(G.output_fd, 0); */
 }
 
-#if ENABLE_FEATURE_WGET_OPENSSL
-static int spawn_https_helper_openssl(const char *host, unsigned port)
+#if ENABLE_FEATURE_WGET_BEARSSL
+static int spawn_https_helper_bearssl(const char *host, unsigned port)
 {
 	char *allocated = NULL;
 	char *servername;
@@ -670,49 +675,39 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
 	pid = xvfork();
 	if (pid == 0) {
 		/* Child */
-		char *argv[13];
+		char *argv[9];
 		char **argp;
 
 		close(sp[0]);
 		xmove_fd(sp[1], 0);
 		xdup2(0, 1);
 		/*
-		 * openssl s_client -quiet -connect www.kernel.org:443 2>/dev/null
+		 * brssl client -q www.kernel.org:443 2>/dev/null
 		 * It prints some debug stuff on stderr, don't know how to suppress it.
 		 * Work around by dev-nulling stderr. We lose all error messages :(
 		 */
 		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[0] = (char*)"brssl";
+		argv[1] = (char*)"client";
+		argv[2] = (char*)"-igneof";
+		argv[3] = (char*)host;
 		/*
 		 * 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.
 		 */
-		argp = &argv[5];
+		argp = &argv[4];
 		if (!is_ip_address(servername)) {
-			*argp++ = (char*)"-servername"; //[5]
-			*argp++ = (char*)servername;    //[6]
-		}
-		if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT)) {
-			/* Abort on bad server certificate */
-			*argp++ = (char*)"-verify";              //[7]
-			*argp++ = (char*)"100";                  //[8]
-			*argp++ = (char*)"-verify_return_error"; //[9]
-			if (!is_ip_address(servername)) {
-				*argp++ = (char*)"-verify_hostname"; //[10]
-				*argp++ = (char*)servername;         //[11]
-			} else {
-				*argp++ = (char*)"-verify_ip"; //[10]
-				*argp++ = (char*)host;         //[11]
-			}
+			*argp++ = (char*)"-sni"; //[4]
+			*argp++ = (char*)servername; //[5]
 		}
-		//[12] (or earlier) is NULL terminator
+#if ENABLE_WGET_BEARSSL_CA_CERTIFICATE
+		*argp++ = (char*)"-CA";   //[6]
+		*argp++ = (char*)CA_FILE; //[7]
+#endif
+		//[8] (or earlier) is NULL terminator
 
 		BB_EXECVP(argv[0], argv);
 		xmove_fd(3, 2);
@@ -1158,22 +1153,36 @@ static void download_one_url(const char *url)
 		int status;
 
 		/* Open socket to http(s) server */
-#if ENABLE_FEATURE_WGET_OPENSSL
-		/* openssl (and maybe internal TLS) support is configured */
+#if ENABLE_FEATURE_WGET_BEARSSL
+		/* bearssl (and maybe internal TLS) support is configured */
 		if (server.protocol == P_HTTPS) {
-			/* openssl-based helper
+			int fd;
+			/* bearssl-based helper
 			 * Inconvenient API since we can't give it an open fd
 			 */
-			int fd = spawn_https_helper_openssl(server.host, server.port);
+			if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT)) {
+				fd = spawn_https_helper_bearssl(server.host, server.port);
 # if ENABLE_FEATURE_WGET_HTTPS
-			if (fd < 0) { /* no openssl? try internal */
+				if (fd < 0) { /* no bearssl? try internal */
+					sfp = open_socket(lsa);
+					spawn_ssl_client(server.host, fileno(sfp), /*flags*/ 0);
+					goto socket_opened;
+				}
+			} else {
+				/* BearSSL's client is really strict (as one would expect from
+				 * an SSL client implementation from an SSL library), thus we
+				 * are using the builtin ssl_client if the user doesn't want to
+				 * verify certificates.
+				 */
 				sfp = open_socket(lsa);
 				spawn_ssl_client(server.host, fileno(sfp), /*flags*/ 0);
 				goto socket_opened;
-			}
 # else
-			/* We don't check for exec("openssl") failure in this case */
+			/* We don't check for exec("bearssl") failure in this case */
+			} else {
+				bb_simple_error_msg_and_die("builtin ssl_client must be available for this feature.");
 # endif
+			}
 			sfp = fdopen(fd, "r+");
 			if (!sfp)
 				bb_die_memory_exhausted();
-- 
cgit v1.2.3