Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions manpages/wolfssl-s_client.1
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
.SH NAME
wolfssl-s_client, s_client \- basic TLS client for testing connections
.SH SYNOPSIS
wolfssl s_client -connect <host>:<port> [-starttls proto] [-CAfile file] [-verify_return_error] [-disable_stdin_check] [-noservername] [-help]
wolfssl s_client -connect <host>:<port> [-starttls proto] [-CAfile file] [-verify_return_error] [-verify_hostname name] [-verify_ip ip] [-disable_stdin_check] [-noservername] [-help]
.SH DESCRIPTION
Opens a TLS connection to a server for testing. Server Name Indication is
sent by default. Certificate verification is disabled by default and can be
enabled with -verify_return_error.
sent by default. Certificate verification is off by default. When enabled
it checks the certificate chain, not that the certificate was issued for
the host being connected to; that is asked for separately.
.SH OPTIONS
-connect host:port address and port to connect to. IPv6 addresses use
.br
Expand All @@ -20,14 +21,24 @@ enabled with -verify_return_error.
.LP
-CAfile file CA certificate file. Has no effect on whether the
.br
connection succeeds or fails unless
connection succeeds or fails unless certificate
.br
\-verify_return_error is also given (see NOTES).
verification is enabled (see NOTES).
.br
.LP
-verify_return_error close the connection on a verification error.
.br
.LP
-verify_hostname name check the peer certificate against the DNS name
.br
\fIname\fR.
.br
.LP
-verify_ip ip check the peer certificate against the IP address
.br
\fIip\fR.
.br
.LP
-disable_stdin_check do not wait for or read input on stdin; useful when
.br
scripting the client.
Expand All @@ -48,6 +59,16 @@ Connect and verify the server's certificate:
wolfssl s_client -connect example.com:443 -CAfile ca-cert.pem -verify_return_error
.RE
.LP
Connect and require the certificate to be issued for the host:
.RS
wolfssl s_client -connect example.com:443 -CAfile ca-cert.pem -verify_hostname example.com
.RE
.LP
Connect and require the certificate to carry the address dialed:
.RS
wolfssl s_client -connect 127.0.0.1:11111 -CAfile ca-cert.pem -verify_ip 127.0.0.1
.RE
.LP
Connect to an SMTP server using STARTTLS:
.RS
wolfssl s_client -connect mail.example.com:25 -starttls smtp
Expand All @@ -59,8 +80,9 @@ Available only when wolfSSL is built with filesystem support.
.LP
The client negotiates the highest TLS version both peers support. CRL
checking is not performed. The server certificate is only verified when
\-verify_return_error is given; without it the connection proceeds
unverified (with a warning) even if \-CAfile is supplied.
\-verify_return_error, \-verify_hostname or \-verify_ip is given; each of
them enables verification on its own. Without one of them the connection
proceeds unverified (with a warning) even if \-CAfile is supplied.
.SH BUGS
No known bugs at this time.
.SH AUTHOR
Expand Down
34 changes: 32 additions & 2 deletions src/client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,8 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
because can't tell if we're really
going there to detect old chacha-poly
*/
const char* checkDomain = NULL;
const char* checkIpAddr = NULL;
#ifndef WOLFSSL_VXWORKS
int ch;
static const struct mygetopt_long_config long_options[] = {
Expand All @@ -2194,6 +2196,8 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
{ "pqc", 1, 259 },
#endif
{ "disable_stdin_check", 0, 260 },
{ "verify_hostname", 1, 300 },
{ "verify_ip", 1, 301 },
{ 0, 0, 0 }
};
#endif
Expand All @@ -2214,6 +2218,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
int dtlsSCTP = 0;
int doMcast = 0;
int matchName = 0;
int matchIpAddr = 0;
int doPeerCheck = 1;
int nonBlocking = 0;
int simulateWantWrite = 0;
Expand Down Expand Up @@ -2404,6 +2409,16 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
disable_stdin_chk = 1;
break;

case 300 :
matchName = 1;
checkDomain = myoptarg;
break;

case 301 :
matchIpAddr = 1;
checkIpAddr = myoptarg;
break;

case 'g' :
sendGET = 1;
break;
Expand Down Expand Up @@ -3877,8 +3892,23 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
SetupAtomicUser(ctx, ssl);
#endif

if (matchName && doPeerCheck)
wolfSSL_check_domain_name(ssl, domain);
if (matchName && doPeerCheck) {
if (wolfSSL_check_domain_name(ssl,
checkDomain != NULL ? checkDomain : domain)
!= WOLFSSL_SUCCESS) {
wolfSSL_free(ssl); ssl = NULL;
wolfSSL_CTX_free(ctx); ctx = NULL;
err_sys("can't set domain name to check");
}
}

