Bug 1532312, add -E option to selfserv/tstclnt to enable post-handshake auth, r=mt
authorDaiki Ueno <dueno@redhat.com>
Mon, 08 Apr 2019 17:30:27 +0200
changeset 15079 bb58098d38a521c6c8b42bddb2e78f45d16d70d7
parent 15078 eb03936b42bb51d1e96acc73ac25a3b2501090b9
child 15080 15905cd1cab9c8460b245f19134043b5217e0e8b
push id3321
push userdueno@redhat.com
push dateMon, 08 Apr 2019 15:32:54 +0000
reviewersmt
bugs1532312
Bug 1532312, add -E option to selfserv/tstclnt to enable post-handshake auth, r=mt Reviewers: mt Reviewed By: mt Bug #: 1532312 Differential Revision: https://phabricator.services.mozilla.com/D21936
cmd/selfserv/selfserv.c
cmd/tstclnt/tstclnt.c
tests/ssl/ssl.sh
tests/ssl/sslauth.txt
--- a/cmd/selfserv/selfserv.c
+++ b/cmd/selfserv/selfserv.c
@@ -228,17 +228,19 @@ PrintParameterUsage()
         "   P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n"
         "-J comma separated list of enabled signature schemes in preference order.\n"
         "   The following values are valid:\n"
         "     rsa_pkcs1_sha1, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512,\n"
         "     ecdsa_sha1, ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384,\n"
         "     ecdsa_secp521r1_sha512,\n"
         "     rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512,\n"
         "     rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512,\n"
-        "-Z enable 0-RTT (for TLS 1.3; also use -u)\n",
+        "-Z enable 0-RTT (for TLS 1.3; also use -u)\n"
+        "-E enable post-handshake authentication\n"
+        "   (for TLS 1.3; only has an effect with 3 or more -r options)\n",
         stderr);
 }
 
 static void
 Usage(const char *progName)
 {
     PrintUsageHeader(progName);
     PrintParameterUsage();
@@ -799,16 +801,17 @@ PRBool disableRollBack = PR_FALSE;
 PRBool NoReuse = PR_FALSE;
 PRBool hasSidCache = PR_FALSE;
 PRBool disableLocking = PR_FALSE;
 PRBool enableSessionTickets = PR_FALSE;
 PRBool failedToNegotiateName = PR_FALSE;
 PRBool enableExtendedMasterSecret = PR_FALSE;
 PRBool zeroRTT = PR_FALSE;
 PRBool enableALPN = PR_FALSE;
+PRBool enablePostHandshakeAuth = PR_FALSE;
 SSLNamedGroup *enabledGroups = NULL;
 unsigned int enabledGroupsCount = 0;
 const SSLSignatureScheme *enabledSigSchemes = NULL;
 unsigned int enabledSigSchemeCount = 0;
 
 static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX];
 static int virtServerNameIndex = 1;
 
@@ -1426,25 +1429,38 @@ handle_connection(PRFileDesc *tcp_sock, 
                         break;
                     }
                     rv = SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE,
                                        (requestCert == 4));
                     if (rv < 0) {
                         errWarn("second SSL_OptionSet SSL_REQUIRE_CERTIFICATE");
                         break;
                     }
-                    rv = SSL_ReHandshake(ssl_sock, PR_TRUE);
-                    if (rv != 0) {
-                        errWarn("SSL_ReHandshake");
-                        break;
-                    }
-                    rv = SSL_ForceHandshake(ssl_sock);
-                    if (rv < 0) {
-                        errWarn("SSL_ForceHandshake");
-                        break;
+                    if (enablePostHandshakeAuth) {
+                        rv = SSL_SendCertificateRequest(ssl_sock);
+                        if (rv != SECSuccess) {
+                            errWarn("SSL_SendCertificateRequest");
+                            break;
+                        }
+                        rv = SSL_ForceHandshake(ssl_sock);
+                        if (rv != SECSuccess) {
+                            errWarn("SSL_ForceHandshake");
+                            break;
+                        }
+                    } else {
+                        rv = SSL_ReHandshake(ssl_sock, PR_TRUE);
+                        if (rv != 0) {
+                            errWarn("SSL_ReHandshake");
+                            break;
+                        }
+                        rv = SSL_ForceHandshake(ssl_sock);
+                        if (rv < 0) {
+                            errWarn("SSL_ForceHandshake");
+                            break;
+                        }
                     }
                 }
             }
 
             numIOVs = 0;
 
             iovs[numIOVs].iov_base = (char *)outHeader;
             iovs[numIOVs].iov_len = (sizeof(outHeader)) - 1;
