This source file includes following definitions.
- ssl_Do1stHandshake
- ssl_FinishHandshake
- ssl3_AlwaysBlock
- ssl3_SetAlwaysBlock
- ssl_SetTimeout
- SSL_ResetHandshake
- SSL_ReHandshake
- SSL_ReHandshakeWithTimeout
- SSL_RedoHandshake
- SSL_HandshakeCallback
- SSL_SetCanFalseStartCallback
- SSL_RecommendedCanFalseStart
- SSL_ForceHandshake
- SSL_ForceHandshakeWithTimeout
- sslBuffer_Grow
- sslBuffer_Append
- ssl_SaveWriteData
- ssl_SendSavedWriteData
- DoRecv
- NSS_FindCertKEAType
- serverCAListShutdown
- serverCAListSetup
- ssl_ConfigSecureServer
- SSL_ConfigSecureServer
- SSL_ConfigSecureServerWithCertChain
- ssl_CreateSecurityInfo
- ssl_CopySecurityInfo
- ssl_ResetSecurityInfo
- ssl_DestroySecurityInfo
- ssl_SecureConnect
- ssl_SecureClose
- ssl_SecureShutdown
- ssl_SecureRecv
- ssl_SecureRead
- ssl_SecureSend
- ssl_SecureWrite
- SSL_BadCertHook
- SSL_SetURL
- SSL_SetTrustAnchors
- SSL_DataPending
- SSL_InvalidateSession
- ssl3_CacheSessionUnlocked
- SSL_CacheSession
- SSL_CacheSessionUnlocked
- SSL_GetSessionID
- SSL_CertDBHandleSet
- SSL_RestartHandshakeAfterCertReq
- SSL_RestartHandshakeAfterChannelIDReq
- SSL_RestartHandshakeAfterServerCert
- SSL_AuthCertificateComplete
- SSL_SNISocketConfigHook
#include "cert.h"
#include "secitem.h"
#include "keyhi.h"
#include "ssl.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "secoid.h"
#include "pk11func.h"
#include "nss.h"
#include "prinit.h"
#define MAX_BLOCK_CYPHER_SIZE 32
#define TEST_FOR_FAILURE
#define SET_ERROR_CODE
int
ssl_Do1stHandshake(sslSocket *ss)
{
int rv = SECSuccess;
int loopCount = 0;
do {
PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss));
PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss));
if (ss->handshake == 0) {
ss->handshake = ss->nextHandshake;
ss->nextHandshake = 0;
}
if (ss->handshake == 0) {
ss->handshake = ss->securityHandshake;
ss->securityHandshake = 0;
}
if (ss->handshake == 0) {
if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) {
ssl_GetRecvBufLock(ss);
ss->gs.recordLen = 0;
ssl_FinishHandshake(ss);
ssl_ReleaseRecvBufLock(ss);
}
break;
}
rv = (*ss->handshake)(ss);
++loopCount;
} while (rv != SECFailure);
PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss));
PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss));
if (rv == SECWouldBlock) {
PORT_SetError(PR_WOULD_BLOCK_ERROR);
rv = SECFailure;
}
return rv;
}
void
ssl_FinishHandshake(sslSocket *ss)
{
PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd));
ss->firstHsDone = PR_TRUE;
ss->enoughFirstHsDone = PR_TRUE;
ss->gs.writeOffset = 0;
ss->gs.readOffset = 0;
if (ss->handshakeCallback) {
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
}
}
static SECStatus
ssl3_AlwaysBlock(sslSocket *ss)
{
PORT_SetError(PR_WOULD_BLOCK_ERROR);
return SECWouldBlock;
}
void
ssl3_SetAlwaysBlock(sslSocket *ss)
{
if (!ss->firstHsDone) {
ss->handshake = ssl3_AlwaysBlock;
ss->nextHandshake = 0;
}
}
static SECStatus
ssl_SetTimeout(PRFileDesc *fd, PRIntervalTime timeout)
{
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SetTimeout", SSL_GETPID(), fd));
return SECFailure;
}
SSL_LOCK_READER(ss);
ss->rTimeout = timeout;
if (ss->opt.fdx) {
SSL_LOCK_WRITER(ss);
}
ss->wTimeout = timeout;
if (ss->opt.fdx) {
SSL_UNLOCK_WRITER(ss);
}
SSL_UNLOCK_READER(ss);
return SECSuccess;
}
SECStatus
SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
{
sslSocket *ss;
SECStatus status;
PRNetAddr addr;
ss = ssl_FindSocket(s);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in ResetHandshake", SSL_GETPID(), s));
return SECFailure;
}
if (!ss->opt.useSecurity)
return SECSuccess;
SSL_LOCK_READER(ss);
SSL_LOCK_WRITER(ss);
ssl_Get1stHandshakeLock(ss);
ss->firstHsDone = PR_FALSE;
ss->enoughFirstHsDone = PR_FALSE;
if ( asServer ) {
ss->handshake = ssl2_BeginServerHandshake;
ss->handshaking = sslHandshakingAsServer;
} else {
ss->handshake = ssl2_BeginClientHandshake;
ss->handshaking = sslHandshakingAsClient;
}
ss->nextHandshake = 0;
ss->securityHandshake = 0;
ssl_GetRecvBufLock(ss);
status = ssl_InitGather(&ss->gs);
ssl_ReleaseRecvBufLock(ss);
ssl_GetSSL3HandshakeLock(ss);
ss->ssl3.hs.canFalseStart = PR_FALSE;
ss->ssl3.hs.restartTarget = NULL;
ssl_GetXmitBufLock(ss);
ssl_ResetSecurityInfo(&ss->sec, PR_TRUE);
status = ssl_CreateSecurityInfo(ss);
ssl_ReleaseXmitBufLock(ss);
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
if (!ss->TCPconnected)
ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr));
SSL_UNLOCK_WRITER(ss);
SSL_UNLOCK_READER(ss);
return status;
}
SECStatus
SSL_ReHandshake(PRFileDesc *fd, PRBool flushCache)
{
sslSocket *ss;
SECStatus rv;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in RedoHandshake", SSL_GETPID(), fd));
return SECFailure;
}
if (!ss->opt.useSecurity)
return SECSuccess;
ssl_Get1stHandshakeLock(ss);
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
rv = SECFailure;
} else {
ssl_GetSSL3HandshakeLock(ss);
rv = ssl3_RedoHandshake(ss, flushCache);
ssl_ReleaseSSL3HandshakeLock(ss);
}
ssl_Release1stHandshakeLock(ss);
return rv;
}
SSL_IMPORT SECStatus SSL_ReHandshakeWithTimeout(PRFileDesc *fd,
PRBool flushCache,
PRIntervalTime timeout)
{
if (SECSuccess != ssl_SetTimeout(fd, timeout)) {
return SECFailure;
}
return SSL_ReHandshake(fd, flushCache);
}
SECStatus
SSL_RedoHandshake(PRFileDesc *fd)
{
return SSL_ReHandshake(fd, PR_TRUE);
}
SECStatus
SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb,
void *client_data)
{
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in HandshakeCallback",
SSL_GETPID(), fd));
return SECFailure;
}
if (!ss->opt.useSecurity) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
ss->handshakeCallback = cb;
ss->handshakeCallbackData = client_data;
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
return SECSuccess;
}
SECStatus
SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
void *arg)
{
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
SSL_GETPID(), fd));
return SECFailure;
}
if (!ss->opt.useSecurity) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
ss->canFalseStartCallback = cb;
ss->canFalseStartCallbackData = arg;
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
return SECSuccess;
}
SECStatus
SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
{
sslSocket *ss;
*canFalseStart = PR_FALSE;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart",
SSL_GETPID(), fd));
return SECFailure;
}
if (!ss->ssl3.initialized) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
return SECFailure;
}
*canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
return SECSuccess;
}
SECStatus
SSL_ForceHandshake(PRFileDesc *fd)
{
sslSocket *ss;
SECStatus rv = SECFailure;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in ForceHandshake",
SSL_GETPID(), fd));
return rv;
}
if (!ss->opt.useSecurity)
return SECSuccess;
if (!ssl_SocketIsBlocking(ss)) {
ssl_GetXmitBufLock(ss);
if (ss->pendingBuf.len != 0) {
int sent = ssl_SendSavedWriteData(ss);
if ((sent < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
ssl_ReleaseXmitBufLock(ss);
return SECFailure;
}
}
ssl_ReleaseXmitBufLock(ss);
}
ssl_Get1stHandshakeLock(ss);
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
int gatherResult;
ssl_GetRecvBufLock(ss);
gatherResult = ssl3_GatherCompleteHandshake(ss, 0);
ssl_ReleaseRecvBufLock(ss);
if (gatherResult > 0) {
rv = SECSuccess;
} else if (gatherResult == 0) {
PORT_SetError(PR_END_OF_FILE_ERROR);
} else if (gatherResult == SECWouldBlock) {
PORT_SetError(PR_WOULD_BLOCK_ERROR);
}
} else if (!ss->firstHsDone) {
rv = ssl_Do1stHandshake(ss);
} else {
rv = SECSuccess;
}
ssl_Release1stHandshakeLock(ss);
return rv;
}
SSL_IMPORT SECStatus SSL_ForceHandshakeWithTimeout(PRFileDesc *fd,
PRIntervalTime timeout)
{
if (SECSuccess != ssl_SetTimeout(fd, timeout)) {
return SECFailure;
}
return SSL_ForceHandshake(fd);
}
SECStatus
sslBuffer_Grow(sslBuffer *b, unsigned int newLen)
{
newLen = PR_MAX(newLen, MAX_FRAGMENT_LENGTH + 2048);
if (newLen > b->space) {
unsigned char *newBuf;
if (b->buf) {
newBuf = (unsigned char *) PORT_Realloc(b->buf, newLen);
} else {
newBuf = (unsigned char *) PORT_Alloc(newLen);
}
if (!newBuf) {
return SECFailure;
}
SSL_TRC(10, ("%d: SSL: grow buffer from %d to %d",
SSL_GETPID(), b->space, newLen));
b->buf = newBuf;
b->space = newLen;
}
return SECSuccess;
}
SECStatus
sslBuffer_Append(sslBuffer *b, const void * data, unsigned int len)
{
unsigned int newLen = b->len + len;
SECStatus rv;
rv = sslBuffer_Grow(b, newLen);
if (rv != SECSuccess)
return rv;
PORT_Memcpy(b->buf + b->len, data, len);
b->len += len;
return SECSuccess;
}
SECStatus
ssl_SaveWriteData(sslSocket *ss, const void *data, unsigned int len)
{
SECStatus rv;
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
rv = sslBuffer_Append(&ss->pendingBuf, data, len);
SSL_TRC(5, ("%d: SSL[%d]: saving %u bytes of data (%u total saved so far)",
SSL_GETPID(), ss->fd, len, ss->pendingBuf.len));
return rv;
}
int
ssl_SendSavedWriteData(sslSocket *ss)
{
int rv = 0;
PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) );
if (ss->pendingBuf.len != 0) {
SSL_TRC(5, ("%d: SSL[%d]: sending %d bytes of saved data",
SSL_GETPID(), ss->fd, ss->pendingBuf.len));
rv = ssl_DefSend(ss, ss->pendingBuf.buf, ss->pendingBuf.len, 0);
if (rv < 0) {
return rv;
}
ss->pendingBuf.len -= rv;
if (ss->pendingBuf.len > 0 && rv > 0) {
PORT_Memmove(ss->pendingBuf.buf, ss->pendingBuf.buf + rv,
ss->pendingBuf.len);
}
}
return rv;
}
static int
DoRecv(sslSocket *ss, unsigned char *out, int len, int flags)
{
int rv;
int amount;
int available;
ssl_Get1stHandshakeLock(ss);
ssl_GetRecvBufLock(ss);
available = ss->gs.writeOffset - ss->gs.readOffset;
if (available == 0) {
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
rv = ssl3_GatherAppDataRecord(ss, 0);
} else {
rv = ssl2_GatherRecord(ss, 0);
}
if (rv <= 0) {
if (rv == 0) {
SSL_TRC(10, ("%d: SSL[%d]: ssl_recv EOF",
SSL_GETPID(), ss->fd));
goto done;
}
if ((rv != SECWouldBlock) &&
(PR_GetError() != PR_WOULD_BLOCK_ERROR)) {
goto done;
}
} else {
}
available = ss->gs.writeOffset - ss->gs.readOffset;
if (available == 0) {
PORT_SetError(PR_WOULD_BLOCK_ERROR);
rv = SECFailure;
goto done;
}
SSL_TRC(30, ("%d: SSL[%d]: partial data ready, available=%d",
SSL_GETPID(), ss->fd, available));
}
amount = PR_MIN(len, available);
PORT_Memcpy(out, ss->gs.buf.buf + ss->gs.readOffset, amount);
if (!(flags & PR_MSG_PEEK)) {
ss->gs.readOffset += amount;
}
PORT_Assert(ss->gs.readOffset <= ss->gs.writeOffset);
rv = amount;
SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d",
SSL_GETPID(), ss->fd, amount, available));
PRINT_BUF(4, (ss, "DoRecv receiving plaintext:", out, amount));
done:
ssl_ReleaseRecvBufLock(ss);
ssl_Release1stHandshakeLock(ss);
return rv;
}
SSLKEAType
NSS_FindCertKEAType(CERTCertificate * cert)
{
SSLKEAType keaType = kt_null;
int tag;
if (!cert) goto loser;
tag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
switch (tag) {
case SEC_OID_X500_RSA_ENCRYPTION:
case SEC_OID_PKCS1_RSA_ENCRYPTION:
keaType = kt_rsa;
break;
case SEC_OID_X942_DIFFIE_HELMAN_KEY:
keaType = kt_dh;
break;
#ifdef NSS_ENABLE_ECC
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
keaType = kt_ecdh;
break;
#endif
default:
keaType = kt_null;
}
loser:
return keaType;
}
static const PRCallOnceType pristineCallOnce;
static PRCallOnceType setupServerCAListOnce;
static SECStatus serverCAListShutdown(void* appData, void* nssData)
{
PORT_Assert(ssl3_server_ca_list);
if (ssl3_server_ca_list) {
CERT_FreeDistNames(ssl3_server_ca_list);
ssl3_server_ca_list = NULL;
}
setupServerCAListOnce = pristineCallOnce;
return SECSuccess;
}
static PRStatus serverCAListSetup(void *arg)
{
CERTCertDBHandle *dbHandle = (CERTCertDBHandle *)arg;
SECStatus rv = NSS_RegisterShutdown(serverCAListShutdown, NULL);
PORT_Assert(SECSuccess == rv);
if (SECSuccess == rv) {
ssl3_server_ca_list = CERT_GetSSLCACerts(dbHandle);
return PR_SUCCESS;
}
return PR_FAILURE;
}
SECStatus
ssl_ConfigSecureServer(sslSocket *ss, CERTCertificate *cert,
const CERTCertificateList *certChain,
ssl3KeyPair *keyPair, SSLKEAType kea)
{
CERTCertificateList *localCertChain = NULL;
sslServerCerts *sc = ss->serverCerts + kea;
if (sc->serverCert != NULL) {
CERT_DestroyCertificate(sc->serverCert);
sc->serverCert = NULL;
sc->serverKeyBits = 0;
}
if (sc->serverCertChain != NULL) {
CERT_DestroyCertificateList(sc->serverCertChain);
sc->serverCertChain = NULL;
}
if (cert) {
sc->serverCert = CERT_DupCertificate(cert);
sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->pubKey);
if (!certChain) {
localCertChain =
CERT_CertChainFromCert(sc->serverCert, certUsageSSLServer,
PR_TRUE);
if (!localCertChain)
goto loser;
}
sc->serverCertChain = (certChain) ? CERT_DupCertList(certChain) :
localCertChain;
if (!sc->serverCertChain) {
goto loser;
}
localCertChain = NULL;
}
if (sc->serverKeyPair != NULL) {
ssl3_FreeKeyPair(sc->serverKeyPair);
sc->serverKeyPair = NULL;
}
if (keyPair) {
SECKEY_CacheStaticFlags(keyPair->privKey);
sc->serverKeyPair = ssl3_GetKeyPairRef(keyPair);
}
if (kea == kt_rsa && cert && sc->serverKeyBits > 512 &&
!ss->opt.noStepDown && !ss->stepDownKeyPair) {
if (ssl3_CreateRSAStepDownKeys(ss) != SECSuccess) {
goto loser;
}
}
return SECSuccess;
loser:
if (localCertChain) {
CERT_DestroyCertificateList(localCertChain);
}
if (sc->serverCert != NULL) {
CERT_DestroyCertificate(sc->serverCert);
sc->serverCert = NULL;
}
if (sc->serverCertChain != NULL) {
CERT_DestroyCertificateList(sc->serverCertChain);
sc->serverCertChain = NULL;
}
if (sc->serverKeyPair != NULL) {
ssl3_FreeKeyPair(sc->serverKeyPair);
sc->serverKeyPair = NULL;
}
return SECFailure;
}
SECStatus
SSL_ConfigSecureServer(PRFileDesc *fd, CERTCertificate *cert,
SECKEYPrivateKey *key, SSL3KEAType kea)
{
return SSL_ConfigSecureServerWithCertChain(fd, cert, NULL, key, kea);
}
SECStatus
SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert,
const CERTCertificateList *certChainOpt,
SECKEYPrivateKey *key, SSL3KEAType kea)
{
sslSocket *ss;
SECKEYPublicKey *pubKey = NULL;
ssl3KeyPair *keyPair = NULL;
SECStatus rv = SECFailure;
ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
if (!cert != !key) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if ((kea >= kt_kea_size) || (kea < kt_null)) {
PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
return SECFailure;
}
if (kea != NSS_FindCertKEAType(cert)) {
PORT_SetError(SSL_ERROR_CERT_KEA_MISMATCH);
return SECFailure;
}
if (cert) {
pubKey = CERT_ExtractPublicKey(cert);
if (!pubKey)
return SECFailure;
}
if (key) {
SECKEYPrivateKey * keyCopy = NULL;
CK_MECHANISM_TYPE keyMech = CKM_INVALID_MECHANISM;
if (key->pkcs11Slot) {
PK11SlotInfo * bestSlot;
bestSlot = PK11_ReferenceSlot(key->pkcs11Slot);
if (bestSlot) {
keyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key);
PK11_FreeSlot(bestSlot);
}
}
if (keyCopy == NULL)
keyMech = PK11_MapSignKeyType(key->keyType);
if (keyMech != CKM_INVALID_MECHANISM) {
PK11SlotInfo * bestSlot;
bestSlot = PK11_GetBestSlot(keyMech, NULL );
if (bestSlot) {
keyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key);
PK11_FreeSlot(bestSlot);
}
}
if (keyCopy == NULL)
keyCopy = SECKEY_CopyPrivateKey(key);
if (keyCopy == NULL)
goto loser;
keyPair = ssl3_NewKeyPair(keyCopy, pubKey);
if (keyPair == NULL) {
SECKEY_DestroyPrivateKey(keyCopy);
goto loser;
}
pubKey = NULL;
}
if (ssl_ConfigSecureServer(ss, cert, certChainOpt,
keyPair, kea) == SECFailure) {
goto loser;
}
if (PR_SUCCESS == PR_CallOnceWithArg(&setupServerCAListOnce,
&serverCAListSetup,
(void *)(ss->dbHandle))) {
rv = SECSuccess;
}
loser:
if (keyPair) {
ssl3_FreeKeyPair(keyPair);
}
if (pubKey) {
SECKEY_DestroyPublicKey(pubKey);
pubKey = NULL;
}
return rv;
}
SECStatus
ssl_CreateSecurityInfo(sslSocket *ss)
{
SECStatus status;
ssl2_UseClearSendFunc(ss);
ss->sec.blockSize = 1;
ss->sec.blockShift = 0;
ssl_GetXmitBufLock(ss);
status = sslBuffer_Grow(&ss->sec.writeBuf, 4096);
ssl_ReleaseXmitBufLock(ss);
return status;
}
SECStatus
ssl_CopySecurityInfo(sslSocket *ss, sslSocket *os)
{
ss->sec.send = os->sec.send;
ss->sec.isServer = os->sec.isServer;
ss->sec.keyBits = os->sec.keyBits;
ss->sec.secretKeyBits = os->sec.secretKeyBits;
ss->sec.peerCert = CERT_DupCertificate(os->sec.peerCert);
if (os->sec.peerCert && !ss->sec.peerCert)
goto loser;
ss->sec.cache = os->sec.cache;
ss->sec.uncache = os->sec.uncache;
ss->sec.sendSequence = os->sec.sendSequence;
ss->sec.rcvSequence = os->sec.rcvSequence;
if (os->sec.hash && os->sec.hashcx) {
ss->sec.hash = os->sec.hash;
ss->sec.hashcx = os->sec.hash->clone(os->sec.hashcx);
if (os->sec.hashcx && !ss->sec.hashcx)
goto loser;
} else {
ss->sec.hash = NULL;
ss->sec.hashcx = NULL;
}
SECITEM_CopyItem(0, &ss->sec.sendSecret, &os->sec.sendSecret);
if (os->sec.sendSecret.data && !ss->sec.sendSecret.data)
goto loser;
SECITEM_CopyItem(0, &ss->sec.rcvSecret, &os->sec.rcvSecret);
if (os->sec.rcvSecret.data && !ss->sec.rcvSecret.data)
goto loser;
PORT_Assert(os->sec.readcx == 0);
PORT_Assert(os->sec.writecx == 0);
ss->sec.readcx = os->sec.readcx;
ss->sec.writecx = os->sec.writecx;
ss->sec.destroy = 0;
ss->sec.enc = os->sec.enc;
ss->sec.dec = os->sec.dec;
ss->sec.blockShift = os->sec.blockShift;
ss->sec.blockSize = os->sec.blockSize;
return SECSuccess;
loser:
return SECFailure;
}
void
ssl_ResetSecurityInfo(sslSecurityInfo *sec, PRBool doMemset)
{
if (sec->hash && sec->hashcx) {
(*sec->hash->destroy)(sec->hashcx, PR_TRUE);
sec->hashcx = NULL;
sec->hash = NULL;
}
SECITEM_ZfreeItem(&sec->sendSecret, PR_FALSE);
SECITEM_ZfreeItem(&sec->rcvSecret, PR_FALSE);
if (sec->destroy) {
(*sec->destroy)(sec->readcx, PR_TRUE);
(*sec->destroy)(sec->writecx, PR_TRUE);
sec->readcx = NULL;
sec->writecx = NULL;
} else {
PORT_Assert(sec->readcx == 0);
PORT_Assert(sec->writecx == 0);
}
sec->readcx = 0;
sec->writecx = 0;
if (sec->localCert) {
CERT_DestroyCertificate(sec->localCert);
sec->localCert = NULL;
}
if (sec->peerCert) {
CERT_DestroyCertificate(sec->peerCert);
sec->peerCert = NULL;
}
if (sec->peerKey) {
SECKEY_DestroyPublicKey(sec->peerKey);
sec->peerKey = NULL;
}
if (sec->ci.sid != NULL) {
ssl_FreeSID(sec->ci.sid);
}
PORT_ZFree(sec->ci.sendBuf.buf, sec->ci.sendBuf.space);
if (doMemset) {
memset(&sec->ci, 0, sizeof sec->ci);
}
}
void
ssl_DestroySecurityInfo(sslSecurityInfo *sec)
{
ssl_ResetSecurityInfo(sec, PR_FALSE);
PORT_ZFree(sec->writeBuf.buf, sec->writeBuf.space);
sec->writeBuf.buf = 0;
memset(sec, 0, sizeof *sec);
}
int
ssl_SecureConnect(sslSocket *ss, const PRNetAddr *sa)
{
PRFileDesc *osfd = ss->fd->lower;
int rv;
if ( ss->opt.handshakeAsServer ) {
ss->securityHandshake = ssl2_BeginServerHandshake;
ss->handshaking = sslHandshakingAsServer;
} else {
ss->securityHandshake = ssl2_BeginClientHandshake;
ss->handshaking = sslHandshakingAsClient;
}
rv = osfd->methods->connect(osfd, sa, ss->cTimeout);
if (rv == PR_SUCCESS) {
ss->TCPconnected = 1;
} else {
int err = PR_GetError();
SSL_DBG(("%d: SSL[%d]: connect failed, errno=%d",
SSL_GETPID(), ss->fd, err));
if (err == PR_IS_CONNECTED_ERROR) {
ss->TCPconnected = 1;
}
}
SSL_TRC(5, ("%d: SSL[%d]: secure connect completed, rv == %d",
SSL_GETPID(), ss->fd, rv));
return rv;
}
int
ssl_SecureClose(sslSocket *ss)
{
int rv;
if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
!(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
ss->firstHsDone &&
!ss->recvdCloseNotify &&
ss->ssl3.initialized) {
if (!ss->delayDisabled) {
ssl_EnableNagleDelay(ss, PR_FALSE);
ss->delayDisabled = 1;
}
(void) SSL3_SendAlert(ss, alert_warning, close_notify);
}
rv = ssl_DefClose(ss);
return rv;
}
int
ssl_SecureShutdown(sslSocket *ss, int nsprHow)
{
PRFileDesc *osfd = ss->fd->lower;
int rv;
PRIntn sslHow = nsprHow + 1;
if ((unsigned)nsprHow > PR_SHUTDOWN_BOTH) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return PR_FAILURE;
}
if ((sslHow & ssl_SHUTDOWN_SEND) != 0 &&
ss->version >= SSL_LIBRARY_VERSION_3_0 &&
!(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
ss->firstHsDone &&
!ss->recvdCloseNotify &&
ss->ssl3.initialized) {
(void) SSL3_SendAlert(ss, alert_warning, close_notify);
}
rv = osfd->methods->shutdown(osfd, nsprHow);
ss->shutdownHow |= sslHow;
return rv;
}
int
ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags)
{
sslSecurityInfo *sec;
int rv = 0;
sec = &ss->sec;
if (ss->shutdownHow & ssl_SHUTDOWN_RCV) {
PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR);
return PR_FAILURE;
}
if (flags & ~PR_MSG_PEEK) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return PR_FAILURE;
}
if (!ssl_SocketIsBlocking(ss) && !ss->opt.fdx) {
ssl_GetXmitBufLock(ss);
if (ss->pendingBuf.len != 0) {
rv = ssl_SendSavedWriteData(ss);
if ((rv < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
ssl_ReleaseXmitBufLock(ss);
return SECFailure;
}
}
ssl_ReleaseXmitBufLock(ss);
}
rv = 0;
if (!ss->firstHsDone) {
ssl_Get1stHandshakeLock(ss);
if (ss->handshake || ss->nextHandshake || ss->securityHandshake) {
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
}
if (rv < 0) {
return rv;
}
if (len == 0) return 0;
rv = DoRecv(ss, (unsigned char*) buf, len, flags);
SSL_TRC(2, ("%d: SSL[%d]: recving %d bytes securely (errno=%d)",
SSL_GETPID(), ss->fd, rv, PORT_GetError()));
return rv;
}
int
ssl_SecureRead(sslSocket *ss, unsigned char *buf, int len)
{
return ssl_SecureRecv(ss, buf, len, 0);
}
int
ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
{
int rv = 0;
SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes",
SSL_GETPID(), ss->fd, len));
if (ss->shutdownHow & ssl_SHUTDOWN_SEND) {
PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR);
rv = PR_FAILURE;
goto done;
}
if (flags) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
rv = PR_FAILURE;
goto done;
}
ssl_GetXmitBufLock(ss);
if (ss->pendingBuf.len != 0) {
PORT_Assert(ss->pendingBuf.len > 0);
rv = ssl_SendSavedWriteData(ss);
if (rv >= 0 && ss->pendingBuf.len != 0) {
PORT_Assert(ss->pendingBuf.len > 0);
PORT_SetError(PR_WOULD_BLOCK_ERROR);
rv = SECFailure;
}
}
ssl_ReleaseXmitBufLock(ss);
if (rv < 0) {
goto done;
}
if (len > 0)
ss->writerThread = PR_GetCurrentThread();
if (!ss->firstHsDone) {
PRBool falseStart = PR_FALSE;
ssl_Get1stHandshakeLock(ss);
if (ss->opt.enableFalseStart &&
ss->version >= SSL_LIBRARY_VERSION_3_0) {
ssl_GetSSL3HandshakeLock(ss);
falseStart = ss->ssl3.hs.canFalseStart;
ssl_ReleaseSSL3HandshakeLock(ss);
}
if (!falseStart &&
(ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
rv = ssl_Do1stHandshake(ss);
}
ssl_Release1stHandshakeLock(ss);
}
if (rv < 0) {
ss->writerThread = NULL;
goto done;
}
if (len == 0) {
rv = 0;
goto done;
}
PORT_Assert(buf != NULL);
if (!buf) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
rv = PR_FAILURE;
goto done;
}
if (!ss->firstHsDone) {
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_3_0);
#ifdef DEBUG
ssl_GetSSL3HandshakeLock(ss);
PORT_Assert(ss->ssl3.hs.canFalseStart);
ssl_ReleaseSSL3HandshakeLock(ss);
#endif
SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
SSL_GETPID(), ss->fd));
}
ssl_GetXmitBufLock(ss);
rv = (*ss->sec.send)(ss, buf, len, flags);
ssl_ReleaseXmitBufLock(ss);
ss->writerThread = NULL;
done:
if (rv < 0) {
SSL_TRC(2, ("%d: SSL[%d]: SecureSend: returning %d count, error %d",
SSL_GETPID(), ss->fd, rv, PORT_GetError()));
} else {
SSL_TRC(2, ("%d: SSL[%d]: SecureSend: returning %d count",
SSL_GETPID(), ss->fd, rv));
}
return rv;
}
int
ssl_SecureWrite(sslSocket *ss, const unsigned char *buf, int len)
{
return ssl_SecureSend(ss, buf, len, 0);
}
SECStatus
SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, void *arg)
{
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSLBadCertHook",
SSL_GETPID(), fd));
return SECFailure;
}
ss->handleBadCert = f;
ss->badCertArg = arg;
return SECSuccess;
}
SECStatus
SSL_SetURL(PRFileDesc *fd, const char *url)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus rv = SECSuccess;
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSLSetURL",
SSL_GETPID(), fd));
return SECFailure;
}
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
if ( ss->url ) {
PORT_Free((void *)ss->url);
}
ss->url = (const char *)PORT_Strdup(url);
if ( ss->url == NULL ) {
rv = SECFailure;
}
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
return rv;
}
SECStatus
SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
{
sslSocket * ss = ssl_FindSocket(fd);
CERTDistNames *names = NULL;
if (!certList) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetTrustAnchors",
SSL_GETPID(), fd));
return SECFailure;
}
names = CERT_DistNamesFromCertList(certList);
if (names == NULL) {
return SECFailure;
}
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
if (ss->ssl3.ca_list) {
CERT_FreeDistNames(ss->ssl3.ca_list);
}
ss->ssl3.ca_list = names;
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
return SECSuccess;
}
int
SSL_DataPending(PRFileDesc *fd)
{
sslSocket *ss;
int rv = 0;
ss = ssl_FindSocket(fd);
if (ss && ss->opt.useSecurity) {
ssl_GetRecvBufLock(ss);
rv = ss->gs.writeOffset - ss->gs.readOffset;
ssl_ReleaseRecvBufLock(ss);
}
return rv;
}
SECStatus
SSL_InvalidateSession(PRFileDesc *fd)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus rv = SECFailure;
if (ss) {
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
if (ss->sec.ci.sid && ss->sec.uncache) {
ss->sec.uncache(ss->sec.ci.sid);
rv = SECSuccess;
}
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
}
return rv;
}
static void
ssl3_CacheSessionUnlocked(sslSocket *ss)
{
PORT_Assert(!ss->sec.isServer);
if (ss->ssl3.hs.cacheSID) {
ss->sec.cache(ss->sec.ci.sid);
ss->ssl3.hs.cacheSID = PR_FALSE;
}
}
SECStatus
SSL_CacheSession(PRFileDesc *fd)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus rv = SECFailure;
if (ss) {
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
ssl3_CacheSessionUnlocked(ss);
rv = SECSuccess;
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
}
return rv;
}
SECStatus
SSL_CacheSessionUnlocked(PRFileDesc *fd)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus rv = SECFailure;
if (ss) {
ssl3_CacheSessionUnlocked(ss);
rv = SECSuccess;
}
return rv;
}
SECItem *
SSL_GetSessionID(PRFileDesc *fd)
{
sslSocket * ss;
SECItem * item = NULL;
ss = ssl_FindSocket(fd);
if (ss) {
ssl_Get1stHandshakeLock(ss);
ssl_GetSSL3HandshakeLock(ss);
if (ss->opt.useSecurity && ss->firstHsDone && ss->sec.ci.sid) {
item = (SECItem *)PORT_Alloc(sizeof(SECItem));
if (item) {
sslSessionID * sid = ss->sec.ci.sid;
if (sid->version < SSL_LIBRARY_VERSION_3_0) {
item->len = SSL2_SESSIONID_BYTES;
item->data = (unsigned char*)PORT_Alloc(item->len);
PORT_Memcpy(item->data, sid->u.ssl2.sessionID, item->len);
} else {
item->len = sid->u.ssl3.sessionIDLength;
item->data = (unsigned char*)PORT_Alloc(item->len);
PORT_Memcpy(item->data, sid->u.ssl3.sessionID, item->len);
}
}
}
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
}
return item;
}
SECStatus
SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle)
{
sslSocket * ss;
ss = ssl_FindSocket(fd);
if (!ss)
return SECFailure;
if (!dbHandle) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
ss->dbHandle = dbHandle;
return SECSuccess;
}
SECStatus
SSL_RestartHandshakeAfterCertReq(PRFileDesc * fd,
CERTCertificate * cert,
SECKEYPrivateKey * key,
CERTCertificateList *certChain)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus ret;
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterCertReq",
SSL_GETPID(), fd));
if (cert) {
CERT_DestroyCertificate(cert);
}
if (key) {
SECKEY_DestroyPrivateKey(key);
}
if (certChain) {
CERT_DestroyCertificateList(certChain);
}
return SECFailure;
}
ssl_Get1stHandshakeLock(ss);
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
} else {
if (certChain != NULL) {
CERT_DestroyCertificateList(certChain);
}
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
ret = SECFailure;
}
ssl_Release1stHandshakeLock(ss);
return ret;
}
SECStatus
SSL_RestartHandshakeAfterChannelIDReq(PRFileDesc * fd,
SECKEYPublicKey * channelIDPub,
SECKEYPrivateKey *channelID)
{
sslSocket * ss = ssl_FindSocket(fd);
SECStatus ret;
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in"
" SSL_RestartHandshakeAfterChannelIDReq",
SSL_GETPID(), fd));
goto loser;
}
ssl_Get1stHandshakeLock(ss);
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
ssl_Release1stHandshakeLock(ss);
goto loser;
}
ret = ssl3_RestartHandshakeAfterChannelIDReq(ss, channelIDPub,
channelID);
ssl_Release1stHandshakeLock(ss);
return ret;
loser:
SECKEY_DestroyPublicKey(channelIDPub);
SECKEY_DestroyPrivateKey(channelID);
return SECFailure;
}
int
SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
{
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return -1;
}
SECStatus
SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error)
{
SECStatus rv;
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_AuthCertificateComplete",
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 {
rv = ssl3_AuthCertificateComplete(ss, error);
}
ssl_Release1stHandshakeLock(ss);
return rv;
}
SECStatus
SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
void *arg)
{
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
SSL_DBG(("%d: SSL[%d]: bad socket in SNISocketConfigHook",
SSL_GETPID(), fd));
return SECFailure;
}
ss->sniSocketConfig = func;
ss->sniSocketConfigArg = arg;
return SECSuccess;
}