if (matchIpAddr && doPeerCheck) {
if (wolfSSL_check_ip_address(ssl, checkIpAddr) != WOLFSSL_SUCCESS) {
wolfSSL_free(ssl); ssl = NULL;
wolfSSL_CTX_free(ctx); ctx = NULL;
err_sys("can't set IP address to check");
}
}
#ifndef WOLFSSL_CALLBACKS
if (nonBlocking) {
#ifdef WOLFSSL_DTLS
Expand Down
50 changes: 49 additions & 1 deletion src/client/clu_client_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ static const struct option client_options[] = {
{"-verify_return_error", no_argument, 0, WOLFCLU_VERIFY_RETURN_ERROR},
{"-disable_stdin_check", no_argument, 0, WOLFCLU_DISABLE_STDINCHK },
{"-noservername", no_argument, 0, WOLFCLU_NOSERVERNAME },
{"-verify_hostname", required_argument, 0, WOLFCLU_VERIFY_HOSTNAME },
{"-verify_ip", required_argument, 0, WOLFCLU_VERIFY_IP },
{"-help", no_argument, 0, WOLFCLU_HELP },
{"-h", no_argument, 0, WOLFCLU_HELP },

Expand All @@ -58,6 +60,11 @@ static void wolfCLU_ClientHelp(void)
WOLFCLU_LOG(WOLFCLU_L0, "\t-verify_return_error close connection on verification error");
WOLFCLU_LOG(WOLFCLU_L0, "\t-disable_stdin_check ");
WOLFCLU_LOG(WOLFCLU_L0, "\t-noservername do not send Server Name Indication");
WOLFCLU_LOG(WOLFCLU_L0, "\t-verify_hostname <name> check the peer certificate"
" against <name>");
WOLFCLU_LOG(WOLFCLU_L0, "\t-verify_ip <ip> check the peer certificate against"
" <ip>");
WOLFCLU_LOG(WOLFCLU_L0, "\t\teither one turns on peer verification");
}

static const char hostFlag[] = "-h";
Expand All @@ -69,6 +76,8 @@ static const char noClientCert[] = "-x";
static const char startTLSFlag[] = "-M";
static const char disableCRLFlag[] = "-C";
static const char sniFlag[] = "-S";
static const char verifyHostFlag[] = "--verify_hostname";
static const char verifyIpFlag[] = "--verify_ip";

int myoptind = 0;
char* myoptarg = NULL;
Expand Down Expand Up @@ -105,6 +114,8 @@ int wolfCLU_Client(int argc, char** argv)
int verify = 0;
int noservername = 0;
char* ipv6 = NULL;
char* verifyHost = NULL;
char* verifyIp = NULL;

int clientArgc = 0;
const char* clientArgv[MAX_CLIENT_ARGS];
Expand Down Expand Up @@ -236,6 +247,26 @@ int wolfCLU_Client(int argc, char** argv)
noservername = 1;
break;

case WOLFCLU_VERIFY_HOSTNAME:
if (optarg == NULL) {
wolfCLU_LogError("-verify_hostname requires a name");
ret = WOLFCLU_FATAL_ERROR;
}
else {
verifyHost = optarg;
}
break;

case WOLFCLU_VERIFY_IP:
if (optarg == NULL) {
wolfCLU_LogError("-verify_ip requires an address");
ret = WOLFCLU_FATAL_ERROR;
}
else {
verifyIp = optarg;
}
break;

case ARG_FOUND_TWICE:
wolfCLU_LogError("Found duplicate argument");
return WOLFCLU_FATAL_ERROR;
Expand Down Expand Up @@ -265,10 +296,27 @@ int wolfCLU_Client(int argc, char** argv)
}
}

if (ret == WOLFCLU_SUCCESS && verifyHost != NULL) {
verify = 1;
ret = _addClientArg(clientArgv, verifyHostFlag, &clientArgc);
if (ret == WOLFCLU_SUCCESS) {
ret = _addClientArg(clientArgv, verifyHost, &clientArgc);
}
}

if (ret == WOLFCLU_SUCCESS && verifyIp != NULL) {
verify = 1;
ret = _addClientArg(clientArgv, verifyIpFlag, &clientArgc);
if (ret == WOLFCLU_SUCCESS) {
ret = _addClientArg(clientArgv, verifyIp, &clientArgc);
}
}

if (ret == WOLFCLU_SUCCESS && !verify) {
ret = _addClientArg(clientArgv, noVerifyFlag, &clientArgc);

WOLFCLU_LOG(WOLFCLU_L0, "\nWarning: -verify_return_error not specified."
WOLFCLU_LOG(WOLFCLU_L0, "\nWarning: none of -verify_return_error,"
" -verify_hostname or -verify_ip specified."
" Defaulting to NOT verifying peer.");
}

Expand Down
32 changes: 32 additions & 0 deletions tests/client/client-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,38 @@ def test_client_help(self):
r = run_wolfssl("s_client", "-help")
self.assertEqual(r.returncode, 0, r.stderr)
self.assertIn("s_client" , r.stderr, "help menu was not printed")
self.assertIn("-verify_hostname", r.stderr,
"-verify_hostname missing from help menu")
self.assertIn("-verify_ip", r.stderr,
"-verify_ip missing from help menu")

def test_no_verify_option_warns(self):
"""With no verify option, s_client says it is not verifying.

The warning is the only signal that a connection went unchecked,
so its wording is worth pinning. No server is needed; it prints
before the connection is attempted.
"""
r = run_wolfssl("s_client", "-connect", "127.0.0.1:1")
self.assertIn("Defaulting to NOT verifying peer", r.stderr,
f"no-verification warning missing: {r.stderr}")

def test_verify_option_requires_value(self):
"""-verify_hostname and -verify_ip must fail when given no value.

wolfCLU_GetOpt leaves optarg NULL for a required_argument option in
the last position; falling back to an unverified connection would
hand the user the opposite of what they asked for.
"""
for flag in ("-verify_hostname", "-verify_ip"):
with self.subTest(flag=flag):
r = run_wolfssl("s_client", "-connect", "127.0.0.1:1", flag)
self.assertNotEqual(r.returncode, 0,
f"SECURITY FAILURE: {flag} with no value "
"did not fail")
self.assertIn("requires", r.stderr,
f"{flag} with no value failed, but not for "
f"the missing value: {r.stderr}")

class ShellInjectionTest(unittest.TestCase):
"""Regression tests for shell command injection via hostname.
Expand Down
Loading
Loading