Commit 1d0560a9 authored by Natanael Copa's avatar Natanael Copa

main/busybox: properly fix wget https support

fix busybox wget https support by using an external ssl_client helper
for https.

Disable the use of external openssl. This was fixed to check
certificates as a temporary solution. openssl can not produce any useful
error messages on certificate errors. It is big. So we simply disable
its use.

For dynamic busybox we disable the internal ssl_client and the internal
(broken) tls code, and build our own ssl_client which properly verifies
the certificates.

For the static busybox we enable the internal ssl_client and tls code,
but we only allow its use with --no-check-certificates. This is so we
still can fetch things from https in an emergency situation.

We auto-install ssl_client if both libssl and busybox are installed. This
is to keep backwards compatibility.
parent 782065cc
From 76026fa8c9a62423e359b992b16b6e0bbdd4e5a1 Mon Sep 17 00:00:00 2001
From: Jakub Jirutka <jakub@jirutka.cz>
Date: Thu, 24 May 2018 02:19:23 +0200
Subject: [PATCH] wget: print warning when internal TLS is used
From 948090c675f8b60b74c7357fcafb1cc8c179e0a6 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Mon, 28 May 2018 14:36:26 +0200
Subject: [PATCH] wget: emit a message that certificate verification is not
implemented
Internal TLS code (FEATURE_WGET_HTTPS) does not implement verification
of the server's certificate. It is documented in the code, but not
even mentioned in the --help message, so users typically don't know
about this behaviour. That's a crime against security!
function old new delta
spawn_ssl_client 185 209 +24
This patch changes this behaviour for the case when both
FEATURE_WGET_LONG_OPTIONS and FEATURE_WGET_HTTPS are enabled -
before initializing a TLS connection using the internal TLS code (i.e.
without certificate validation) warning message is printed, unless the
user specified option "--no-check-certificate".
See-Also: http://lists.busybox.net/pipermail/busybox/2018-May/086444.html
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
networking/wget.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
networking/wget.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/networking/wget.c b/networking/wget.c
index d1d85230c..cd92b3a28 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -135,18 +135,21 @@
//usage:#define wget_full_usage "\n\n"
@@ -136,6 +136,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: "\n --spider Only check URL existence: $? is 0 if exists"
+//usage: IF_FEATURE_WGET_HTTPS(
+//usage: "\n --no-check-certificate Don't validate the server's certificate"
+//usage: )
//usage: )
-//usage: "\n -c Continue retrieval of aborted transfer"
-//usage: "\n -q Quiet"
-//usage: "\n -P DIR Save to DIR (default .)"
-//usage: "\n -S Show server response"
+//usage: "\n -c Continue retrieval of aborted transfer"
+//usage: "\n -q Quiet"
+//usage: "\n -P DIR Save to DIR (default .)"
+//usage: "\n -S Show server response"
//usage: IF_FEATURE_WGET_TIMEOUT(
-//usage: "\n -T SEC Network read timeout is SEC seconds"
+//usage: "\n -T SEC Network read timeout is SEC seconds"
//usage: "\n --spider Only check URL existence: $? is 0 if exists"
+///////: "\n --no-check-certificate Don't validate the server's certificate"
//usage: )
-//usage: "\n -O FILE Save to FILE ('-' for stdout)"
-//usage: "\n -U STR Use STR for User-Agent header"
-//usage: "\n -Y on/off Use proxy"
+//usage: "\n -O FILE Save to FILE ('-' for stdout)"
+//usage: "\n -U STR Use STR for User-Agent header"
+//usage: "\n -Y on/off Use proxy"
#include "libbb.h"
@@ -267,6 +270,7 @@
//usage: "\n -c Continue retrieval of aborted transfer"
//usage: "\n -q Quiet"
@@ -267,6 +268,7 @@ enum {
WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
WGET_OPT_SPIDER = (1 << 12) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
......@@ -61,19 +32,17 @@ diff --git a/networking/wget.c b/networking/wget.c
};
enum {
@@ -765,6 +769,11 @@
@@ -765,6 +767,9 @@ static void spawn_ssl_client(const char *host, int network_fd)
int pid;
char *servername, *p;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT))
+ bb_error_msg("WARNING: SSL/TLS certificate is not being validated!");
+#endif
+ bb_error_msg("note: TLS certificate validation not implemented");
+
servername = xstrdup(host);
p = strrchr(servername, ':');
if (p) *p = '\0';
@@ -1353,10 +1362,9 @@
@@ -1353,10 +1358,9 @@ IF_DESKTOP( "tries\0" Required_argument "t")
"header\0" Required_argument "\xff"
"post-data\0" Required_argument "\xfe"
"spider\0" No_argument "\xfd"
......@@ -85,3 +54,14 @@ diff --git a/networking/wget.c b/networking/wget.c
/* Ignored (we don't support caching) */
IF_DESKTOP( "no-cache\0" No_argument "\xf0")
IF_DESKTOP( "no-verbose\0" No_argument "\xf0")
@@ -1416,6 +1420,7 @@ IF_DESKTOP( "no-parent\0" No_argument "\xf0")
if (option_mask32 & WGET_OPT_HEADER) bb_error_msg("--header");
if (option_mask32 & WGET_OPT_POST_DATA) bb_error_msg("--post-data");
if (option_mask32 & WGET_OPT_SPIDER) bb_error_msg("--spider");
+ if (option_mask32 & WGET_OPT_NO_CHECK_CERT) bb_error_msg("--no-check-certificate");
exit(0);
#endif
argv += optind;
--
2.17.0
From: Jakub Jirutka <jakub@jirutka.cz>
Date: Mon, 28 May 2018 00:04:00 +0200
Subject: [PATCH] wget: verify certificate when openssl helper is used
This patch is based on
http://lists.busybox.net/pipermail/busybox/2018-May/086458.html.
When TLS verification fails, e.g. due to invalid certificate, wget will print:
Connecting to example.org (...:443)
wget: error getting response: Connection reset by peer
wget executes openssl s_client as an external command and communicates
with it using stdin/stdout. Since s_client prints debug output to stderr
even when -quiet option is used, wget throws it to /dev/null. s_client
also does not disquish various error states using different exit codes,
so if openssl s_client exits prematurely, it cannot know why.
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -709,7 +709,12 @@
pid = xvfork();
if (pid == 0) {
/* Child */
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ char *argv[13];
+ int argc;
+#else
char *argv[8];
+#endif
close(sp[0]);
xmove_fd(sp[1], 0);
@@ -735,7 +740,26 @@
if (!is_ip_address(servername)) {
argv[5] = (char*)"-servername";
argv[6] = (char*)servername;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ argc = 7;
+ } else
+ argc = 5;
+
+ if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT)) {
+ argv[argc++] = (char*)"-verify";
+ argv[argc++] = (char*)"16";
+ argv[argc++] = (char*)"-verify_return_error";
+
+ if (is_ip_address(servername))
+ argv[argc++] = (char*)"-verify_ip";
+ else
+ argv[argc++] = (char*)"-verify_hostname";
+
+ argv[argc++] = (char*)servername;
}
+#else
+ }
+#endif
BB_EXECVP(argv[0], argv);
xmove_fd(3, 2);
@@ -1068,6 +1092,10 @@
int fd = spawn_https_helper_openssl(server.host, server.port);
# if ENABLE_FEATURE_WGET_HTTPS
if (fd < 0) { /* no openssl? try internal */
+# if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT))
+ bb_error_msg_and_die("unable to validate the server's certificate");
+# endif
sfp = open_socket(lsa);
spawn_ssl_client(server.host, fileno(sfp));
goto socket_opened;
......@@ -3,19 +3,19 @@
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=busybox
pkgver=1.28.3
pkgrel=1
pkgrel=2
pkgdesc="Size optimized toolbox of many common UNIX utilities"
url=http://busybox.net
arch="all"
license="GPL-2.0"
makedepends_build=""
makedepends_build="libressl-dev"
makedepends_host="linux-headers"
makedepends="$makedepends_build $makedepends_host"
checkdepends="zip"
provides="/bin/sh"
install="$pkgname.post-install $pkgname.post-upgrade
$pkgname-extras.post-install $pkgname-extras.pre-deinstall"
subpackages="$pkgname-static $pkgname-suid $pkgname-extras"
subpackages="$pkgname-static $pkgname-suid $pkgname-extras ssl_client"
options="suid !check"
triggers="busybox.trigger=/bin:/usr/bin:/sbin:/usr/sbin:/lib/modules/*"
source="http://busybox.net/downloads/$pkgname-$pkgver.tar.bz2
......@@ -42,8 +42,8 @@ source="http://busybox.net/downloads/$pkgname-$pkgver.tar.bz2
0001-nsenter-Rename-network-option-to-net.patch
0002-nsenter-fix-parsing-of-t-S-and-G-options.patch
0001-wget-print-warning-when-internal-tls-is-used.patch
0002-wget-verify-certificate-when-openssl-helper-is-used.patch
0001-wget-emit-a-message-that-certificate-verification-is.patch
external_ssl_client.patch
acpid.logrotate
busyboxconfig
......@@ -51,6 +51,7 @@ source="http://busybox.net/downloads/$pkgname-$pkgver.tar.bz2
bbsuid.c
dad.if-up
nologin.c
ssl_client.c
"
# secfixes:
......@@ -81,6 +82,10 @@ build() {
${CC:-${CROSS_COMPILE}gcc} ${CPPFLAGS} ${CFLAGS} \
${LDFLAGS} "$srcdir"/bbsuid.c -o "$_dyndir"/bbsuid
msg "Building ssl_client"
${CC:-${CROSS_COMPILE}gcc} ${CPPFLAGS} ${CFLAGS} $(pkg-config --cflags libtls) \
"$srcdir"/ssl_client.c -o "$_dyndir"/ssl_client ${LDFLAGS} $(pkg-config --libs libtls)
# build dynamic
cd "$_dyndir"
msg "Building dynamic busybox"
......@@ -104,8 +109,10 @@ build() {
# build static
cd "$_staticdir"
msg "Building static busybox"
# enable internal ssl_client for static build
sed -e "s/.*CONFIG_PIE.*/\# CONFIG_PIE is not set/" \
-e "s/.*CONFIG_STATIC.*/CONFIG_STATIC=y/" \
-e "s/.*CONFIG_SSL_CLIENT.*/CONFIG_SSL_CLIENT=y/" \
"$_config" > .config
# musl does not support GNU regex
[ "$CLIBC" = musl ] && sed -i \
......@@ -187,6 +194,16 @@ static() {
"$subpkgdir"/bin/busybox.static
}
ssl_client() {
pkgdesc="EXternal ssl_client for busybox wget"
local _sslver=$(pkg-config --modversion libssl)
# automatically pull in if both busybox and libssl is installed
install_if="$pkgname=$pkgver-r$pkgrel libressl${_sslver%.*}-libssl"
mkdir -p "$subpkgdir"/usr/bin
install -m755 "$_dyndir"/ssl_client \
"$subpkgdir"/usr/bin/ssl_client
}
sha512sums="0e87019d9d1179bc072a5ef9752d7342747e311db13c0b0dfa9f380a1e3ad1c265cca118d41f86829629ec5c8eb3e83985421c6f3ea6c8a9991b92ed301a4982 busybox-1.28.3.tar.bz2
51d4d58baff825a51d476bd4594cb8980ec2aa4d0c864a0eec39ccbbadd1ae9f1cd1b20f492a735ffcdf7c925573594f3c4363b0561c8aa7b91ef534bfc7b2e0 0001-ash-add-support-for-command_not_found_handle-hook-fu.patch
5d2fd3e521ee29d970f377363e3a3144eaf9f7714bc57494d743ded9e39c1ad93ea8759b2febd9c3786968b41e61b8d01ce2361aa997df177b644d63718470ba 0002-fsck-resolve-LABEL-.-UUID-.-spec-to-device.patch
......@@ -208,11 +225,12 @@ d90d6b3406760fe3df6dbed46a0f4d1c02a69d5184ebc86d8c1692bc4576532127283ba3ff9a81e6
0dbe3ee424c0a6e4aba4f551f6b6b9ee087655a03747a40906961b141d40b1cbb2345438f17887a1b78d880cb3a7ad0116936dd7c05e95160febfd299423e83b 0001-cat-fix-cat-e-and-cat-v-erroneously-numbering-1st-li.patch
d2364e20b12c5215c4baecc3c6faf903e6e1e2bee95d697af047d680e9d57e7aeea54c8584d062d92daa0ea64898b502fbae010b22ab236ec4018966b74deeec 0001-nsenter-Rename-network-option-to-net.patch
0dbffae82b62317fc4144a01940ebc601e58b0e14eb8338bc42db79407d0b74dbe9f0f44758b9a5baa399eb90f8e8ee8f9c344bebd1b03bdd2ce520cb2b28d5e 0002-nsenter-fix-parsing-of-t-S-and-G-options.patch
38973e70fc77450ba1bf4d2aa7db5425d57f18eab9ae5676d457294ade12ae6b44300ae41f100f452e2efa1d027612fa501c9ac0f95ce340519e1dce497e4971 0001-wget-print-warning-when-internal-tls-is-used.patch
2af27d1f6f1a0b028464a0f5abed79311d39d27f2ba99abe91fb15e24ed93d0df69edd8cfbf5c6444d10af1eb8b343ec8d5053010f385fe77a6cc71abb3cdcbd 0002-wget-verify-certificate-when-openssl-helper-is-used.patch
90f9e95f6f953df2cf579f701b3135bad910f514e94b3e23b953acec12b10f33aa9200169dc7b454c6a04fbd992a370e6ca835406f9b5495951e0a8416137000 0001-wget-emit-a-message-that-certificate-verification-is.patch
27bd37af65f48b52fe6329f5ddf86ce9afdd1c156f94c6e868d35434298ec96c3b436097ced57f403940a29a9721b56e09bee66da3ee2cfc49c0d90d2e7a2d3d external_ssl_client.patch
a9b1403c844c51934637215307dd9e2adb9458921047acff0d86dcf229b6e0027f4b2c6cdaa25a58407aad9d098fb5685d58eb5ff8d2aa3de4912cdea21fe54c acpid.logrotate
035f2a28719971d9ff805d208d70bc1144fd3701235dc46ef581a559e696ef92265f28f7debf0248a2cee004a773dcd07828bcc088716f5aff944ccdce15d30f busyboxconfig
d65dc165488a179ab19482ad74e350df9dfdccf2363b26424d2d145e27ab0819cd0cfdfb79b4a2bd0bd7c6eda3b95ea61f3c264357986e78c4675df94d487aec busyboxconfig
0efbe22e2fd56993d92b6542d4ccffb2b42d50495be085c98f417a71f503b4071e2f092afcec77f78064d33ffb0922c28daa3cb9958e6d7fb26d5a660abd90f4 busyboxconfig-extras
0becc2186d6c32fb0c401cf7bc0e46268b38ce8892db33be1daf40273024c1c02d518283f44086a313a2ccef34230a1d945ec148cc173f26e6aa9d88a7426e54 bbsuid.c
a1127c8a384294135e11500fde7ead33b73d24b11c21911b08447a4c4ef71d7a9965d6466f60f2da64e3b877213b0a3e924a5add3c5333ee3ecde8c2a91c5e02 dad.if-up
061f7417c1cbf0424a5fab77e2f5912aa1593f39b33ea294af4c03518ca712d793a77ea82ff1f36e9cb98751d9faacb9d0240cdf0894efd8f26c13c28a692404 nologin.c"
061f7417c1cbf0424a5fab77e2f5912aa1593f39b33ea294af4c03518ca712d793a77ea82ff1f36e9cb98751d9faacb9d0240cdf0894efd8f26c13c28a692404 nologin.c
d7e1409a7beba30bb8f30a04d2ef1aad6461c19d5ab3a09514e3698fe86c247c4cc10d4d94b85c1608e6401374964b705fa6982b3f7a2b2acc2d6f14ba91806d ssl_client.c"
......@@ -910,7 +910,7 @@ CONFIG_FEATURE_FANCY_PING=y
CONFIG_PSCAN=y
CONFIG_ROUTE=y
CONFIG_SLATTACH=y
CONFIG_SSL_CLIENT=y
# CONFIG_SSL_CLIENT is not set
# CONFIG_TCPSVD is not set
# CONFIG_UDPSVD is not set
# CONFIG_TELNET is not set
......@@ -927,7 +927,7 @@ CONFIG_SSL_CLIENT=y
# CONFIG_FEATURE_TFTP_PUT is not set
# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
# CONFIG_TFTP_DEBUG is not set
CONFIG_TLS=y
# CONFIG_TLS is not set
CONFIG_TRACEROUTE=y
CONFIG_TRACEROUTE6=y
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
......@@ -941,7 +941,7 @@ CONFIG_FEATURE_WGET_STATUSBAR=y
CONFIG_FEATURE_WGET_AUTHENTICATION=y
CONFIG_FEATURE_WGET_TIMEOUT=y
CONFIG_FEATURE_WGET_HTTPS=y
CONFIG_FEATURE_WGET_OPENSSL=y
# CONFIG_FEATURE_WGET_OPENSSL is not set
CONFIG_WHOIS=y
# CONFIG_ZCIP is not set
# CONFIG_UDHCPD is not set
......
diff --git a/networking/wget.c b/networking/wget.c
index cd92b3a28..a12c921cd 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -50,7 +50,6 @@
//config: bool "Support HTTPS using internal TLS code"
//config: default y
//config: depends on WGET
-//config: select TLS
//config: help
//config: wget will use internal TLS code to connect to https:// URLs.
//config: Note:
@@ -767,8 +766,8 @@ static void spawn_ssl_client(const char *host, int network_fd)
int pid;
char *servername, *p;
- if (!(option_mask32 & WGET_OPT_NO_CHECK_CERT))
- bb_error_msg("note: TLS certificate validation not implemented");
+ if (ENABLE_SSL_CLIENT && !(option_mask32 & WGET_OPT_NO_CHECK_CERT))
+ bb_error_msg_and_die("note: TLS certificate validation not implemented");
servername = xstrdup(host);
p = strrchr(servername, ':');
@@ -785,21 +784,25 @@ static void spawn_ssl_client(const char *host, int network_fd)
close(sp[0]);
xmove_fd(sp[1], 0);
xdup2(0, 1);
- if (BB_MMU) {
+ if (BB_MMU && ENABLE_TLS && (option_mask32 & WGET_OPT_NO_CHECK_CERT)) {
tls_state_t *tls = new_tls_state();
tls->ifd = tls->ofd = network_fd;
tls_handshake(tls, servername);
tls_run_copy_loop(tls);
exit(0);
} else {
- char *argv[5];
+ char *argv[6];
xmove_fd(network_fd, 3);
argv[0] = (char*)"ssl_client";
argv[1] = (char*)"-s3";
//TODO: if (!is_ip_address(servername))...
argv[2] = (char*)"-n";
argv[3] = servername;
- argv[4] = NULL;
+ if (!ENABLE_SSL_CLIENT &&(option_mask32 & WGET_OPT_NO_CHECK_CERT)) {
+ argv[4] = (char*)"-I";
+ argv[5] = NULL;
+ } else
+ argv[4] = NULL;
BB_EXECVP(argv[0], argv);
bb_perror_msg_and_die("can't execute '%s'", argv[0]);
}
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <tls.h>
#define BUFSIZE 16384
#define TLS_DEBUG 0
#if TLS_DEBUG
# define dbg(...) fprintf(stderr, __VA_ARGS__)
#else
# define dbg(...) ((void)0)
#endif
static void copy_from_stdin_to_tls(struct tls *ctx, int *fd)
{
static size_t buf[BUFSIZE];
ssize_t n;
int i = 0;
dbg("DEBUG: data from STDIN\n");
do {
n = read(STDIN_FILENO, buf, sizeof(buf));
dbg("read %zu\n", n);
} while (n < 0 && errno == EINTR);
if (n < 1) {
*fd = -1;
return;
}
while (n > 0) {
ssize_t r = tls_write(ctx, &buf[i], n);
if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
continue;
if (r < 0)
err(1, "tls_write: %s", tls_error(ctx));
i += r;
n -= r;
}
}
static int copy_from_tls_to_stdout(struct tls *ctx)
{
static size_t buf[BUFSIZE];
ssize_t n,r;
int i = 0;
dbg("DEBUG: data from TLS\n");
do {
n = tls_read(ctx, buf, sizeof(buf));
} while (n == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
if (n < 0)
err(1, "tls read: %s", tls_error(ctx));
if (n == 0)
return 1;
while (n) {
r = write(STDOUT_FILENO, &buf[i], n);
if (r < 0)
err(1, "write");
i += r;
n -= r;
}
return 0;
}
int do_poll(struct pollfd *fds, int nfds)
{
int r;
while ((r = poll(fds, nfds, -1)) < 0) {
if (errno != EINTR && errno != ENOMEM)
err(1, "poll");
}
return r;
}
static void copy_loop(struct tls *ctx, int sfd)
{
struct pollfd fds[2] = {
{ .fd = STDIN_FILENO, .events = POLLIN },
{ .fd = sfd, .events = POLLIN },
};
while (1) {
int r = do_poll(fds, 2);
if (fds[0].revents)
copy_from_stdin_to_tls(ctx, &fds[0].fd);
if (fds[1].revents && copy_from_tls_to_stdout(ctx))
break;
}
}
void usage(const char *prog, int ret) {
printf("usage: %s [-s FD] [-I] -n SNI\n", prog);
exit(ret);
}
int main(int argc, char *argv[])
{
int c, sfd = 1;;
const char *sni = NULL;
struct tls_config *tc;
struct tls *ctx;
int insecure = 0;
while ((c = getopt(argc, argv, "hs:n:I")) != -1) {
switch (c) {
case 'h':
usage(argv[0], 0);
break;
case 's':
sfd = atoi(optarg);
break;
case 'n':
sni = optarg;
break;
case 'I':
insecure = 1;
break;
case '?':
usage(argv[0], 1);
}
}
if (tls_init() == -1)
errx(1, "tls_init() failed");
if ((ctx = tls_client()) == NULL)
errx(1, "tls_client() failed");
if (insecure) {
if ((tc = tls_config_new()) == NULL)
errx(1, "tls_config_new() failed");
tls_config_insecure_noverifycert(tc);
tls_config_insecure_noverifyname(tc);
tls_config_insecure_noverifytime(tc);
if (tls_configure(ctx, tc) == -1)
err(1, "tls_configure: %s", tls_error(ctx));
tls_config_free(tc);
}
if (tls_connect_fds(ctx, sfd, sfd, sni) == -1)
errx(1, "%s: TLS connect failed", sni);
if (tls_handshake(ctx) == -1)
errx(1, "%s: %s", sni, tls_error(ctx));
copy_loop(ctx, sfd);
tls_close(ctx);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment