summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEKR <ekr@rtfm.com>2017-11-11 11:50:18 +0800
committerEKR <ekr@rtfm.com>2017-11-11 11:50:18 +0800
commit10d2584a2401c544ff7c689b37543eee6cedff6b (patch)
tree24e3b0b15893eaf215a3f796cda3dca6bc1731a6
parenteac086bba4225123cdaf24f3ff11b195e7250206 (diff)
downloadnss-hg-10d2584a2401c544ff7c689b37543eee6cedff6b.tar.gz
Bug 1423016 - DTLS support for tstclnt. r=mt
Reviewers: mt Differential Revision: https://phabricator.services.mozilla.com/D314
-rw-r--r--cmd/tstclnt/tstclnt.c283
-rwxr-xr-xtests/ssl/ssl.sh48
2 files changed, 263 insertions, 68 deletions
diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c
index 87229085a..1ad99502b 100644
--- a/cmd/tstclnt/tstclnt.c
+++ b/cmd/tstclnt/tstclnt.c
@@ -96,6 +96,7 @@ PRBool verbose;
int dumpServerChain = 0;
int renegotiationsToDo = 0;
int renegotiationsDone = 0;
+PRBool initializedServerSessionCache = PR_FALSE;
static char *progName;
@@ -179,7 +180,7 @@ PrintUsageHeader(const char *progName)
"[-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
"[-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
"[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]] [-I groups]\n"
- "[-A requestfile] [-L totalconnections]\n"
+ "[-A requestfile] [-L totalconnections] [-P {client,server}] [-Q]\n"
"\n",
progName);
}
@@ -203,7 +204,7 @@ PrintParameterUsage(void)
fprintf(stderr, "%-20s Print certificate chain information\n", "-C");
fprintf(stderr, "%-20s (use -C twice to print more certificate details)\n", "");
fprintf(stderr, "%-20s (use -C three times to include PEM format certificate dumps)\n", "");
- fprintf(stderr, "%-20s Nickname of key and cert for client auth\n",
+ fprintf(stderr, "%-20s Nickname of key and cert\n",
"-n nickname");
fprintf(stderr,
"%-20s Restricts the set of enabled SSL/TLS protocols versions.\n"
@@ -253,6 +254,8 @@ PrintParameterUsage(void)
"%-20s P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n",
"-I", "", "");
fprintf(stderr, "%-20s Enable alternative TLS 1.3 handshake\n", "-X alt-server-hello");
+ fprintf(stderr, "%-20s Use DTLS\n", "-P {client, server}");
+ fprintf(stderr, "%-20s Exit after handshake\n", "-Q");
}
static void
@@ -917,6 +920,11 @@ PRInt32 requestStringLen = 0;
PRBool requestSent = PR_FALSE;
PRBool enableZeroRtt = PR_FALSE;
PRBool enableAltServerHello = PR_FALSE;
+PRBool useDTLS = PR_FALSE;
+PRBool actAsServer = PR_FALSE;
+PRBool stopAfterHandshake = PR_FALSE;
+PRBool requestToExit = PR_FALSE;
+char *versionString = NULL;
static int
writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
@@ -999,12 +1007,129 @@ handshakeCallback(PRFileDesc *fd, void *client_data)
writeBytesToServer(fd, requestString, requestStringLen);
}
}
+ if (stopAfterHandshake) {
+ requestToExit = PR_TRUE;
+ }
}
#define REQUEST_WAITING (requestString && !requestSent)
+static SECStatus
+installServerCertificate(PRFileDesc *s, char *nickname)
+{
+ CERTCertificate *cert;
+ SECKEYPrivateKey *privKey = NULL;
+
+ if (!nickname) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ cert = PK11_FindCertFromNickname(nickname, &pwdata);
+ if (cert == NULL) {
+ return SECFailure;
+ }
+
+ privKey = PK11_FindKeyByAnyCert(cert, &pwdata);
+ if (privKey == NULL) {
+ return SECFailure;
+ }
+ if (SSL_ConfigServerCert(s, cert, privKey, NULL, 0) != SECSuccess) {
+ return SECFailure;
+ }
+ SECKEY_DestroyPrivateKey(privKey);
+ CERT_DestroyCertificate(cert);
+
+ return SECSuccess;
+}
+
+static SECStatus
+bindToClient(PRFileDesc *s)
+{
+ PRStatus status;
+ status = PR_Bind(s, &addr);
+ if (status != PR_SUCCESS) {
+ return SECFailure;
+ }
+
+ for (;;) {
+ /* Bind the remote address on first packet. This must happen
+ * before we SSL-ize the socket because we need to get the
+ * peer's address before SSLizing. Recvfrom gives us that
+ * while not consuming any data. */
+ unsigned char tmp;
+ PRNetAddr remote;
+ int nb;
+
+ nb = PR_RecvFrom(s, &tmp, 1, PR_MSG_PEEK,
+ &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (nb != 1)
+ continue;
+
+ status = PR_Connect(s, &remote, PR_INTERVAL_NO_TIMEOUT);
+ if (status != PR_SUCCESS) {
+ SECU_PrintError(progName, "server bind to remote end failed");
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ /* Unreachable. */
+}
+
+static SECStatus
+connectToServer(PRFileDesc *s, PRPollDesc *pollset)
+{
+ PRStatus status;
+ PRInt32 filesReady;
+
+ status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (status != PR_SUCCESS) {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ if (verbose)
+ SECU_PrintError(progName, "connect");
+ milliPause(50 * multiplier);
+ pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ pollset[SSOCK_FD].out_flags = 0;
+ pollset[SSOCK_FD].fd = s;
+ while (1) {
+ FPRINTF(stderr,
+ "%s: about to call PR_Poll for connect completion!\n",
+ progName);
+ filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+ if (filesReady < 0) {
+ SECU_PrintError(progName, "unable to connect (poll)");
+ return SECFailure;
+ }
+ FPRINTF(stderr,
+ "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
+ progName, pollset[SSOCK_FD].out_flags);
+ if (filesReady == 0) { /* shouldn't happen! */
+ SECU_PrintError(progName, "%s: PR_Poll returned zero!\n");
+ return SECFailure;
+ }
+ status = PR_GetConnectStatus(pollset);
+ if (status == PR_SUCCESS) {
+ break;
+ }
+ if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
+ SECU_PrintError(progName, "unable to connect (poll)");
+ return SECFailure;
+ }
+ SECU_PrintError(progName, "poll");
+ milliPause(50 * multiplier);
+ }
+ } else {
+ SECU_PrintError(progName, "unable to connect");
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
static int
-run_client(void)
+run(void)
{
int headerSeparatorPtrnId = 0;
int error = 0;
@@ -1020,13 +1145,23 @@ run_client(void)
requestSent = PR_FALSE;
/* Create socket */
- s = PR_OpenTCPSocket(addr.raw.family);
+ if (useDTLS) {
+ s = PR_OpenUDPSocket(addr.raw.family);
+ } else {
+ s = PR_OpenTCPSocket(addr.raw.family);
+ }
+
if (s == NULL) {
SECU_PrintError(progName, "error creating socket");
error = 1;
goto done;
}
+ if (actAsServer) {
+ if (bindToClient(s) != SECSuccess) {
+ return 1;
+ }
+ }
opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = PR_TRUE; /* default */
if (serverCertAuth.testFreshStatusFromSideChannel) {
@@ -1039,13 +1174,16 @@ run_client(void)
goto done;
}
- s = SSL_ImportFD(NULL, s);
+ if (useDTLS) {
+ s = DTLS_ImportFD(NULL, s);
+ } else {
+ s = SSL_ImportFD(NULL, s);
+ }
if (s == NULL) {
SECU_PrintError(progName, "error importing socket");
error = 1;
goto done;
}
-
SSL_SetPKCS11PinArg(s, &pwdata);
rv = SSL_OptionSet(s, SSL_SECURITY, 1);
@@ -1055,7 +1193,7 @@ run_client(void)
goto done;
}
- rv = SSL_OptionSet(s, SSL_HANDSHAKE_AS_CLIENT, 1);
+ rv = SSL_OptionSet(s, actAsServer ? SSL_HANDSHAKE_AS_SERVER : SSL_HANDSHAKE_AS_CLIENT, 1);
if (rv != SECSuccess) {
SECU_PrintError(progName, "error enabling client handshake");
error = 1;
@@ -1225,7 +1363,21 @@ run_client(void)
if (override) {
SSL_BadCertHook(s, ownBadCertHandler, NULL);
}
- SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+ if (actAsServer) {
+ rv = installServerCertificate(s, nickname);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error installing server cert");
+ return 1;
+ }
+ rv = SSL_ConfigServerSessionIDCache(1024, 0, 0, ".");
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "error configuring session cache");
+ return 1;
+ }
+ initializedServerSessionCache = PR_TRUE;
+ } else {
+ SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+ }
SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
if (hs1SniHostName) {
SSL_SetURL(s, hs1SniHostName);
@@ -1233,56 +1385,27 @@ run_client(void)
SSL_SetURL(s, host);
}
- /* Try to connect to the server */
- status = PR_Connect(s, &addr, PR_INTERVAL_NO_TIMEOUT);
- if (status != PR_SUCCESS) {
- if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
- if (verbose)
- SECU_PrintError(progName, "connect");
- milliPause(50 * multiplier);
- pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
- pollset[SSOCK_FD].out_flags = 0;
- pollset[SSOCK_FD].fd = s;
- while (1) {
- FPRINTF(stderr,
- "%s: about to call PR_Poll for connect completion!\n",
- progName);
- filesReady = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
- if (filesReady < 0) {
- SECU_PrintError(progName, "unable to connect (poll)");
- error = 1;
- goto done;
- }
- FPRINTF(stderr,
- "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
- progName, pollset[SSOCK_FD].out_flags);
- if (filesReady == 0) { /* shouldn't happen! */
- FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName);
- error = 1;
- goto done;
- }
- status = PR_GetConnectStatus(pollset);
- if (status == PR_SUCCESS) {
- break;
- }
- if (PR_GetError() != PR_IN_PROGRESS_ERROR) {
- SECU_PrintError(progName, "unable to connect (poll)");
- error = 1;
- goto done;
- }
- SECU_PrintError(progName, "poll");
- milliPause(50 * multiplier);
- }
- } else {
- SECU_PrintError(progName, "unable to connect");
+ if (actAsServer) {
+ rv = SSL_ResetHandshake(s, PR_TRUE /* server */);
+ if (rv != SECSuccess) {
+ return 1;
+ }
+ } else {
+ /* Try to connect to the server */
+ rv = connectToServer(s, pollset);
+ if (rv != SECSuccess) {
+ ;
error = 1;
goto done;
}
}
pollset[SSOCK_FD].fd = s;
- pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT |
- (clientSpeaksFirst ? 0 : PR_POLL_READ);
+ pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT;
+ if (!actAsServer)
+ pollset[SSOCK_FD].in_flags |= (clientSpeaksFirst ? 0 : PR_POLL_READ);
+ else
+ pollset[SSOCK_FD].in_flags |= PR_POLL_READ;
pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
if (!REQUEST_WAITING) {
pollset[STDIN_FD].in_flags = PR_POLL_READ;
@@ -1332,9 +1455,11 @@ run_client(void)
** Select on stdin and on the socket. Write data from stdin to
** socket, read data from socket and write to stdout.
*/
+ requestToExit = PR_FALSE;
FPRINTF(stderr, "%s: ready...\n", progName);
- while ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
- REQUEST_WAITING) {
+ while (!requestToExit &&
+ ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
+ REQUEST_WAITING)) {
char buf[4000]; /* buffer for stdin */
int nb; /* num bytes read from stdin. */
@@ -1520,12 +1645,10 @@ main(int argc, char **argv)
}
}
- SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
-
/* XXX: 'B' was used in the past but removed in 3.28,
* please leave some time before resuing it. */
optstate = PL_CreateOptState(argc, argv,
- "46A:CDFGHI:KL:M:OR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:z");
+ "46A:CDFGHI:KL:M:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:z");
while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
@@ -1606,6 +1729,21 @@ main(int argc, char **argv)
};
break;
+ case 'P':
+ useDTLS = PR_TRUE;
+ if (!strcmp(optstate->value, "server")) {
+ actAsServer = 1;
+ } else {
+ if (strcmp(optstate->value, "client")) {
+ Usage(progName);
+ }
+ }
+ break;
+
+ case 'Q':
+ stopAfterHandshake = PR_TRUE;
+ break;
+
case 'R':
rootModule = PORT_Strdup(optstate->value);
break;
@@ -1623,12 +1761,7 @@ main(int argc, char **argv)
break;
case 'V':
- if (SECU_ParseSSLVersionRangeString(optstate->value,
- enabledVersions, &enabledVersions) !=
- SECSuccess) {
- fprintf(stderr, "Bad version specified.\n");
- Usage(progName);
- }
+ versionString = PORT_Strdup(optstate->value);
break;
case 'X':
@@ -1747,9 +1880,20 @@ main(int argc, char **argv)
break;
}
}
-
PL_DestroyOptState(optstate);
+ SSL_VersionRangeGetSupported(useDTLS ? ssl_variant_datagram : ssl_variant_stream, &enabledVersions);
+
+ if (versionString) {
+ if (SECU_ParseSSLVersionRangeString(versionString,
+ enabledVersions, &enabledVersions) !=
+ SECSuccess) {
+ fprintf(stderr, "Bad version specified.\n");
+ Usage(progName);
+ }
+ PORT_Free(versionString);
+ }
+
if (optstatus == PL_OPT_BAD) {
Usage(progName);
}
@@ -1778,7 +1922,7 @@ main(int argc, char **argv)
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
PK11_SetPasswordFunc(SECU_GetModulePassword);
-
+ memset(&addr, 0, sizeof(addr));
status = PR_StringToNetAddr(host, &addr);
if (status == PR_SUCCESS) {
addr.inet.port = PR_htons(portno);
@@ -1790,6 +1934,7 @@ main(int argc, char **argv)
addrInfo = PR_GetAddrInfoByName(host, PR_AF_UNSPEC,
PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME);
if (!addrInfo) {
+ fprintf(stderr, "HOSTNAME=%s\n", host);
SECU_PrintError(progName, "error looking up host");
error = 1;
goto done;
@@ -1904,7 +2049,7 @@ main(int argc, char **argv)
}
while (numConnections--) {
- error = run_client();
+ error = run();
if (error) {
goto done;
}
@@ -1935,6 +2080,12 @@ done:
}
if (NSS_IsInitialized()) {
SSL_ClearSessionCache();
+ if (initializedServerSessionCache) {
+ if (SSL_ShutdownServerSessionIDCache() != SECSuccess) {
+ error = 1;
+ }
+ }
+
if (NSS_Shutdown() != SECSuccess) {
error = 1;
}
diff --git a/tests/ssl/ssl.sh b/tests/ssl/ssl.sh
index 580fe16e0..de867a4bd 100755
--- a/tests/ssl/ssl.sh
+++ b/tests/ssl/ssl.sh
@@ -64,9 +64,9 @@ ssl_init()
PORT=$(($PORT + $padd))
fi
NSS_SSL_TESTS=${NSS_SSL_TESTS:-normal_normal}
- nss_ssl_run="stapling signed_cert_timestamps cov auth stress"
+ nss_ssl_run="stapling signed_cert_timestamps cov auth stress dtls"
NSS_SSL_RUN=${NSS_SSL_RUN:-$nss_ssl_run}
-
+
# Test case files
SSLCOV=${QADIR}/ssl/sslcov.txt
SSLAUTH=${QADIR}/ssl/sslauth.txt
@@ -1097,6 +1097,47 @@ ssl_crl_cache()
html "</TABLE><BR>"
}
+############################ ssl_dtls ###################################
+# local shell function to test tstclnt acting as client and server for DTLS
+#########################################################################
+ssl_dtls()
+{
+ #verbose="-v"
+ html_head "SSL DTLS $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
+
+ testname="ssl_dtls"
+ value=0
+
+ if [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] ; then
+ echo "$SCRIPTNAME: skipping $testname (non-FIPS only)"
+ return 0
+ fi
+
+ echo "${testname}"
+
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_SERVERDIR} $verbose ${SERVER_OPTIONS} \\"
+ echo " -U -V tls1.1:tls1.2 -P server -Q < ${REQUEST_FILE} &"
+
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${SERVER_OPTIONS} \
+ -d ${P_R_SERVERDIR} $verbose -U -V tls1.1:tls1.2 -P server -n ${HOSTADDR} -w nss < ${REQUEST_FILE} 2>&1 &
+
+ PID=$!
+
+ sleep 1
+
+ echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
+ echo " -U -V tls1.1:tls1.2 -P client -Q < ${REQUEST_FILE}"
+ ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \
+ -d ${P_R_CLIENTDIR} $verbose -U -V tls1.1:tls1.2 -P client -Q < ${REQUEST_FILE} 2>&1
+ ret=$?
+ html_msg $ret $value "${testname}" \
+ "produced a returncode of $ret, expected is $value"
+
+ kill ${PID}
+
+ html "</TABLE><BR>"
+}
+
############################## ssl_cleanup #############################
# local shell function to finish this script (no exit since it might be
@@ -1134,6 +1175,9 @@ ssl_run()
"stress")
ssl_stress
;;
+ "dtls")
+ ssl_dtls
+ ;;
esac
done
}