@@ -1943,16 +1959,26 @@ server_main(
             errExit("error configuring anti-replay ");
         }
         rv = SSL_OptionSet(model_sock, SSL_ENABLE_0RTT_DATA, PR_TRUE);
         if (rv != SECSuccess) {
             errExit("error enabling 0RTT ");
         }
     }
 
+    if (enablePostHandshakeAuth) {
+        if (enabledVersions.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+            errExit("You tried enabling post-handshake auth without enabling TLS 1.3!");
+        }
+        rv = SSL_OptionSet(model_sock, SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+        if (rv != SECSuccess) {
+            errExit("error enabling post-handshake auth");
+        }
+    }
+
     if (enableALPN) {
         PRUint8 alpnVal[] = { 0x08,
                               0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 };
         rv = SSL_OptionSet(model_sock, SSL_ENABLE_ALPN, PR_TRUE);
         if (rv != SECSuccess) {
             errExit("error enabling ALPN");
         }
 
@@ -2218,17 +2244,17 @@ main(int argc, char **argv)
     SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
 
     /* please keep this list of options in ASCII collating sequence.
     ** numbers, then capital letters, then lower case, alphabetical.
     ** XXX: 'B', 'E', 'q', and 'x' were used in the past but removed
     **      in 3.28, please leave some time before resuing those.
     **      'z' was removed in 3.39. */
     optstate = PL_CreateOptState(argc, argv,
-                                 "2:A:C:DGH:I:J:L:M:NP:QRS:T:U:V:W:YZa:bc:d:e:f:g:hi:jk:lmn:op:rst:uvw:y");
+                                 "2:A:C:DEGH:I:J:L:M:NP:QRS:T:U:V:W:YZa:bc:d:e:f:g:hi:jk:lmn:op:rst:uvw:y");
     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
         ++optionsFound;
         switch (optstate->option) {
             case '2':
                 fileName = optstate->value;
                 break;
 
             case 'A':
@@ -2238,16 +2264,21 @@ main(int argc, char **argv)
             case 'C':
                 if (optstate->value)
                     NumSidCacheEntries = PORT_Atoi(optstate->value);
                 break;
 
             case 'D':
                 noDelay = PR_TRUE;
                 break;
+
+            case 'E':
+                enablePostHandshakeAuth = PR_TRUE;
+                break;
+
             case 'H':
                 configureDHE = (PORT_Atoi(optstate->value) != 0);
                 break;
 
             case 'G':
                 enableExtendedMasterSecret = PR_TRUE;
                 break;
 
--- a/cmd/tstclnt/tstclnt.c
+++ b/cmd/tstclnt/tstclnt.c
@@ -216,17 +216,17 @@ printSecurityInfo(PRFileDesc *fd)
 }
 
 static void
 PrintUsageHeader()
 {
     fprintf(stderr,
             "Usage:  %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n"
             "  [-D | -d certdir] [-C] [-b | -R root-module] \n"
-            "  [-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
+            "  [-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z] [-E]\n"
             "  [-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
             "  [-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n"
             "  [-I groups] [-J signatureschemes]\n"
             "  [-A requestfile] [-L totalconnections] [-P {client,server}]\n"
             "  [-N encryptedSniKeys] [-Q]\n"
             "\n",
             progName);
 }
@@ -306,16 +306,19 @@ PrintParameterUsage()
                     "%-20s rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512,\n"
                     "%-20s rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512,\n"
                     "%-20s dsa_sha1, dsa_sha256, dsa_sha384, dsa_sha512\n",
             "-J", "", "", "", "", "", "", "");
     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");
     fprintf(stderr, "%-20s Encrypted SNI Keys\n", "-N");
