From 7f0fbbdd65e6b3197d192804133c0fa3daba998a Mon Sep 17 00:00:00 2001 From: "kaie%kuix.de" Date: Sat, 11 Feb 2012 13:27:07 +0000 Subject: [PATCH] Bug 542832 - SSL_RestartHandshakeAfterServerCert is broken, patch by Brian Smith, r=rrelyea git-svn-id: svn://10.0.0.236/trunk@263388 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/security/nss/cmd/tstclnt/tstclnt.c | 104 +++++++++++++++++++-- mozilla/security/nss/tests/ssl/ssl.sh | 14 ++- 2 files changed, 107 insertions(+), 11 deletions(-) diff --git a/mozilla/security/nss/cmd/tstclnt/tstclnt.c b/mozilla/security/nss/cmd/tstclnt/tstclnt.c index f9afc02fca0..ca1fd5947b4 100644 --- a/mozilla/security/nss/cmd/tstclnt/tstclnt.c +++ b/mozilla/security/nss/cmd/tstclnt/tstclnt.c @@ -217,6 +217,8 @@ static void Usage(const char *progName) fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T"); fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S"); fprintf(stderr, "%-20s Client speaks first. \n", "-f"); + fprintf(stderr, "%-20s Use synchronous certificate validation " + "(required for SSL2)\n", "-O"); fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o"); fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s"); fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v"); @@ -293,6 +295,16 @@ disableAllSSLCiphers(void) } } +typedef struct +{ + PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert + * authentication */ + PRBool isPaused; /* PR_TRUE if libssl is waiting for us to validate the + * peer's certificate and restart the handshake. */ + void * dbHandle; /* Certificate database handle to use while + * authenticating the peer's certificate. */ +} ServerCertAuth; + /* * Callback is called when incoming certificate is not valid. * Returns SECSuccess to accept the cert anyway, SECFailure to reject. @@ -307,6 +319,20 @@ ownBadCertHandler(void * arg, PRFileDesc * socket) return SECSuccess; /* override, say it's OK. */ } +static SECStatus +ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer) +{ + ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg; + + FPRINTF(stderr, "using asynchronous certificate validation\n", progName); + + PORT_Assert(serverCertAuth->shouldPause); + PORT_Assert(!serverCertAuth->isPaused); + serverCertAuth->isPaused = PR_TRUE; + return SECWouldBlock; +} + SECStatus own_GetClientAuthData(void * arg, PRFileDesc * socket, @@ -498,11 +524,47 @@ separateReqHeader(const PRFileDesc* outFd, const char* buf, const int nb, Usage(progName); \ } +static SECStatus +restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd, + ServerCertAuth * serverCertAuth, + PRBool override) +{ + SECStatus rv; + PRErrorCode status; + + if (!serverCertAuth->isPaused) + return SECSuccess; + + FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n", + progName); + + serverCertAuth->isPaused = PR_FALSE; + rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE); + if (rv != SECSuccess) { + status = PR_GetError(); + if (status == 0) { + PR_NOT_REACHED("SSL_AuthCertificate return SECFailure without " + "setting error code."); + status = PR_INVALID_STATE_ERROR; + } else if (override) { + rv = ownBadCertHandler(NULL, fd); + } + } + if (rv == SECSuccess) { + status = 0; + } + + if (SSL_AuthCertificateComplete(fd, status) != SECSuccess) { + rv = SECFailure; + } + + return rv; +} + int main(int argc, char **argv) { PRFileDesc * s; PRFileDesc * std_out; - CERTCertDBHandle * handle; char * host = NULL; char * certDir = NULL; char * nickname = NULL; @@ -530,6 +592,7 @@ int main(int argc, char **argv) PRBool clientSpeaksFirst = PR_FALSE; PRBool wrStarted = PR_FALSE; PRBool skipProtoHeader = PR_FALSE; + ServerCertAuth serverCertAuth; int headerSeparatorPtrnId = 0; int error = 0; PRUint16 portno = 443; @@ -539,6 +602,10 @@ int main(int argc, char **argv) PLOptStatus optstatus; PRStatus prStatus; + serverCertAuth.shouldPause = PR_TRUE; + serverCertAuth.isPaused = PR_FALSE; + serverCertAuth.dbHandle = NULL; + progName = strrchr(argv[0], '/'); if (!progName) progName = strrchr(argv[0], '\\'); @@ -553,7 +620,7 @@ int main(int argc, char **argv) } optstate = PL_CreateOptState(argc, argv, - "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz"); + "23BOSTW:a:c:d:fgh:m:n:op:qr:suvw:xz"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -565,6 +632,8 @@ int main(int argc, char **argv) case 'B': bypassPKCS11 = 1; break; + case 'O': serverCertAuth.shouldPause = PR_FALSE; break; + case 'S': skipProtoHeader = PR_TRUE; break; case 'T': disableTLS = 1; break; @@ -650,14 +719,8 @@ int main(int argc, char **argv) rv = NSS_Init(certDir); if (rv != SECSuccess) { SECU_PrintError(progName, "unable to open cert database"); -#if 0 - rv = CERT_OpenVolatileCertDB(handle); - CERT_SetDefaultCertDB(handle); -#else return 1; -#endif } - handle = CERT_GetDefaultCertDB(); /* set the policy bits true for all the cipher suites. */ if (useExportPolicy) @@ -876,7 +939,13 @@ int main(int argc, char **argv) SSL_SetPKCS11PinArg(s, &pwdata); - SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle); + serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); + + if (serverCertAuth.shouldPause) { + SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); + } else { + SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle); + } if (override) { SSL_BadCertHook(s, ownBadCertHandler, NULL); } @@ -984,6 +1053,14 @@ int main(int argc, char **argv) char buf[4000]; /* buffer for stdin */ int nb; /* num bytes read from stdin. */ + rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth, + override); + if (rv != SECSuccess) { + error = 254; /* 254 (usually) means "handshake failed" */ + SECU_PrintError(progName, "authentication of server cert failed"); + goto done; + } + pollset[SSOCK_FD].out_flags = 0; pollset[STDIN_FD].out_flags = 0; @@ -1042,6 +1119,15 @@ int main(int argc, char **argv) nb -= cc; if (nb <= 0) break; + + rv = restartHandshakeAfterServerCertIfNeeded(s, + &serverCertAuth, override); + if (rv != SECSuccess) { + error = 254; /* 254 (usually) means "handshake failed" */ + SECU_PrintError(progName, "authentication of server cert failed"); + goto done; + } + pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; pollset[SSOCK_FD].out_flags = 0; FPRINTF(stderr, diff --git a/mozilla/security/nss/tests/ssl/ssl.sh b/mozilla/security/nss/tests/ssl/ssl.sh index 33fa7ed59f4..5a0723bdd03 100755 --- a/mozilla/security/nss/tests/ssl/ssl.sh +++ b/mozilla/security/nss/tests/ssl/ssl.sh @@ -308,6 +308,16 @@ ssl_cov() EXP=$? echo "${testname}" | grep "SSL2" > /dev/null SSL2=$? + + if [ "${SSL2}" -eq 0 ] ; then + # We cannot use asynchronous cert verification with SSL2 + SSL2_FLAGS=-O + else + # Do not enable SSL2 for non-SSL2-specific tests. SSL2 is disabled by + # default in libssl but it is enabled by default in tstclnt; we want + # to test the libssl default whenever possible. + SSL2_FLAGS=-2 + fi if [ "$NORM_EXT" = "Extended Test" -a "${SSL2}" -eq 0 ] ; then echo "$SCRIPTNAME: skipping $testname for $NORM_EXT" @@ -350,11 +360,11 @@ ssl_cov() fi fi - echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} \\" + echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} \\" echo " -f -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE}" rm ${TMP}/$HOST.tmp.$$ 2>/dev/null - ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} -f \ + ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} -f \ -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE} \ >${TMP}/$HOST.tmp.$$ 2>&1 ret=$?