mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-20 08:51:04 +00:00
Bug 698552: Add SSL_RestartAfterAuthCertificate to mozilla-central's copy of NSS_3_13_2_BETA1, r=kaie, r=honzab
This commit is contained in:
parent
903bd33ecb
commit
9585597fca
@ -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,37 @@ separateReqHeader(const PRFileDesc* outFd, const char* buf, const int nb,
|
||||
Usage(progName); \
|
||||
}
|
||||
|
||||
static SECStatus
|
||||
restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
|
||||
ServerCertAuth * serverCertAuth,
|
||||
PRBool override)
|
||||
{
|
||||
SECStatus rv;
|
||||
|
||||
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 && override) {
|
||||
rv = ownBadCertHandler(NULL, fd);
|
||||
}
|
||||
if (rv != SECSuccess) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
|
||||
|
||||
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 +582,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 +592,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 +610,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 +622,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 +709,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 +929,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 +1043,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 +1109,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,
|
||||
|
@ -411,3 +411,9 @@ ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID, (SSL_ERROR_BASE + 116),
|
||||
|
||||
ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2, (SSL_ERROR_BASE + 117),
|
||||
"SSL feature not supported for SSL 2.0 connections.")
|
||||
|
||||
ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS, (SSL_ERROR_BASE + 118),
|
||||
"SSL feature not supported for servers.")
|
||||
|
||||
ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS, (SSL_ERROR_BASE + 119),
|
||||
"SSL feature not supported for clients.")
|
||||
|
@ -169,6 +169,7 @@ NSSSSL_GetVersion;
|
||||
SSL_SetNextProtoCallback;
|
||||
SSL_SetNextProtoNego;
|
||||
SSL_GetNextProto;
|
||||
SSL_RestartHandshakeAfterAuthCertificate;
|
||||
;+ local:
|
||||
;+ *;
|
||||
;+};
|
||||
|
@ -339,6 +339,19 @@ SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
|
||||
** Authenticate certificate hook. Called when a certificate comes in
|
||||
** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
|
||||
** certificate.
|
||||
**
|
||||
** The authenticate certificate hook must return SECSuccess to indicate the
|
||||
** certificate is valid, SECFailure to indicate the certificate is invalid,
|
||||
** or SECWouldBlock if the application will authenticate the certificate
|
||||
** asynchronously.
|
||||
**
|
||||
** If the authenticate certificate hook returns SECFailure, then the bad cert
|
||||
** hook will be called. The bad cert handler is NEVER called if the
|
||||
** authenticate certificate hook returns SECWouldBlock.
|
||||
**
|
||||
** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
|
||||
** information about the asynchronous behavior that occurs when the
|
||||
** authenticate certificate hook returns SECWouldBlock.
|
||||
*/
|
||||
typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd,
|
||||
PRBool checkSig,
|
||||
@ -442,6 +455,15 @@ SSL_IMPORT SECStatus SSL_SetPKCS11PinArg(PRFileDesc *fd, void *a);
|
||||
** This is a callback for dealing with server certs that are not authenticated
|
||||
** by the client. The client app can decide that it actually likes the
|
||||
** cert by some external means and restart the connection.
|
||||
**
|
||||
** The bad cert hook must return SECSuccess to override the result of the
|
||||
** authenticate certificate hook, SECFailure if the certificate should still be
|
||||
** considered invalid, or SECWouldBlock if the application will authenticate
|
||||
** the certificate asynchronously.
|
||||
**
|
||||
** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
|
||||
** information about the asynchronous behavior that occurs when the bad cert
|
||||
** hook returns SECWouldBlock.
|
||||
*/
|
||||
typedef SECStatus (PR_CALLBACK *SSLBadCertHandler)(void *arg, PRFileDesc *fd);
|
||||
SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f,
|
||||
@ -740,6 +762,53 @@ extern PRBool NSSSSL_VersionCheck(const char *importedVersion);
|
||||
*/
|
||||
extern const char *NSSSSL_GetVersion(void);
|
||||
|
||||
/* Restart an SSL connection that was paused to do asynchronous certificate
|
||||
* chain validation (when the auth certificate hook or bad cert handler
|
||||
* returned SECWouldBlock).
|
||||
*
|
||||
* Currently, this function works only for the client role of a connection; it
|
||||
* does not work for the server role.
|
||||
*
|
||||
* The application MUST call SSL_RestartHandshakeAfterAuthCertificate after it
|
||||
* has successfully validated the peer's certificate to continue the SSL
|
||||
* handshake.
|
||||
*
|
||||
* The application MUST NOT call SSL_RestartHandshakeAfterAuthCertificate when
|
||||
* certificate validation fails; instead, it should just close the connection.
|
||||
*
|
||||
* This function will not complete the entire handshake. The application must
|
||||
* call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function
|
||||
* to force the handshake to complete.
|
||||
*
|
||||
* libssl will wait for the peer's certificate to be authenticated before
|
||||
* calling the handshake callback, sending a client certificate,
|
||||
* sending any application data, or returning any application data to the
|
||||
* application (on the first handshake on a connection only).
|
||||
*
|
||||
* However, libssl may send and receive handshake messages while waiting for
|
||||
* the application to call SSL_RestartHandshakeAfterAuthCertificate, and it may
|
||||
* call other callbacks (e.g, the client auth data hook) before
|
||||
* SSL_RestartHandshakeAfterAuthCertificate has been called.
|
||||
*
|
||||
* An application that uses this asynchronous mechanism will usually have lower
|
||||
* handshake latency if it has to do public key operations on the certificate
|
||||
* chain during the authentication, especially if it does so in parallel on
|
||||
* another thread. However, if the application can authenticate the peer's
|
||||
* certificate quickly then it may be more efficient to use the synchronous
|
||||
* mechanism (i.e. returning SECFailure/SECSuccess instead of SECWouldBlock
|
||||
* from the authenticate certificate hook).
|
||||
*
|
||||
* Be careful about converting an application from synchronous cert validation
|
||||
* to asynchronous certificate validation. A naive conversion is likely to
|
||||
* result in deadlocks; e.g. the application will wait in PR_Poll for network
|
||||
* I/O on the connection while all network I/O on the connection is blocked
|
||||
* waiting for this function to be called.
|
||||
*
|
||||
* Returns SECFailure on failure, SECSuccess on success. Never returns
|
||||
* SECWouldBlock.
|
||||
*/
|
||||
SSL_IMPORT SECStatus SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd);
|
||||
|
||||
SEC_END_PROTOS
|
||||
|
||||
#endif /* __ssl_h_ */
|
||||
|
@ -5649,70 +5649,18 @@ done:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* attempt to restart the handshake after asynchronously handling
|
||||
* a request for the client's certificate.
|
||||
*
|
||||
* inputs:
|
||||
* cert Client cert chosen by application.
|
||||
* Note: ssl takes this reference, and does not bump the
|
||||
* reference count. The caller should drop its reference
|
||||
* without calling CERT_DestroyCert after calling this function.
|
||||
*
|
||||
* key Private key associated with cert. This function makes a
|
||||
* copy of the private key, so the caller remains responsible
|
||||
* for destroying its copy after this function returns.
|
||||
*
|
||||
* certChain DER-encoded certs, client cert and its signers.
|
||||
* Note: ssl takes this reference, and does not copy the chain.
|
||||
* The caller should drop its reference without destroying the
|
||||
* chain. SSL will free the chain when it is done with it.
|
||||
*
|
||||
* Return value: XXX
|
||||
*
|
||||
* XXX This code only works on the initial handshake on a connection, XXX
|
||||
* It does not work on a subsequent handshake (redo).
|
||||
*
|
||||
* Caller holds 1stHandshakeLock.
|
||||
*/
|
||||
SECStatus
|
||||
ssl3_RestartHandshakeAfterCertReq(sslSocket * ss,
|
||||
CERTCertificate * cert,
|
||||
SECKEYPrivateKey * key,
|
||||
CERTCertificateList *certChain)
|
||||
{
|
||||
SECStatus rv = SECSuccess;
|
||||
|
||||
if (MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0)) {
|
||||
/* XXX This code only works on the initial handshake on a connection,
|
||||
** XXX It does not work on a subsequent handshake (redo).
|
||||
*/
|
||||
if (ss->handshake != 0) {
|
||||
ss->handshake = ssl_GatherRecord1stHandshake;
|
||||
ss->ssl3.clientCertificate = cert;
|
||||
ss->ssl3.clientCertChain = certChain;
|
||||
if (key == NULL) {
|
||||
(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
|
||||
ss->ssl3.clientPrivateKey = NULL;
|
||||
} else {
|
||||
ss->ssl3.clientPrivateKey = SECKEY_CopyPrivateKey(key);
|
||||
}
|
||||
ssl_GetRecvBufLock(ss);
|
||||
if (ss->ssl3.hs.msgState.buf != NULL) {
|
||||
rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
|
||||
}
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
ssl3_CanFalseStart(sslSocket *ss) {
|
||||
PRBool rv;
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
|
||||
|
||||
/* XXX: does not take into account whether we are waiting for
|
||||
* SSL_RestartHandshakeAfterAuthCertificate or
|
||||
* SSL_RestartHandshakeAfterCertReq. If/when that is done, this function
|
||||
* could return different results each time it would be called.
|
||||
*/
|
||||
|
||||
ssl_GetSpecReadLock(ss);
|
||||
rv = ss->opt.enableFalseStart &&
|
||||
!ss->sec.isServer &&
|
||||
@ -5726,6 +5674,8 @@ ssl3_CanFalseStart(sslSocket *ss) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
|
||||
|
||||
/* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
|
||||
* ssl3 Server Hello Done message.
|
||||
* Caller must hold Handshake and RecvBuf locks.
|
||||
@ -5735,7 +5685,6 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
|
||||
{
|
||||
SECStatus rv;
|
||||
SSL3WaitState ws = ss->ssl3.hs.ws;
|
||||
PRBool send_verify = PR_FALSE;
|
||||
|
||||
SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
|
||||
SSL_GETPID(), ss->fd));
|
||||
@ -5751,6 +5700,64 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
rv = ssl3_SendClientSecondRound(ss);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Called from ssl3_HandleServerHelloDone and
|
||||
* ssl3_RestartHandshakeAfterServerCert.
|
||||
*
|
||||
* Caller must hold Handshake and RecvBuf locks.
|
||||
*/
|
||||
static SECStatus
|
||||
ssl3_SendClientSecondRound(sslSocket *ss)
|
||||
{
|
||||
SECStatus rv;
|
||||
PRBool sendClientCert;
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
|
||||
|
||||
sendClientCert = !ss->ssl3.sendEmptyCert &&
|
||||
ss->ssl3.clientCertChain != NULL &&
|
||||
ss->ssl3.clientPrivateKey != NULL;
|
||||
|
||||
/* We must wait for the server's certificate to be authenticated before
|
||||
* sending the client certificate in order to disclosing the client
|
||||
* certificate to an attacker that does not have a valid cert for the
|
||||
* domain we are connecting to.
|
||||
*
|
||||
* XXX: We should do the same for the NPN extension, but for that we
|
||||
* need an option to give the application the ability to leak the NPN
|
||||
* information to get better performance.
|
||||
*
|
||||
* During the initial handshake on a connection, we never send/receive
|
||||
* application data until we have authenticated the server's certificate;
|
||||
* i.e. we have fully authenticated the handshake before using the cipher
|
||||
* specs agreed upon for that handshake. During a renegotiation, we may
|
||||
* continue sending and receiving application data during the handshake
|
||||
* interleaved with the handshake records. If we were to send the client's
|
||||
* second round for a renegotiation before the server's certificate was
|
||||
* authenticated, then the application data sent/received after this point
|
||||
* would be using cipher spec that hadn't been authenticated. By waiting
|
||||
* until the server's certificate has been authenticated during
|
||||
* renegotiations, we ensure that renegotiations have the same property
|
||||
* as initial handshakes; i.e. we have fully authenticated the handshake
|
||||
* before using the cipher specs agreed upon for that handshake for
|
||||
* application data.
|
||||
*/
|
||||
if (ss->ssl3.hs.restartTarget) {
|
||||
PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
if (ss->ssl3.hs.authCertificatePending &&
|
||||
(sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
|
||||
ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
|
||||
return SECWouldBlock;
|
||||
}
|
||||
|
||||
ssl_GetXmitBufLock(ss); /*******************************/
|
||||
|
||||
if (ss->ssl3.sendEmptyCert) {
|
||||
@ -5760,10 +5767,7 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* error code is set. */
|
||||
}
|
||||
} else
|
||||
if (ss->ssl3.clientCertChain != NULL &&
|
||||
ss->ssl3.clientPrivateKey != NULL) {
|
||||
send_verify = PR_TRUE;
|
||||
} else if (sendClientCert) {
|
||||
rv = ssl3_SendCertificate(ss);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* error code is set. */
|
||||
@ -5775,17 +5779,21 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
|
||||
goto loser; /* err is set. */
|
||||
}
|
||||
|
||||
if (send_verify) {
|
||||
if (sendClientCert) {
|
||||
rv = ssl3_SendCertificateVerify(ss);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* err is set. */
|
||||
}
|
||||
}
|
||||
|
||||
rv = ssl3_SendChangeCipherSpecs(ss);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* err code was set. */
|
||||
}
|
||||
|
||||
/* XXX: If the server's certificate hasn't been authenticated by this
|
||||
* point, then we may be leaking this NPN message to an attacker.
|
||||
*/
|
||||
if (!ss->firstHsDone) {
|
||||
rv = ssl3_SendNextProto(ss);
|
||||
if (rv != SECSuccess) {
|
||||
@ -7814,8 +7822,6 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
{
|
||||
ssl3CertNode * c;
|
||||
ssl3CertNode * lastCert = NULL;
|
||||
ssl3CertNode * certs = NULL;
|
||||
PRArenaPool * arena = NULL;
|
||||
PRInt32 remaining = 0;
|
||||
PRInt32 size;
|
||||
SECStatus rv;
|
||||
@ -7872,11 +7878,11 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
errCode = PORT_GetError();
|
||||
goto loser;
|
||||
}
|
||||
goto cert_block;
|
||||
goto server_no_cert;
|
||||
}
|
||||
|
||||
ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
if ( arena == NULL ) {
|
||||
ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
if (ss->ssl3.peerCertArena == NULL) {
|
||||
goto loser; /* don't send alerts on memory errors */
|
||||
}
|
||||
|
||||
@ -7926,7 +7932,7 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
length -= size;
|
||||
remaining -= size;
|
||||
|
||||
c = PORT_ArenaNew(arena, ssl3CertNode);
|
||||
c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode);
|
||||
if (c == NULL) {
|
||||
goto loser; /* don't send alerts on memory errors */
|
||||
}
|
||||
@ -7944,7 +7950,7 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
if (lastCert) {
|
||||
lastCert->next = c;
|
||||
} else {
|
||||
certs = c;
|
||||
ss->ssl3.peerCertChain = c;
|
||||
}
|
||||
lastCert = c;
|
||||
}
|
||||
@ -7954,6 +7960,8 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
|
||||
SECKEY_UpdateCertPQG(ss->sec.peerCert);
|
||||
|
||||
ss->ssl3.hs.authCertificatePending = PR_FALSE;
|
||||
|
||||
/*
|
||||
* Ask caller-supplied callback function to validate cert chain.
|
||||
*/
|
||||
@ -7961,24 +7969,26 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
PR_TRUE, isServer);
|
||||
if (rv) {
|
||||
errCode = PORT_GetError();
|
||||
if (!ss->handleBadCert) {
|
||||
goto bad_cert;
|
||||
}
|
||||
rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd);
|
||||
if ( rv ) {
|
||||
if ( rv == SECWouldBlock ) {
|
||||
/* someone will handle this connection asynchronously*/
|
||||
SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
|
||||
SSL_GETPID(), ss->fd));
|
||||
ss->ssl3.peerCertChain = certs;
|
||||
certs = NULL;
|
||||
ssl3_SetAlwaysBlock(ss);
|
||||
goto cert_block;
|
||||
if (rv != SECWouldBlock) {
|
||||
if (ss->handleBadCert) {
|
||||
rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
|
||||
}
|
||||
/* cert is bad */
|
||||
}
|
||||
|
||||
if (rv == SECWouldBlock) {
|
||||
if (ss->sec.isServer) {
|
||||
errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
|
||||
rv = SECFailure;
|
||||
goto loser;
|
||||
}
|
||||
|
||||
ss->ssl3.hs.authCertificatePending = PR_TRUE;
|
||||
rv = SECSuccess;
|
||||
}
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
goto bad_cert;
|
||||
}
|
||||
/* cert is good */
|
||||
}
|
||||
|
||||
ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
|
||||
@ -8026,14 +8036,7 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
|
||||
SECKEY_DestroyPublicKey(pubKey);
|
||||
pubKey = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL;
|
||||
|
||||
cert_block:
|
||||
if (ss->sec.isServer) {
|
||||
ss->ssl3.hs.ws = wait_client_key;
|
||||
} else {
|
||||
ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */
|
||||
if (ss->ssl3.hs.kea_def->is_limited ||
|
||||
/* XXX OR server cert is signing only. */
|
||||
@ -8044,11 +8047,17 @@ cert_block:
|
||||
ss->ssl3.hs.kea_def->exchKeyType == kt_dh) {
|
||||
ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */
|
||||
}
|
||||
} else {
|
||||
server_no_cert:
|
||||
ss->ssl3.hs.ws = wait_client_key;
|
||||
}
|
||||
|
||||
/* rv must normally be equal to SECSuccess here. If we called
|
||||
* handleBadCert, it can also be SECWouldBlock.
|
||||
*/
|
||||
PORT_Assert(rv == SECSuccess);
|
||||
if (rv != SECSuccess) {
|
||||
errCode = SEC_ERROR_LIBRARY_FAILURE;
|
||||
rv = SECFailure;
|
||||
goto loser;
|
||||
}
|
||||
return rv;
|
||||
|
||||
ambiguous_err:
|
||||
@ -8099,7 +8108,6 @@ alert_loser:
|
||||
(void)SSL3_SendAlert(ss, alert_fatal, desc);
|
||||
|
||||
loser:
|
||||
ss->ssl3.peerCertChain = certs; certs = NULL; arena = NULL;
|
||||
ssl3_CleanupPeerCerts(ss);
|
||||
|
||||
if (ss->sec.peerCert != NULL) {
|
||||
@ -8110,43 +8118,49 @@ loser:
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
static SECStatus ssl3_FinishHandshake(sslSocket *ss);
|
||||
|
||||
/* restart an SSL connection that we stopped to run certificate dialogs
|
||||
** XXX Need to document here how an application marks a cert to show that
|
||||
** the application has accepted it (overridden CERT_VerifyCert).
|
||||
*
|
||||
* XXX This code only works on the initial handshake on a connection, XXX
|
||||
* It does not work on a subsequent handshake (redo).
|
||||
*
|
||||
* Return value: XXX
|
||||
*
|
||||
* Caller holds 1stHandshakeLock.
|
||||
/* Caller must hold 1stHandshakeLock.
|
||||
*/
|
||||
int
|
||||
ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
|
||||
SECStatus
|
||||
ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss)
|
||||
{
|
||||
int rv = SECSuccess;
|
||||
SECStatus rv;
|
||||
|
||||
if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
|
||||
SET_ERROR_CODE
|
||||
return SECFailure;
|
||||
}
|
||||
if (!ss->ssl3.initialized) {
|
||||
SET_ERROR_CODE
|
||||
return SECFailure;
|
||||
PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
|
||||
|
||||
if (ss->sec.isServer) {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->handshake != NULL) {
|
||||
ss->handshake = ssl_GatherRecord1stHandshake;
|
||||
ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
|
||||
ssl_GetRecvBufLock(ss);
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
ssl_GetRecvBufLock(ss);
|
||||
if (ss->ssl3.hs.msgState.buf != NULL) {
|
||||
rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
|
||||
}
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
if (!ss->ssl3.hs.authCertificatePending) {
|
||||
PORT_SetError(PR_INVALID_STATE_ERROR);
|
||||
rv = SECFailure;
|
||||
} else {
|
||||
ss->ssl3.hs.authCertificatePending = PR_FALSE;
|
||||
if (ss->ssl3.hs.restartTarget != NULL) {
|
||||
sslRestartTarget target = ss->ssl3.hs.restartTarget;
|
||||
ss->ssl3.hs.restartTarget = NULL;
|
||||
rv = target(ss);
|
||||
/* Even if we blocked here, we have accomplished enough to claim
|
||||
* success. Any remaining work will be taken care of by subsequent
|
||||
* calls to SSL_ForceHandshake/PR_Send/PR_Read/etc.
|
||||
*/
|
||||
if (rv == SECWouldBlock) {
|
||||
rv = SECSuccess;
|
||||
}
|
||||
} else {
|
||||
rv = SECSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -8499,9 +8513,6 @@ xmit_loser:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* The first handshake is now completed. */
|
||||
ss->handshake = NULL;
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->gs.writeOffset = 0;
|
||||
ss->gs.readOffset = 0;
|
||||
|
||||
@ -8551,10 +8562,42 @@ xmit_loser:
|
||||
/* If the wrap failed, we don't cache the sid.
|
||||
* The connection continues normally however.
|
||||
*/
|
||||
if (rv == SECSuccess) {
|
||||
(*ss->sec.cache)(sid);
|
||||
}
|
||||
ss->ssl3.hs.cacheSID = rv == SECSuccess;
|
||||
}
|
||||
|
||||
if (ss->ssl3.hs.authCertificatePending) {
|
||||
if (ss->ssl3.hs.restartTarget) {
|
||||
PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
|
||||
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
|
||||
return SECWouldBlock;
|
||||
}
|
||||
|
||||
rv = ssl3_FinishHandshake(ss);
|
||||
return rv;
|
||||
}
|
||||
|
||||
SECStatus
|
||||
ssl3_FinishHandshake(sslSocket * ss)
|
||||
{
|
||||
SECStatus rv;
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
|
||||
PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
|
||||
|
||||
/* The first handshake is now completed. */
|
||||
ss->handshake = NULL;
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
|
||||
if (ss->sec.ci.sid->cached == never_cached &&
|
||||
!ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) {
|
||||
(*ss->sec.cache)(ss->sec.ci.sid);
|
||||
}
|
||||
|
||||
ss->ssl3.hs.ws = idle_handshake;
|
||||
|
||||
/* Do the handshake callback for sslv3 here, if we cannot false start. */
|
||||
|
@ -192,21 +192,53 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
|
||||
do {
|
||||
/* bring in the next sslv3 record. */
|
||||
rv = ssl3_GatherData(ss, &ss->gs, flags);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* decipher it, and handle it if it's a handshake.
|
||||
* If it's application data, ss->gs.buf will not be empty upon return.
|
||||
* If it's a change cipher spec, alert, or handshake message,
|
||||
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
|
||||
*/
|
||||
cText.type = (SSL3ContentType)ss->gs.hdr[0];
|
||||
cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
|
||||
cText.buf = &ss->gs.inbuf;
|
||||
rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
|
||||
/* Without this, we may end up wrongly reporting
|
||||
* SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
|
||||
* peer while we are waiting to be restarted.
|
||||
*/
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
if (rv != SECSuccess) {
|
||||
PORT_SetError(PR_WOULD_BLOCK_ERROR);
|
||||
return (int) SECFailure;
|
||||
}
|
||||
|
||||
/* Treat an empty msgState like a NULL msgState. (Most of the time
|
||||
* when ssl3_HandleHandshake returns SECWouldBlock, it leaves
|
||||
* behind a non-NULL but zero-length msgState).
|
||||
* Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
|
||||
*/
|
||||
if (ss->ssl3.hs.msgState.buf != NULL) {
|
||||
if (ss->ssl3.hs.msgState.len == 0) {
|
||||
ss->ssl3.hs.msgState.buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ss->ssl3.hs.msgState.buf != NULL) {
|
||||
/* ssl3_HandleHandshake previously returned SECWouldBlock and the
|
||||
* as-yet-unprocessed plaintext of that previous handshake record.
|
||||
* We need to process it now before we overwrite it with the next
|
||||
* handshake record.
|
||||
*/
|
||||
rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
|
||||
} else {
|
||||
/* bring in the next sslv3 record. */
|
||||
rv = ssl3_GatherData(ss, &ss->gs, flags);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* decipher it, and handle it if it's a handshake.
|
||||
* If it's application data, ss->gs.buf will not be empty upon return.
|
||||
* If it's a change cipher spec, alert, or handshake message,
|
||||
* ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
|
||||
*/
|
||||
cText.type = (SSL3ContentType)ss->gs.hdr[0];
|
||||
cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
|
||||
cText.buf = &ss->gs.inbuf;
|
||||
rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
|
||||
}
|
||||
if (rv < 0) {
|
||||
return ss->recvdCloseNotify ? 0 : rv;
|
||||
}
|
||||
|
@ -208,6 +208,8 @@ SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY = (SSL_ERROR_BASE + 115),
|
||||
SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID = (SSL_ERROR_BASE + 116),
|
||||
|
||||
SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2 = (SSL_ERROR_BASE + 117),
|
||||
SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS = (SSL_ERROR_BASE + 118),
|
||||
SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS = (SSL_ERROR_BASE + 119),
|
||||
|
||||
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
|
||||
} SSLErrorCodes;
|
||||
|
@ -750,6 +750,8 @@ struct TLSExtensionDataStr {
|
||||
PRUint32 sniNameArrSize;
|
||||
};
|
||||
|
||||
typedef SECStatus (*sslRestartTarget)(sslSocket *);
|
||||
|
||||
/*
|
||||
** This is the "hs" member of the "ssl3" struct.
|
||||
** This entire struct is protected by ssl3HandshakeLock
|
||||
@ -789,6 +791,13 @@ const ssl3CipherSuiteDef *suite_def;
|
||||
#ifdef NSS_ENABLE_ECC
|
||||
PRUint32 negotiatedECCurves; /* bit mask */
|
||||
#endif /* NSS_ENABLE_ECC */
|
||||
|
||||
PRBool authCertificatePending;
|
||||
/* Which function should SSL_RestartHandshake* call if we're blocked?
|
||||
* One of NULL, ssl3_SendClientSecondRound, or ssl3_FinishHandshake. */
|
||||
sslRestartTarget restartTarget;
|
||||
/* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
|
||||
PRBool cacheSID;
|
||||
} SSL3HandshakeState;
|
||||
|
||||
|
||||
@ -1340,7 +1349,6 @@ extern SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec,
|
||||
/* These functions are called from secnav, even though they're "private". */
|
||||
|
||||
extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
|
||||
extern int SSL_RestartHandshakeAfterServerCert(struct sslSocketStr *ss);
|
||||
extern int SSL_RestartHandshakeAfterCertReq(struct sslSocketStr *ss,
|
||||
CERTCertificate *cert,
|
||||
SECKEYPrivateKey *key,
|
||||
@ -1350,12 +1358,7 @@ extern void ssl_FreeSocket(struct sslSocketStr *ssl);
|
||||
extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
|
||||
SSL3AlertDescription desc);
|
||||
|
||||
extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket * ss,
|
||||
CERTCertificate * cert,
|
||||
SECKEYPrivateKey * key,
|
||||
CERTCertificateList *certChain);
|
||||
|
||||
extern int ssl3_RestartHandshakeAfterServerCert(sslSocket *ss);
|
||||
extern SECStatus ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss);
|
||||
|
||||
/*
|
||||
* for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
|
||||
|
@ -1463,29 +1463,8 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle)
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* attempt to restart the handshake after asynchronously handling
|
||||
* a request for the client's certificate.
|
||||
*
|
||||
* inputs:
|
||||
* cert Client cert chosen by application.
|
||||
* Note: ssl takes this reference, and does not bump the
|
||||
* reference count. The caller should drop its reference
|
||||
* without calling CERT_DestroyCert after calling this function.
|
||||
*
|
||||
* key Private key associated with cert. This function makes a
|
||||
* copy of the private key, so the caller remains responsible
|
||||
* for destroying its copy after this function returns.
|
||||
*
|
||||
* certChain Chain of signers for cert.
|
||||
* Note: ssl takes this reference, and does not copy the chain.
|
||||
* The caller should drop its reference without destroying the
|
||||
* chain. SSL will free the chain when it is done with it.
|
||||
*
|
||||
* Return value: XXX
|
||||
*
|
||||
* XXX This code only works on the initial handshake on a connection, XXX
|
||||
* It does not work on a subsequent handshake (redo).
|
||||
/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
|
||||
* this implementation exists to maintain link-time compatibility.
|
||||
*/
|
||||
int
|
||||
SSL_RestartHandshakeAfterCertReq(sslSocket * ss,
|
||||
@ -1493,46 +1472,47 @@ SSL_RestartHandshakeAfterCertReq(sslSocket * ss,
|
||||
SECKEYPrivateKey * key,
|
||||
CERTCertificateList *certChain)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ssl_Get1stHandshakeLock(ss); /************************************/
|
||||
|
||||
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
|
||||
ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
|
||||
} else {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
|
||||
ret = SECFailure;
|
||||
}
|
||||
|
||||
ssl_Release1stHandshakeLock(ss); /************************************/
|
||||
return ret;
|
||||
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* restart an SSL connection that we stopped to run certificate dialogs
|
||||
** XXX Need to document here how an application marks a cert to show that
|
||||
** the application has accepted it (overridden CERT_VerifyCert).
|
||||
*
|
||||
* XXX This code only works on the initial handshake on a connection, XXX
|
||||
* It does not work on a subsequent handshake (redo).
|
||||
*
|
||||
* Return value: XXX
|
||||
*/
|
||||
/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
|
||||
* this implementation exists to maintain link-time compatibility.
|
||||
*/
|
||||
int
|
||||
SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
|
||||
SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
|
||||
{
|
||||
int rv = SECSuccess;
|
||||
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
/* See documentation in ssl.h */
|
||||
SECStatus
|
||||
SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd)
|
||||
{
|
||||
SECStatus rv = SECSuccess;
|
||||
sslSocket *ss = ssl_FindSocket(fd);
|
||||
|
||||
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
|
||||
rv = ssl3_RestartHandshakeAfterServerCert(ss);
|
||||
if (!ss) {
|
||||
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
|
||||
SSL_GETPID(), fd));
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
|
||||
if (!ss->ssl3.initialized) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
rv = SECFailure;
|
||||
} else if (ss->version < SSL_LIBRARY_VERSION_3_0) {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
|
||||
rv = SECFailure;
|
||||
} else {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
|
||||
rv = SECFailure;
|
||||
rv = ssl3_RestartHandshakeAfterAuthCertificate(ss);
|
||||
}
|
||||
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -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=$?
|
||||
|
@ -1,2 +1,7 @@
|
||||
This directory contains patches that were added locally
|
||||
on top of the NSS release.
|
||||
|
||||
bug-542832-ssl-restart-4.patch and bug-542832-ssl-restart-tstclnt-4.patch were
|
||||
added so that we could test the new PSM SSL threading code (bug 674147) and
|
||||
SPDY (bug 528288). These patches will be removed when the NSS 3.13.2 release
|
||||
that includes them is imported into mozilla-central.
|
||||
|
1076
security/patches/bug-542832-ssl-restart-4.patch
Normal file
1076
security/patches/bug-542832-ssl-restart-4.patch
Normal file
File diff suppressed because it is too large
Load Diff
349
security/patches/bug-542832-ssl-restart-tstclnt-4.patch
Normal file
349
security/patches/bug-542832-ssl-restart-tstclnt-4.patch
Normal file
@ -0,0 +1,349 @@
|
||||
Index: mozilla/security/nss/cmd/tstclnt/tstclnt.c
|
||||
===================================================================
|
||||
RCS file: /cvsroot/mozilla/security/nss/cmd/tstclnt/tstclnt.c,v
|
||||
retrieving revision 1.64
|
||||
diff -u -8 -p -r1.64 tstclnt.c
|
||||
--- mozilla/security/nss/cmd/tstclnt/tstclnt.c 6 Oct 2011 22:42:33 -0000 1.64
|
||||
+++ mozilla/security/nss/cmd/tstclnt/tstclnt.c 16 Nov 2011 08:24:12 -0000
|
||||
@@ -212,16 +212,18 @@ static void Usage(const char *progName)
|
||||
"-n nickname");
|
||||
fprintf(stderr,
|
||||
"%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B");
|
||||
fprintf(stderr, "%-20s Disable SSL v2.\n", "-2");
|
||||
fprintf(stderr, "%-20s Disable SSL v3.\n", "-3");
|
||||
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");
|
||||
fprintf(stderr, "%-20s Use export policy.\n", "-x");
|
||||
fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
|
||||
fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
|
||||
fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
|
||||
fprintf(stderr, "%-20s Enable compression.\n", "-z");
|
||||
@@ -288,30 +290,54 @@ disableAllSSLCiphers(void)
|
||||
fprintf(stderr,
|
||||
"SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n",
|
||||
suite, i, SECU_Strerror(err));
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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.
|
||||
*/
|
||||
static SECStatus
|
||||
ownBadCertHandler(void * arg, PRFileDesc * socket)
|
||||
{
|
||||
PRErrorCode err = PR_GetError();
|
||||
/* can log invalid cert here */
|
||||
fprintf(stderr, "Bad server certificate: %d, %s\n", err,
|
||||
SECU_Strerror(err));
|
||||
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,
|
||||
struct CERTDistNamesStr * caNames,
|
||||
struct CERTCertificateStr ** pRetCert,
|
||||
struct SECKEYPrivateKeyStr **pRetKey)
|
||||
{
|
||||
if (verbose > 1) {
|
||||
@@ -493,21 +519,47 @@ separateReqHeader(const PRFileDesc* outF
|
||||
} else if (((c) >= 'a') && ((c) <= 'f')) { \
|
||||
i = (c) - 'a' + 10; \
|
||||
} else if (((c) >= 'A') && ((c) <= 'F')) { \
|
||||
i = (c) - 'A' + 10; \
|
||||
} else { \
|
||||
Usage(progName); \
|
||||
}
|
||||
|
||||
+static SECStatus
|
||||
+restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
|
||||
+ ServerCertAuth * serverCertAuth,
|
||||
+ PRBool override)
|
||||
+{
|
||||
+ SECStatus rv;
|
||||
+
|
||||
+ 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 && override) {
|
||||
+ rv = ownBadCertHandler(NULL, fd);
|
||||
+ }
|
||||
+ if (rv != SECSuccess) {
|
||||
+ return rv;
|
||||
+ }
|
||||
+
|
||||
+ rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
|
||||
+
|
||||
+ return rv;
|
||||
+}
|
||||
+
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
PRFileDesc * s;
|
||||
PRFileDesc * std_out;
|
||||
- CERTCertDBHandle * handle;
|
||||
char * host = NULL;
|
||||
char * certDir = NULL;
|
||||
char * nickname = NULL;
|
||||
char * cipherString = NULL;
|
||||
char * tmp;
|
||||
int multiplier = 0;
|
||||
SECStatus rv;
|
||||
PRStatus status;
|
||||
@@ -525,51 +577,58 @@ int main(int argc, char **argv)
|
||||
int enableFalseStart = 0;
|
||||
PRSocketOptionData opt;
|
||||
PRNetAddr addr;
|
||||
PRPollDesc pollset[2];
|
||||
PRBool pingServerFirst = PR_FALSE;
|
||||
PRBool clientSpeaksFirst = PR_FALSE;
|
||||
PRBool wrStarted = PR_FALSE;
|
||||
PRBool skipProtoHeader = PR_FALSE;
|
||||
+ ServerCertAuth serverCertAuth;
|
||||
int headerSeparatorPtrnId = 0;
|
||||
int error = 0;
|
||||
PRUint16 portno = 443;
|
||||
char * hs1SniHostName = NULL;
|
||||
char * hs2SniHostName = NULL;
|
||||
PLOptState *optstate;
|
||||
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], '\\');
|
||||
progName = progName ? progName+1 : argv[0];
|
||||
|
||||
tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT");
|
||||
if (tmp && tmp[0]) {
|
||||
int sec = PORT_Atoi(tmp);
|
||||
if (sec > 0) {
|
||||
maxInterval = PR_SecondsToInterval(sec);
|
||||
}
|
||||
}
|
||||
|
||||
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 '?':
|
||||
default : Usage(progName); break;
|
||||
|
||||
case '2': disableSSL2 = 1; break;
|
||||
|
||||
case '3': disableSSL3 = 1; break;
|
||||
|
||||
case 'B': bypassPKCS11 = 1; break;
|
||||
|
||||
+ case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
|
||||
+
|
||||
case 'S': skipProtoHeader = PR_TRUE; break;
|
||||
|
||||
case 'T': disableTLS = 1; break;
|
||||
|
||||
case 'a': if (!hs1SniHostName) {
|
||||
hs1SniHostName = PORT_Strdup(optstate->value);
|
||||
} else if (!hs2SniHostName) {
|
||||
hs2SniHostName = PORT_Strdup(optstate->value);
|
||||
@@ -645,24 +704,18 @@ int main(int argc, char **argv)
|
||||
} else {
|
||||
char *certDirTmp = certDir;
|
||||
certDir = SECU_ConfigDirectory(certDirTmp);
|
||||
PORT_Free(certDirTmp);
|
||||
}
|
||||
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)
|
||||
NSS_SetExportPolicy();
|
||||
else
|
||||
NSS_SetDomesticPolicy();
|
||||
|
||||
/* all the SSL2 and SSL3 cipher suites are enabled by default. */
|
||||
@@ -871,17 +924,23 @@ int main(int argc, char **argv)
|
||||
rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
|
||||
if (rv != SECSuccess) {
|
||||
SECU_PrintError(progName, "error enabling false start");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
|
||||
SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
|
||||
if (hs1SniHostName) {
|
||||
SSL_SetURL(s, hs1SniHostName);
|
||||
} else {
|
||||
@@ -979,16 +1038,24 @@ int main(int argc, char **argv)
|
||||
** socket, read data from socket and write to stdout.
|
||||
*/
|
||||
FPRINTF(stderr, "%s: ready...\n", progName);
|
||||
|
||||
while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) {
|
||||
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;
|
||||
|
||||
FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
|
||||
filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
|
||||
if (filesReady < 0) {
|
||||
SECU_PrintError(progName, "select failed");
|
||||
error = 1;
|
||||
@@ -1037,16 +1104,25 @@ int main(int argc, char **argv)
|
||||
goto done;
|
||||
}
|
||||
cc = 0;
|
||||
}
|
||||
bufp += cc;
|
||||
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,
|
||||
"%s: about to call PR_Poll on writable socket !\n",
|
||||
progName);
|
||||
cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
|
||||
FPRINTF(stderr,
|
||||
"%s: PR_Poll returned with writable socket !\n",
|
||||
Index: mozilla/security/nss/tests/ssl/ssl.sh
|
||||
===================================================================
|
||||
RCS file: /cvsroot/mozilla/security/nss/tests/ssl/ssl.sh,v
|
||||
retrieving revision 1.106
|
||||
diff -u -8 -p -r1.106 ssl.sh
|
||||
--- mozilla/security/nss/tests/ssl/ssl.sh 29 Jan 2010 22:36:25 -0000 1.106
|
||||
+++ mozilla/security/nss/tests/ssl/ssl.sh 16 Nov 2011 08:24:14 -0000
|
||||
@@ -303,16 +303,26 @@ ssl_cov()
|
||||
|
||||
exec < ${SSLCOV}
|
||||
while read ectype tls param testname
|
||||
do
|
||||
echo "${testname}" | grep "EXPORT" > /dev/null
|
||||
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"
|
||||
elif [ "$ectype" = "ECC" -a -z "$NSS_ENABLE_ECC" ] ; then
|
||||
echo "$SCRIPTNAME: skipping $testname (ECC only)"
|
||||
elif [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] && [ "$SSL2" -eq 0 -o "$EXP" -eq 0 ] ; then
|
||||
echo "$SCRIPTNAME: skipping $testname (non-FIPS only)"
|
||||
elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
|
||||
@@ -345,21 +355,21 @@ ssl_cov()
|
||||
is_selfserv_alive
|
||||
else
|
||||
kill_selfserv
|
||||
start_selfserv
|
||||
mixed=0
|
||||
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=$?
|
||||
cat ${TMP}/$HOST.tmp.$$
|
||||
rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
|
||||
html_msg $ret 0 "${testname}" \
|
||||
"produced a returncode of $ret, expected is 0"
|
||||
fi
|
Loading…
x
Reference in New Issue
Block a user