+    fprintf(stderr, "%-20s Enable post-handshake authentication\n"
+                    "%-20s for TLS 1.3; need to specify -n\n",
+            "-E", "");
 }
 
 static void
 Usage()
 {
     PrintUsageHeader();
     PrintParameterUsage();
     exit(1);
@@ -984,16 +987,17 @@ unsigned int zeroRttLen = 0;
 PRBool enableAltServerHello = PR_FALSE;
 PRBool useDTLS = PR_FALSE;
 PRBool actAsServer = PR_FALSE;
 PRBool stopAfterHandshake = PR_FALSE;
 PRBool requestToExit = PR_FALSE;
 char *versionString = NULL;
 PRBool handshakeComplete = PR_FALSE;
 char *encryptedSNIKeys = NULL;
+PRBool enablePostHandshakeAuth = PR_FALSE;
 
 static int
 writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
 {
     SECStatus rv;
     const PRUint8 *bufp = buf;
     PRPollDesc pollDesc;
 
@@ -1405,16 +1409,25 @@ run()
     rv = SSL_OptionSet(s, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
                        enableSignedCertTimestamps);
     if (rv != SECSuccess) {
         SECU_PrintError(progName, "error enabling signed cert timestamps");
         error = 1;
         goto done;
     }
 
+    if (enablePostHandshakeAuth) {
+        rv = SSL_OptionSet(s, SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE);
+        if (rv != SECSuccess) {
+            SECU_PrintError(progName, "error enabling post-handshake auth");
+            error = 1;
+            goto done;
+        }
+    }
+
     if (enabledGroups) {
         rv = SSL_NamedGroupConfig(s, enabledGroups, enabledGroupsCount);
         if (rv < 0) {
             SECU_PrintError(progName, "SSL_NamedGroupConfig failed");
             error = 1;
             goto done;
         }
     }
@@ -1702,17 +1715,17 @@ main(int argc, char **argv)
         }
     }
 
     /* Note: 'B' was used in the past but removed in 3.28
      *       'z' was removed in 3.39
      * Please leave some time before reusing these.
      */
     optstate = PL_CreateOptState(argc, argv,
-                                 "46A:CDFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
+                                 "46A:CDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
         switch (optstate->option) {
             case '?':
             default:
                 Usage();
                 break;
 
             case '4':
@@ -1733,16 +1746,20 @@ main(int argc, char **argv)
             case 'C':
                 ++dumpServerChain;
                 break;
 
             case 'D':
                 openDB = PR_FALSE;
                 break;
 
+            case 'E':
+                enablePostHandshakeAuth = PR_TRUE;
+                break;
+
             case 'F':
                 if (serverCertAuth.testFreshStatusFromSideChannel) {
                     /* parameter given twice or more */
                     serverCertAuth.requireDataForIntermediates = PR_TRUE;
                 }
                 serverCertAuth.testFreshStatusFromSideChannel = PR_TRUE;
                 break;
 
@@ -1983,16 +2000,21 @@ main(int argc, char **argv)
         exit(1);
     }
 
     if (rootModule && loadDefaultRootCAs) {
         fprintf(stderr, "%s: Cannot combine parameters -b and -R\n", progName);
         exit(1);
     }
 
+    if (enablePostHandshakeAuth && !nickname) {
+        fprintf(stderr, "%s: -E requires the use of -n\n", progName);
+        exit(1);
+    }
+
     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);
     } else {
--- a/tests/ssl/ssl.sh
+++ b/tests/ssl/ssl.sh
@@ -215,28 +215,30 @@ start_selfserv()
   else
       ECC_OPTIONS=""
   fi
   if [ -z "$RSA_PSS_CERT" -o "$RSA_PSS_CERT" != "1" ] ; then
       RSA_OPTIONS="-n ${HOSTADDR}"
   else
       RSA_OPTIONS="-n ${HOSTADDR}-rsa-pss"
   fi
+  SERVER_VMIN=${SERVER_VMIN-ssl3}
+  SERVER_VMAX=${SERVER_VMAX-tls1.2}
   echo "selfserv starting at `date`"
   echo "selfserv -D -p ${PORT} -d ${P_R_SERVERDIR} ${RSA_OPTIONS} ${SERVER_OPTIONS} \\"
   echo "         ${ECC_OPTIONS} -S ${HOSTADDR}-dsa -w nss "$@" -i ${R_SERVERPID}\\"
-  echo "         -V ssl3:tls1.2 $verbose -H 1 &"
+  echo "         -V ${SERVER_VMIN}:${SERVER_VMAX} $verbose -H 1 &"
   if [ ${fileout} -eq 1 ]; then
       ${PROFTOOL} ${BINDIR}/selfserv -D -p ${PORT} -d ${P_R_SERVERDIR} ${RSA_OPTIONS} ${SERVER_OPTIONS} \
-               ${ECC_OPTIONS} -S ${HOSTADDR}-dsa -w nss "$@" -i ${R_SERVERPID} -V ssl3:tls1.2 $verbose -H 1 \
+               ${ECC_OPTIONS} -S ${HOSTADDR}-dsa -w nss "$@" -i ${R_SERVERPID} -V ${SERVER_VMIN}:${SERVER_VMAX} $verbose -H 1 \
                > ${SERVEROUTFILE} 2>&1 &
       RET=$?
   else
       ${PROFTOOL} ${BINDIR}/selfserv -D -p ${PORT} -d ${P_R_SERVERDIR} ${RSA_OPTIONS} ${SERVER_OPTIONS} \
-               ${ECC_OPTIONS} -S ${HOSTADDR}-dsa -w nss "$@" -i ${R_SERVERPID} -V ssl3:tls1.2 $verbose -H 1 &
+               ${ECC_OPTIONS} -S ${HOSTADDR}-dsa -w nss "$@" -i ${R_SERVERPID} -V ${SERVER_VMIN}:${SERVER_VMAX} $verbose -H 1 &
       RET=$?
   fi
 
   # The PID $! returned by the MKS or Cygwin shell is not the PID of
   # the real background process, but rather the PID of a helper
   # process (sh.exe).  MKS's kill command has a bug: invoking kill
   # on the helper process does not terminate the real background
   # process.  Our workaround has been to have selfserv save its PID
@@ -383,27 +385,36 @@ ssl_auth()
   #verbose="-v"
   html_head "SSL Client Authentication $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE"
 
   ignore_blank_lines ${SSLAUTH} | \
   while read ectype value sparam cparam testname
   do
       echo "${testname}" | grep "don't require client auth" > /dev/null
       CAUTH=$?
+      echo "${testname}" | grep "TLS 1.3" > /dev/null
+      TLS13=$?
 
       if [ "${CLIENT_MODE}" = "fips" -a "${CAUTH}" -eq 0 ] ; then
           echo "$SCRIPTNAME: skipping  $testname (non-FIPS only)"
       elif [ "$ectype" = "SNI" -a "$NORM_EXT" = "Extended Test" ] ; then
           echo "$SCRIPTNAME: skipping  $testname for $NORM_EXT"
       else
           cparam=`echo $cparam | sed -e 's;_; ;g' -e "s/TestUser/$USER_NICKNAME/g" `
           if [ "$ectype" = "SNI" ]; then
               cparam=`echo $cparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" `
               sparam=`echo $sparam | sed -e "s/Host/$HOST/g" -e "s/Dom/$DOMSUF/g" `
           fi
+	  # SSL3 cannot be used with TLS 1.3
+	  unset SERVER_VMIN
+	  unset SERVER_VMAX
+	  if [ $TLS13 -eq 0 ] ; then
+	      SERVER_VMIN=tls1.0
+	      SERVER_VMAX=tls1.3
+	  fi
           start_selfserv `echo "$sparam" | sed -e 's,_, ,g'`
 
           echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\"
           echo "        ${cparam}  < ${REQUEST_FILE}"
           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
           ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${cparam} $verbose ${CLIENT_OPTIONS} \
                   -d ${P_R_CLIENTDIR} < ${REQUEST_FILE} \
                   >${TMP}/$HOST.tmp.$$  2>&1
@@ -664,19 +675,28 @@ ssl_crl_ssl()
   # Cert number $UNREVOKED_CERT_GRP_1 was not revoked
   CRL_GROUP_BEGIN=$CRL_GRP_1_BEGIN
   CRL_GROUP_RANGE=$CRL_GRP_1_RANGE
   UNREVOKED_CERT=$UNREVOKED_CERT_GRP_1
 
   ignore_blank_lines ${SSLAUTH} | \
   while read ectype value sparam cparam testname
   do
+    echo "${testname}" | grep "TLS 1.3" > /dev/null
+    TLS13=$?
     if [ "$ectype" = "SNI" ]; then
         continue
     else
+        # SSL3 cannot be used with TLS 1.3
+        unset SERVER_VMIN
+        unset SERVER_VMAX
+        if [ $TLS13 -eq 0 ] ; then
+            SERVER_VMIN=tls1.0
+            SERVER_VMAX=tls1.3
+        fi
         servarg=`echo $sparam | awk '{r=split($0,a,"-r") - 1;print r;}'`
         pwd=`echo $cparam | grep nss`
         user=`echo $cparam | grep TestUser`
         _cparam=$cparam
         case $servarg in
             1) if [ -z "$pwd" -o -z "$user" ]; then
                  rev_modvalue=0
                else
@@ -1034,17 +1054,17 @@ ssl_crl_cache()
 {
   #verbose="-v"
   html_head "Cache CRL SSL Client Tests $NORM_EXT"
   SSLAUTH_TMP=${TMP}/authin.tl.tmp
   SERV_ARG=-r_-r
   rm -f ${SSLAUTH_TMP}
   echo ${SSLAUTH_TMP}
 
-  grep -- " $SERV_ARG " ${SSLAUTH} | grep -v "^#" | grep -v none | grep -v bogus > ${SSLAUTH_TMP}
+  grep -- " $SERV_ARG " ${SSLAUTH} | grep -v "^#" | grep -v none | grep -v bogus | grep -v 'post hs' > ${SSLAUTH_TMP}
   echo $?
   while [ $? -eq 0 -a -f ${SSLAUTH_TMP} ]
     do
     start_selfserv `echo $SERV_ARG | sed -e 's,_, ,g'`
     exec < ${SSLAUTH_TMP}
     while read ectype value sparam cparam testname
       do
       [ "$ectype" = "" ] && continue
--- a/tests/ssl/sslauth.txt
+++ b/tests/ssl/sslauth.txt
@@ -33,16 +33,20 @@
   noECC     1       -r_-r_-r_-r  -V_ssl3:tls1.0_-w_bogus_-n_TestUser  TLS 1.0 Require client auth on 2nd hs (bad password)
   noECC     0       -r_-r_-r_-r  -V_ssl3:tls1.0_-w_nss_-n_TestUser    TLS 1.0 Require client auth on 2nd hs (client auth)
   noECC     0       -r_-r_-r     -V_ssl3:ssl3_-w_nss_-n_none     SSL3 Request don't require client auth on 2nd hs (client does not provide auth)
   noECC     0       -r_-r_-r     -V_ssl3:ssl3_-n_TestUser_-w_bogus SSL3 Request don't require client auth on 2nd hs (bad password)
   noECC     0       -r_-r_-r     -V_ssl3:ssl3_-n_TestUser_-w_nss SSL3 Request don't require client auth on 2nd hs (client auth)
   noECC     1       -r_-r_-r_-r  -V_ssl3:ssl3_-w_nss_-n_none     SSL3 Require client auth on 2nd hs (client does not provide auth)
   noECC     1       -r_-r_-r_-r  -V_ssl3:ssl3_-n_TestUser_-w_bogus SSL3 Require client auth on 2nd hs (bad password)
   noECC     0       -r_-r_-r_-r  -V_ssl3:ssl3_-n_TestUser_-w_nss SSL3 Require client auth on 2nd hs (client auth)
+  noECC     0       -r_-r_-r_-E  -V_tls1.3:tls1.3_-E_-n_TestUser_-w_nss TLS 1.3 Request don't require client auth on post hs (client auth)
+  noECC     0       -r_-r_-r_-r_-E  -V_tls1.3:tls1.3_-E_-n_TestUser_-w_nss TLS 1.3 Require client auth on post hs (client auth)
+  noECC     0       -r_-r_-r_-E  -V_tls1.3:tls1.3_-E_-n_none_-w_nss TLS 1.3 Request don't require client auth on post hs (client does not provide auth)
+  noECC     1       -r_-r_-r_-r_-E  -V_tls1.3:tls1.3_-E_-n_none_-w_nss TLS 1.3 Require client auth on post hs (client does not provide auth)
 #
 # Use EC cert for client authentication
 #
    ECC      0       -r           -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec     TLS Request don't require client auth (EC) (bad password)
    ECC      0       -r           -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec       TLS Request don't require client auth (EC) (client auth)
    ECC     254      -r_-r        -V_ssl3:tls1.2_-w_bogus_-n_TestUser-ec     TLS Require client auth (EC) (bad password)
    ECC      0       -r_-r        -V_ssl3:tls1.2_-w_nss_-n_TestUser-ec_      TLS Require client auth (EC) (client auth)
    ECC      0       -r           -V_ssl3:ssl3_-n_TestUser-ec_-w_bogus  SSL3 Request don't require client auth (EC) (bad password)