mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-10 18:00:15 +01:00
44b7f056d9
bug1001332, 56b691c003ad, bug1086145, bug1054069, bug1155922, bug991783, bug1125025, bug1162521, bug1162644, bug1132941, bug1164364, bug1166205, bug1166163, bug1166515, bug1138554, bug1167046, bug1167043, bug1169451, bug1172128, bug1170322, bug102794, bug1128184, bug557830, bug1174648, bug1180244, bug1177784, bug1173413, bug1169174, bug1084669, bug951455, bug1183395, bug1177430, bug1183827, bug1160139, bug1154106, bug1142209, bug1185033, bug1193467, bug1182667(with sha512 changes backed out, which breaks VC6 compilation), bug1158489, bug337796
3250 lines
96 KiB
C
3250 lines
96 KiB
C
/*
|
|
* vtables (and methods that call through them) for the 4 types of
|
|
* SSLSockets supported. Only one type is still supported.
|
|
* Various other functions.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include "seccomon.h"
|
|
#include "cert.h"
|
|
#include "keyhi.h"
|
|
#include "ssl.h"
|
|
#include "sslimpl.h"
|
|
#include "sslproto.h"
|
|
#include "nspr.h"
|
|
#include "private/pprio.h"
|
|
#ifndef NO_PKCS11_BYPASS
|
|
#include "blapi.h"
|
|
#endif
|
|
#include "nss.h"
|
|
#include "pk11pqg.h"
|
|
|
|
#define SET_ERROR_CODE /* reminder */
|
|
|
|
static const sslSocketOps ssl_default_ops = { /* No SSL. */
|
|
ssl_DefConnect,
|
|
NULL,
|
|
ssl_DefBind,
|
|
ssl_DefListen,
|
|
ssl_DefShutdown,
|
|
ssl_DefClose,
|
|
ssl_DefRecv,
|
|
ssl_DefSend,
|
|
ssl_DefRead,
|
|
ssl_DefWrite,
|
|
ssl_DefGetpeername,
|
|
ssl_DefGetsockname
|
|
};
|
|
|
|
static const sslSocketOps ssl_secure_ops = { /* SSL. */
|
|
ssl_SecureConnect,
|
|
NULL,
|
|
ssl_DefBind,
|
|
ssl_DefListen,
|
|
ssl_SecureShutdown,
|
|
ssl_SecureClose,
|
|
ssl_SecureRecv,
|
|
ssl_SecureSend,
|
|
ssl_SecureRead,
|
|
ssl_SecureWrite,
|
|
ssl_DefGetpeername,
|
|
ssl_DefGetsockname
|
|
};
|
|
|
|
/*
|
|
** default settings for socket enables
|
|
*/
|
|
static sslOptions ssl_defaults = {
|
|
{ siBuffer, NULL, 0 }, /* nextProtoNego */
|
|
PR_TRUE, /* useSecurity */
|
|
PR_FALSE, /* useSocks */
|
|
PR_FALSE, /* requestCertificate */
|
|
2, /* requireCertificate */
|
|
PR_FALSE, /* handshakeAsClient */
|
|
PR_FALSE, /* handshakeAsServer */
|
|
PR_FALSE, /* enableSSL2 */ /* now defaults to off in NSS 3.13 */
|
|
PR_FALSE, /* unusedBit9 */
|
|
PR_FALSE, /* unusedBit10 */
|
|
PR_FALSE, /* noCache */
|
|
PR_FALSE, /* fdx */
|
|
PR_FALSE, /* v2CompatibleHello */ /* now defaults to off in NSS 3.13 */
|
|
PR_TRUE, /* detectRollBack */
|
|
PR_FALSE, /* noStepDown */
|
|
PR_FALSE, /* bypassPKCS11 */
|
|
PR_FALSE, /* noLocks */
|
|
PR_FALSE, /* enableSessionTickets */
|
|
PR_FALSE, /* enableDeflate */
|
|
2, /* enableRenegotiation (default: requires extension) */
|
|
PR_FALSE, /* requireSafeNegotiation */
|
|
PR_FALSE, /* enableFalseStart */
|
|
PR_TRUE, /* cbcRandomIV */
|
|
PR_FALSE, /* enableOCSPStapling */
|
|
PR_TRUE, /* enableNPN */
|
|
PR_FALSE, /* enableALPN */
|
|
PR_TRUE, /* reuseServerECDHEKey */
|
|
PR_FALSE, /* enableFallbackSCSV */
|
|
PR_TRUE, /* enableServerDhe */
|
|
};
|
|
|
|
/*
|
|
* default range of enabled SSL/TLS protocols
|
|
*/
|
|
static SSLVersionRange versions_defaults_stream = {
|
|
SSL_LIBRARY_VERSION_3_0,
|
|
SSL_LIBRARY_VERSION_TLS_1_2
|
|
};
|
|
|
|
static SSLVersionRange versions_defaults_datagram = {
|
|
SSL_LIBRARY_VERSION_TLS_1_1,
|
|
SSL_LIBRARY_VERSION_TLS_1_2
|
|
};
|
|
|
|
#define VERSIONS_DEFAULTS(variant) \
|
|
(variant == ssl_variant_stream ? &versions_defaults_stream : \
|
|
&versions_defaults_datagram)
|
|
|
|
sslSessionIDLookupFunc ssl_sid_lookup;
|
|
sslSessionIDCacheFunc ssl_sid_cache;
|
|
sslSessionIDUncacheFunc ssl_sid_uncache;
|
|
|
|
static PRBool ssl_inited = PR_FALSE;
|
|
static PRDescIdentity ssl_layer_id;
|
|
|
|
PRBool locksEverDisabled; /* implicitly PR_FALSE */
|
|
PRBool ssl_force_locks; /* implicitly PR_FALSE */
|
|
int ssl_lock_readers = 1; /* default true. */
|
|
char ssl_debug;
|
|
char ssl_trace;
|
|
FILE * ssl_trace_iob;
|
|
FILE * ssl_keylog_iob;
|
|
char lockStatus[] = "Locks are ENABLED. ";
|
|
#define LOCKSTATUS_OFFSET 10 /* offset of ENABLED */
|
|
|
|
/* SRTP_NULL_HMAC_SHA1_80 and SRTP_NULL_HMAC_SHA1_32 are not implemented. */
|
|
static const PRUint16 srtpCiphers[] = {
|
|
SRTP_AES128_CM_HMAC_SHA1_80,
|
|
SRTP_AES128_CM_HMAC_SHA1_32,
|
|
0
|
|
};
|
|
|
|
/* forward declarations. */
|
|
static sslSocket *ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant variant);
|
|
static SECStatus ssl_MakeLocks(sslSocket *ss);
|
|
static void ssl_SetDefaultsFromEnvironment(void);
|
|
static PRStatus ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack,
|
|
PRDescIdentity id);
|
|
|
|
/************************************************************************/
|
|
|
|
/*
|
|
** Lookup a socket structure from a file descriptor.
|
|
** Only functions called through the PRIOMethods table should use this.
|
|
** Other app-callable functions should use ssl_FindSocket.
|
|
*/
|
|
static sslSocket *
|
|
ssl_GetPrivate(PRFileDesc *fd)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
PORT_Assert(fd != NULL);
|
|
PORT_Assert(fd->methods->file_type == PR_DESC_LAYERED);
|
|
PORT_Assert(fd->identity == ssl_layer_id);
|
|
|
|
if (fd->methods->file_type != PR_DESC_LAYERED ||
|
|
fd->identity != ssl_layer_id) {
|
|
PORT_SetError(PR_BAD_DESCRIPTOR_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
ss = (sslSocket *)fd->secret;
|
|
/* Set ss->fd lazily. We can't rely on the value of ss->fd set by
|
|
* ssl_PushIOLayer because another PR_PushIOLayer call will switch the
|
|
* contents of the PRFileDesc pointed by ss->fd and the new layer.
|
|
* See bug 807250.
|
|
*/
|
|
ss->fd = fd;
|
|
return ss;
|
|
}
|
|
|
|
/* This function tries to find the SSL layer in the stack.
|
|
* It searches for the first SSL layer at or below the argument fd,
|
|
* and failing that, it searches for the nearest SSL layer above the
|
|
* argument fd. It returns the private sslSocket from the found layer.
|
|
*/
|
|
sslSocket *
|
|
ssl_FindSocket(PRFileDesc *fd)
|
|
{
|
|
PRFileDesc *layer;
|
|
sslSocket *ss;
|
|
|
|
PORT_Assert(fd != NULL);
|
|
PORT_Assert(ssl_layer_id != 0);
|
|
|
|
layer = PR_GetIdentitiesLayer(fd, ssl_layer_id);
|
|
if (layer == NULL) {
|
|
PORT_SetError(PR_BAD_DESCRIPTOR_ERROR);
|
|
return NULL;
|
|
}
|
|
|
|
ss = (sslSocket *)layer->secret;
|
|
/* Set ss->fd lazily. We can't rely on the value of ss->fd set by
|
|
* ssl_PushIOLayer because another PR_PushIOLayer call will switch the
|
|
* contents of the PRFileDesc pointed by ss->fd and the new layer.
|
|
* See bug 807250.
|
|
*/
|
|
ss->fd = layer;
|
|
return ss;
|
|
}
|
|
|
|
static sslSocket *
|
|
ssl_DupSocket(sslSocket *os)
|
|
{
|
|
sslSocket *ss;
|
|
SECStatus rv;
|
|
|
|
ss = ssl_NewSocket((PRBool)(!os->opt.noLocks), os->protocolVariant);
|
|
if (ss) {
|
|
ss->opt = os->opt;
|
|
ss->opt.useSocks = PR_FALSE;
|
|
ss->vrange = os->vrange;
|
|
|
|
ss->peerID = !os->peerID ? NULL : PORT_Strdup(os->peerID);
|
|
ss->url = !os->url ? NULL : PORT_Strdup(os->url);
|
|
|
|
ss->ops = os->ops;
|
|
ss->rTimeout = os->rTimeout;
|
|
ss->wTimeout = os->wTimeout;
|
|
ss->cTimeout = os->cTimeout;
|
|
ss->dbHandle = os->dbHandle;
|
|
|
|
/* copy ssl2&3 policy & prefs, even if it's not selected (yet) */
|
|
ss->allowedByPolicy = os->allowedByPolicy;
|
|
ss->maybeAllowedByPolicy= os->maybeAllowedByPolicy;
|
|
ss->chosenPreference = os->chosenPreference;
|
|
PORT_Memcpy(ss->cipherSuites, os->cipherSuites, sizeof os->cipherSuites);
|
|
PORT_Memcpy(ss->ssl3.dtlsSRTPCiphers, os->ssl3.dtlsSRTPCiphers,
|
|
sizeof(PRUint16) * os->ssl3.dtlsSRTPCipherCount);
|
|
ss->ssl3.dtlsSRTPCipherCount = os->ssl3.dtlsSRTPCipherCount;
|
|
PORT_Memcpy(ss->ssl3.signatureAlgorithms, os->ssl3.signatureAlgorithms,
|
|
sizeof(ss->ssl3.signatureAlgorithms[0]) *
|
|
os->ssl3.signatureAlgorithmCount);
|
|
ss->ssl3.signatureAlgorithmCount = os->ssl3.signatureAlgorithmCount;
|
|
|
|
ss->ssl3.dheWeakGroupEnabled = os->ssl3.dheWeakGroupEnabled;
|
|
ss->ssl3.numDHEGroups = os->ssl3.numDHEGroups;
|
|
if (os->ssl3.dheGroups) {
|
|
ss->ssl3.dheGroups = PORT_NewArray(SSLDHEGroupType,
|
|
os->ssl3.numDHEGroups);
|
|
if (!ss->ssl3.dheGroups) {
|
|
goto loser;
|
|
}
|
|
PORT_Memcpy(ss->ssl3.dheGroups, os->ssl3.dheGroups,
|
|
sizeof(SSLDHEGroupType) * os->ssl3.numDHEGroups);
|
|
} else {
|
|
ss->ssl3.dheGroups = NULL;
|
|
}
|
|
|
|
if (os->cipherSpecs) {
|
|
ss->cipherSpecs = (unsigned char*)PORT_Alloc(os->sizeCipherSpecs);
|
|
if (ss->cipherSpecs)
|
|
PORT_Memcpy(ss->cipherSpecs, os->cipherSpecs,
|
|
os->sizeCipherSpecs);
|
|
ss->sizeCipherSpecs = os->sizeCipherSpecs;
|
|
ss->preferredCipher = os->preferredCipher;
|
|
} else {
|
|
ss->cipherSpecs = NULL; /* produced lazily */
|
|
ss->sizeCipherSpecs = 0;
|
|
ss->preferredCipher = NULL;
|
|
}
|
|
if (ss->opt.useSecurity) {
|
|
/* This int should be SSLKEAType, but CC on Irix complains,
|
|
* during the for loop.
|
|
*/
|
|
int i;
|
|
sslServerCerts * oc = os->serverCerts;
|
|
sslServerCerts * sc = ss->serverCerts;
|
|
|
|
for (i=kt_null; i < kt_kea_size; i++, oc++, sc++) {
|
|
if (oc->serverCert && oc->serverCertChain) {
|
|
sc->serverCert = CERT_DupCertificate(oc->serverCert);
|
|
sc->serverCertChain = CERT_DupCertList(oc->serverCertChain);
|
|
if (!sc->serverCertChain)
|
|
goto loser;
|
|
} else {
|
|
sc->serverCert = NULL;
|
|
sc->serverCertChain = NULL;
|
|
}
|
|
sc->serverKeyPair = oc->serverKeyPair ?
|
|
ssl3_GetKeyPairRef(oc->serverKeyPair) : NULL;
|
|
if (oc->serverKeyPair && !sc->serverKeyPair)
|
|
goto loser;
|
|
sc->serverKeyBits = oc->serverKeyBits;
|
|
ss->certStatusArray[i] = !os->certStatusArray[i] ? NULL :
|
|
SECITEM_DupArray(NULL, os->certStatusArray[i]);
|
|
}
|
|
ss->stepDownKeyPair = !os->stepDownKeyPair ? NULL :
|
|
ssl3_GetKeyPairRef(os->stepDownKeyPair);
|
|
ss->ephemeralECDHKeyPair = !os->ephemeralECDHKeyPair ? NULL :
|
|
ssl3_GetKeyPairRef(os->ephemeralECDHKeyPair);
|
|
ss->dheKeyPair = !os->dheKeyPair ? NULL :
|
|
ssl3_GetKeyPairRef(os->dheKeyPair);
|
|
ss->dheParams = os->dheParams;
|
|
|
|
/*
|
|
* XXX the preceding CERT_ and SECKEY_ functions can fail and return NULL.
|
|
* XXX We should detect this, and not just march on with NULL pointers.
|
|
*/
|
|
ss->authCertificate = os->authCertificate;
|
|
ss->authCertificateArg = os->authCertificateArg;
|
|
ss->getClientAuthData = os->getClientAuthData;
|
|
ss->getClientAuthDataArg = os->getClientAuthDataArg;
|
|
ss->sniSocketConfig = os->sniSocketConfig;
|
|
ss->sniSocketConfigArg = os->sniSocketConfigArg;
|
|
ss->handleBadCert = os->handleBadCert;
|
|
ss->badCertArg = os->badCertArg;
|
|
ss->handshakeCallback = os->handshakeCallback;
|
|
ss->handshakeCallbackData = os->handshakeCallbackData;
|
|
ss->canFalseStartCallback = os->canFalseStartCallback;
|
|
ss->canFalseStartCallbackData = os->canFalseStartCallbackData;
|
|
ss->pkcs11PinArg = os->pkcs11PinArg;
|
|
|
|
/* Create security data */
|
|
rv = ssl_CopySecurityInfo(ss, os);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
}
|
|
return ss;
|
|
|
|
loser:
|
|
ssl_FreeSocket(ss);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
ssl_DestroyLocks(sslSocket *ss)
|
|
{
|
|
/* Destroy locks. */
|
|
if (ss->firstHandshakeLock) {
|
|
PZ_DestroyMonitor(ss->firstHandshakeLock);
|
|
ss->firstHandshakeLock = NULL;
|
|
}
|
|
if (ss->ssl3HandshakeLock) {
|
|
PZ_DestroyMonitor(ss->ssl3HandshakeLock);
|
|
ss->ssl3HandshakeLock = NULL;
|
|
}
|
|
if (ss->specLock) {
|
|
NSSRWLock_Destroy(ss->specLock);
|
|
ss->specLock = NULL;
|
|
}
|
|
|
|
if (ss->recvLock) {
|
|
PZ_DestroyLock(ss->recvLock);
|
|
ss->recvLock = NULL;
|
|
}
|
|
if (ss->sendLock) {
|
|
PZ_DestroyLock(ss->sendLock);
|
|
ss->sendLock = NULL;
|
|
}
|
|
if (ss->xmitBufLock) {
|
|
PZ_DestroyMonitor(ss->xmitBufLock);
|
|
ss->xmitBufLock = NULL;
|
|
}
|
|
if (ss->recvBufLock) {
|
|
PZ_DestroyMonitor(ss->recvBufLock);
|
|
ss->recvBufLock = NULL;
|
|
}
|
|
}
|
|
|
|
/* Caller holds any relevant locks */
|
|
static void
|
|
ssl_DestroySocketContents(sslSocket *ss)
|
|
{
|
|
/* "i" should be of type SSLKEAType, but CC on IRIX complains during
|
|
* the for loop.
|
|
*/
|
|
int i;
|
|
|
|
/* Free up socket */
|
|
ssl_DestroySecurityInfo(&ss->sec);
|
|
|
|
ssl3_DestroySSL3Info(ss);
|
|
|
|
PORT_Free(ss->saveBuf.buf);
|
|
PORT_Free(ss->pendingBuf.buf);
|
|
ssl_DestroyGather(&ss->gs);
|
|
|
|
if (ss->peerID != NULL)
|
|
PORT_Free(ss->peerID);
|
|
if (ss->url != NULL)
|
|
PORT_Free((void *)ss->url); /* CONST */
|
|
if (ss->cipherSpecs) {
|
|
PORT_Free(ss->cipherSpecs);
|
|
ss->cipherSpecs = NULL;
|
|
ss->sizeCipherSpecs = 0;
|
|
}
|
|
|
|
/* Clean up server configuration */
|
|
for (i=kt_null; i < kt_kea_size; i++) {
|
|
sslServerCerts * sc = ss->serverCerts + i;
|
|
if (sc->serverCert != NULL)
|
|
CERT_DestroyCertificate(sc->serverCert);
|
|
if (sc->serverCertChain != NULL)
|
|
CERT_DestroyCertificateList(sc->serverCertChain);
|
|
if (sc->serverKeyPair != NULL)
|
|
ssl3_FreeKeyPair(sc->serverKeyPair);
|
|
if (ss->certStatusArray[i] != NULL) {
|
|
SECITEM_FreeArray(ss->certStatusArray[i], PR_TRUE);
|
|
ss->certStatusArray[i] = NULL;
|
|
}
|
|
}
|
|
if (ss->stepDownKeyPair) {
|
|
ssl3_FreeKeyPair(ss->stepDownKeyPair);
|
|
ss->stepDownKeyPair = NULL;
|
|
}
|
|
if (ss->ephemeralECDHKeyPair) {
|
|
ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair);
|
|
ss->ephemeralECDHKeyPair = NULL;
|
|
}
|
|
if (ss->dheKeyPair) {
|
|
ssl3_FreeKeyPair(ss->dheKeyPair);
|
|
ss->dheKeyPair = NULL;
|
|
}
|
|
SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
|
|
if (ss->xtnData.sniNameArr) {
|
|
PORT_Free(ss->xtnData.sniNameArr);
|
|
ss->xtnData.sniNameArr = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* free an sslSocket struct, and all the stuff that hangs off of it
|
|
*/
|
|
void
|
|
ssl_FreeSocket(sslSocket *ss)
|
|
{
|
|
/* Get every lock you can imagine!
|
|
** Caller already holds these:
|
|
** SSL_LOCK_READER(ss);
|
|
** SSL_LOCK_WRITER(ss);
|
|
*/
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetRecvBufLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
ssl_GetXmitBufLock(ss);
|
|
ssl_GetSpecWriteLock(ss);
|
|
|
|
ssl_DestroySocketContents(ss);
|
|
|
|
/* Release all the locks acquired above. */
|
|
SSL_UNLOCK_READER(ss);
|
|
SSL_UNLOCK_WRITER(ss);
|
|
ssl_Release1stHandshakeLock(ss);
|
|
ssl_ReleaseRecvBufLock(ss);
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
ssl_ReleaseXmitBufLock(ss);
|
|
ssl_ReleaseSpecWriteLock(ss);
|
|
|
|
ssl_DestroyLocks(ss);
|
|
|
|
#ifdef DEBUG
|
|
PORT_Memset(ss, 0x1f, sizeof *ss);
|
|
#endif
|
|
PORT_Free(ss);
|
|
return;
|
|
}
|
|
|
|
/************************************************************************/
|
|
SECStatus
|
|
ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled)
|
|
{
|
|
PRFileDesc * osfd = ss->fd->lower;
|
|
SECStatus rv = SECFailure;
|
|
PRSocketOptionData opt;
|
|
|
|
opt.option = PR_SockOpt_NoDelay;
|
|
opt.value.no_delay = (PRBool)!enabled;
|
|
|
|
if (osfd->methods->setsocketoption) {
|
|
rv = (SECStatus) osfd->methods->setsocketoption(osfd, &opt);
|
|
} else {
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static void
|
|
ssl_ChooseOps(sslSocket *ss)
|
|
{
|
|
ss->ops = ss->opt.useSecurity ? &ssl_secure_ops : &ssl_default_ops;
|
|
}
|
|
|
|
/* Called from SSL_Enable (immediately below) */
|
|
static SECStatus
|
|
PrepareSocket(sslSocket *ss)
|
|
{
|
|
SECStatus rv = SECSuccess;
|
|
|
|
ssl_ChooseOps(ss);
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_Enable(PRFileDesc *fd, int which, PRBool on)
|
|
{
|
|
return SSL_OptionSet(fd, which, on);
|
|
}
|
|
|
|
#ifndef NO_PKCS11_BYPASS
|
|
static const PRCallOnceType pristineCallOnce;
|
|
static PRCallOnceType setupBypassOnce;
|
|
|
|
static SECStatus SSL_BypassShutdown(void* appData, void* nssData)
|
|
{
|
|
/* unload freeBL shared library from memory */
|
|
BL_Unload();
|
|
setupBypassOnce = pristineCallOnce;
|
|
return SECSuccess;
|
|
}
|
|
|
|
static PRStatus SSL_BypassRegisterShutdown(void)
|
|
{
|
|
SECStatus rv = NSS_RegisterShutdown(SSL_BypassShutdown, NULL);
|
|
PORT_Assert(SECSuccess == rv);
|
|
return SECSuccess == rv ? PR_SUCCESS : PR_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
static PRStatus SSL_BypassSetup(void)
|
|
{
|
|
#ifdef NO_PKCS11_BYPASS
|
|
/* Guarantee binary compatibility */
|
|
return PR_SUCCESS;
|
|
#else
|
|
return PR_CallOnce(&setupBypassOnce, &SSL_BypassRegisterShutdown);
|
|
#endif
|
|
}
|
|
|
|
/* Implements the semantics for SSL_OptionSet(SSL_ENABLE_TLS, on) described in
|
|
* ssl.h in the section "SSL version range setting API".
|
|
*/
|
|
static void
|
|
ssl_EnableTLS(SSLVersionRange *vrange, PRBool on)
|
|
{
|
|
if (SSL3_ALL_VERSIONS_DISABLED(vrange)) {
|
|
if (on) {
|
|
vrange->min = SSL_LIBRARY_VERSION_TLS_1_0;
|
|
vrange->max = SSL_LIBRARY_VERSION_TLS_1_0;
|
|
} /* else don't change anything */
|
|
return;
|
|
}
|
|
|
|
if (on) {
|
|
/* Expand the range of enabled version to include TLS 1.0 */
|
|
vrange->min = PR_MIN(vrange->min, SSL_LIBRARY_VERSION_TLS_1_0);
|
|
vrange->max = PR_MAX(vrange->max, SSL_LIBRARY_VERSION_TLS_1_0);
|
|
} else {
|
|
/* Disable all TLS versions, leaving only SSL 3.0 if it was enabled */
|
|
if (vrange->min == SSL_LIBRARY_VERSION_3_0) {
|
|
vrange->max = SSL_LIBRARY_VERSION_3_0;
|
|
} else {
|
|
/* Only TLS was enabled, so now no versions are. */
|
|
vrange->min = SSL_LIBRARY_VERSION_NONE;
|
|
vrange->max = SSL_LIBRARY_VERSION_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Implements the semantics for SSL_OptionSet(SSL_ENABLE_SSL3, on) described in
|
|
* ssl.h in the section "SSL version range setting API".
|
|
*/
|
|
static void
|
|
ssl_EnableSSL3(SSLVersionRange *vrange, PRBool on)
|
|
{
|
|
if (SSL3_ALL_VERSIONS_DISABLED(vrange)) {
|
|
if (on) {
|
|
vrange->min = SSL_LIBRARY_VERSION_3_0;
|
|
vrange->max = SSL_LIBRARY_VERSION_3_0;
|
|
} /* else don't change anything */
|
|
return;
|
|
}
|
|
|
|
if (on) {
|
|
/* Expand the range of enabled versions to include SSL 3.0. We know
|
|
* SSL 3.0 or some version of TLS is already enabled at this point, so
|
|
* we don't need to change vrange->max.
|
|
*/
|
|
vrange->min = SSL_LIBRARY_VERSION_3_0;
|
|
} else {
|
|
/* Disable SSL 3.0, leaving TLS unaffected. */
|
|
if (vrange->max > SSL_LIBRARY_VERSION_3_0) {
|
|
vrange->min = PR_MAX(vrange->min, SSL_LIBRARY_VERSION_TLS_1_0);
|
|
} else {
|
|
/* Only SSL 3.0 was enabled, so now no versions are. */
|
|
vrange->min = SSL_LIBRARY_VERSION_NONE;
|
|
vrange->max = SSL_LIBRARY_VERSION_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
SECStatus
|
|
SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRBool on)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
SECStatus rv = SECSuccess;
|
|
PRBool holdingLocks;
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in Enable", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
holdingLocks = (!ss->opt.noLocks);
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
|
|
switch (which) {
|
|
case SSL_SOCKS:
|
|
ss->opt.useSocks = PR_FALSE;
|
|
rv = PrepareSocket(ss);
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
break;
|
|
|
|
case SSL_SECURITY:
|
|
ss->opt.useSecurity = on;
|
|
rv = PrepareSocket(ss);
|
|
break;
|
|
|
|
case SSL_REQUEST_CERTIFICATE:
|
|
ss->opt.requestCertificate = on;
|
|
break;
|
|
|
|
case SSL_REQUIRE_CERTIFICATE:
|
|
ss->opt.requireCertificate = on;
|
|
break;
|
|
|
|
case SSL_HANDSHAKE_AS_CLIENT:
|
|
if ( ss->opt.handshakeAsServer && on ) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
ss->opt.handshakeAsClient = on;
|
|
break;
|
|
|
|
case SSL_HANDSHAKE_AS_SERVER:
|
|
if ( ss->opt.handshakeAsClient && on ) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
ss->opt.handshakeAsServer = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_TLS:
|
|
if (IS_DTLS(ss)) {
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure; /* not allowed */
|
|
}
|
|
break;
|
|
}
|
|
ssl_EnableTLS(&ss->vrange, on);
|
|
ss->preferredCipher = NULL;
|
|
if (ss->cipherSpecs) {
|
|
PORT_Free(ss->cipherSpecs);
|
|
ss->cipherSpecs = NULL;
|
|
ss->sizeCipherSpecs = 0;
|
|
}
|
|
break;
|
|
|
|
case SSL_ENABLE_SSL3:
|
|
if (IS_DTLS(ss)) {
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure; /* not allowed */
|
|
}
|
|
break;
|
|
}
|
|
ssl_EnableSSL3(&ss->vrange, on);
|
|
ss->preferredCipher = NULL;
|
|
if (ss->cipherSpecs) {
|
|
PORT_Free(ss->cipherSpecs);
|
|
ss->cipherSpecs = NULL;
|
|
ss->sizeCipherSpecs = 0;
|
|
}
|
|
break;
|
|
|
|
case SSL_ENABLE_SSL2:
|
|
if (IS_DTLS(ss)) {
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure; /* not allowed */
|
|
}
|
|
break;
|
|
}
|
|
ss->opt.enableSSL2 = on;
|
|
if (on) {
|
|
ss->opt.v2CompatibleHello = on;
|
|
}
|
|
ss->preferredCipher = NULL;
|
|
if (ss->cipherSpecs) {
|
|
PORT_Free(ss->cipherSpecs);
|
|
ss->cipherSpecs = NULL;
|
|
ss->sizeCipherSpecs = 0;
|
|
}
|
|
break;
|
|
|
|
case SSL_NO_CACHE:
|
|
ss->opt.noCache = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FDX:
|
|
if (on && ss->opt.noLocks) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
ss->opt.fdx = on;
|
|
break;
|
|
|
|
case SSL_V2_COMPATIBLE_HELLO:
|
|
if (IS_DTLS(ss)) {
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure; /* not allowed */
|
|
}
|
|
break;
|
|
}
|
|
ss->opt.v2CompatibleHello = on;
|
|
if (!on) {
|
|
ss->opt.enableSSL2 = on;
|
|
}
|
|
break;
|
|
|
|
case SSL_ROLLBACK_DETECTION:
|
|
ss->opt.detectRollBack = on;
|
|
break;
|
|
|
|
case SSL_NO_STEP_DOWN:
|
|
ss->opt.noStepDown = on;
|
|
if (on)
|
|
SSL_DisableExportCipherSuites(fd);
|
|
break;
|
|
|
|
case SSL_BYPASS_PKCS11:
|
|
if (ss->handshakeBegun) {
|
|
PORT_SetError(PR_INVALID_STATE_ERROR);
|
|
rv = SECFailure;
|
|
} else {
|
|
if (PR_FALSE != on) {
|
|
if (PR_SUCCESS == SSL_BypassSetup() ) {
|
|
#ifdef NO_PKCS11_BYPASS
|
|
ss->opt.bypassPKCS11 = PR_FALSE;
|
|
#else
|
|
ss->opt.bypassPKCS11 = on;
|
|
#endif
|
|
} else {
|
|
rv = SECFailure;
|
|
}
|
|
} else {
|
|
ss->opt.bypassPKCS11 = PR_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SSL_NO_LOCKS:
|
|
if (on && ss->opt.fdx) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
if (on && ssl_force_locks)
|
|
on = PR_FALSE; /* silent override */
|
|
ss->opt.noLocks = on;
|
|
if (on) {
|
|
locksEverDisabled = PR_TRUE;
|
|
strcpy(lockStatus + LOCKSTATUS_OFFSET, "DISABLED.");
|
|
} else if (!holdingLocks) {
|
|
rv = ssl_MakeLocks(ss);
|
|
if (rv != SECSuccess) {
|
|
ss->opt.noLocks = PR_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SSL_ENABLE_SESSION_TICKETS:
|
|
ss->opt.enableSessionTickets = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_DEFLATE:
|
|
ss->opt.enableDeflate = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_RENEGOTIATION:
|
|
ss->opt.enableRenegotiation = on;
|
|
break;
|
|
|
|
case SSL_REQUIRE_SAFE_NEGOTIATION:
|
|
ss->opt.requireSafeNegotiation = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FALSE_START:
|
|
ss->opt.enableFalseStart = on;
|
|
break;
|
|
|
|
case SSL_CBC_RANDOM_IV:
|
|
ss->opt.cbcRandomIV = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_OCSP_STAPLING:
|
|
ss->opt.enableOCSPStapling = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_NPN:
|
|
ss->opt.enableNPN = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_ALPN:
|
|
ss->opt.enableALPN = on;
|
|
break;
|
|
|
|
case SSL_REUSE_SERVER_ECDHE_KEY:
|
|
ss->opt.reuseServerECDHEKey = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FALLBACK_SCSV:
|
|
ss->opt.enableFallbackSCSV = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_SERVER_DHE:
|
|
ss->opt.enableServerDhe = on;
|
|
break;
|
|
|
|
default:
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
|
|
/* We can't use the macros for releasing the locks here,
|
|
* because ss->opt.noLocks might have changed just above.
|
|
* We must release these locks (monitors) here, if we aquired them above,
|
|
* regardless of the current value of ss->opt.noLocks.
|
|
*/
|
|
if (holdingLocks) {
|
|
PZ_ExitMonitor((ss)->ssl3HandshakeLock);
|
|
PZ_ExitMonitor((ss)->firstHandshakeLock);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRBool *pOn)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
SECStatus rv = SECSuccess;
|
|
PRBool on = PR_FALSE;
|
|
|
|
if (!pOn) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in Enable", SSL_GETPID(), fd));
|
|
*pOn = PR_FALSE;
|
|
return SECFailure;
|
|
}
|
|
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
|
|
switch (which) {
|
|
case SSL_SOCKS: on = PR_FALSE; break;
|
|
case SSL_SECURITY: on = ss->opt.useSecurity; break;
|
|
case SSL_REQUEST_CERTIFICATE: on = ss->opt.requestCertificate; break;
|
|
case SSL_REQUIRE_CERTIFICATE: on = ss->opt.requireCertificate; break;
|
|
case SSL_HANDSHAKE_AS_CLIENT: on = ss->opt.handshakeAsClient; break;
|
|
case SSL_HANDSHAKE_AS_SERVER: on = ss->opt.handshakeAsServer; break;
|
|
case SSL_ENABLE_TLS:
|
|
on = ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_0;
|
|
break;
|
|
case SSL_ENABLE_SSL3:
|
|
on = ss->vrange.min == SSL_LIBRARY_VERSION_3_0;
|
|
break;
|
|
case SSL_ENABLE_SSL2: on = ss->opt.enableSSL2; break;
|
|
case SSL_NO_CACHE: on = ss->opt.noCache; break;
|
|
case SSL_ENABLE_FDX: on = ss->opt.fdx; break;
|
|
case SSL_V2_COMPATIBLE_HELLO: on = ss->opt.v2CompatibleHello; break;
|
|
case SSL_ROLLBACK_DETECTION: on = ss->opt.detectRollBack; break;
|
|
case SSL_NO_STEP_DOWN: on = ss->opt.noStepDown; break;
|
|
case SSL_BYPASS_PKCS11: on = ss->opt.bypassPKCS11; break;
|
|
case SSL_NO_LOCKS: on = ss->opt.noLocks; break;
|
|
case SSL_ENABLE_SESSION_TICKETS:
|
|
on = ss->opt.enableSessionTickets;
|
|
break;
|
|
case SSL_ENABLE_DEFLATE: on = ss->opt.enableDeflate; break;
|
|
case SSL_ENABLE_RENEGOTIATION:
|
|
on = ss->opt.enableRenegotiation; break;
|
|
case SSL_REQUIRE_SAFE_NEGOTIATION:
|
|
on = ss->opt.requireSafeNegotiation; break;
|
|
case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
|
|
case SSL_CBC_RANDOM_IV: on = ss->opt.cbcRandomIV; break;
|
|
case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
|
|
case SSL_ENABLE_NPN: on = ss->opt.enableNPN; break;
|
|
case SSL_ENABLE_ALPN: on = ss->opt.enableALPN; break;
|
|
case SSL_REUSE_SERVER_ECDHE_KEY:
|
|
on = ss->opt.reuseServerECDHEKey; break;
|
|
case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
|
|
case SSL_ENABLE_SERVER_DHE: on = ss->opt.enableServerDhe; break;
|
|
|
|
default:
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
ssl_Release1stHandshakeLock(ss);
|
|
|
|
*pOn = on;
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_OptionGetDefault(PRInt32 which, PRBool *pOn)
|
|
{
|
|
SECStatus rv = SECSuccess;
|
|
PRBool on = PR_FALSE;
|
|
|
|
if (!pOn) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ssl_SetDefaultsFromEnvironment();
|
|
|
|
switch (which) {
|
|
case SSL_SOCKS: on = PR_FALSE; break;
|
|
case SSL_SECURITY: on = ssl_defaults.useSecurity; break;
|
|
case SSL_REQUEST_CERTIFICATE: on = ssl_defaults.requestCertificate; break;
|
|
case SSL_REQUIRE_CERTIFICATE: on = ssl_defaults.requireCertificate; break;
|
|
case SSL_HANDSHAKE_AS_CLIENT: on = ssl_defaults.handshakeAsClient; break;
|
|
case SSL_HANDSHAKE_AS_SERVER: on = ssl_defaults.handshakeAsServer; break;
|
|
case SSL_ENABLE_TLS:
|
|
on = versions_defaults_stream.max >= SSL_LIBRARY_VERSION_TLS_1_0;
|
|
break;
|
|
case SSL_ENABLE_SSL3:
|
|
on = versions_defaults_stream.min == SSL_LIBRARY_VERSION_3_0;
|
|
break;
|
|
case SSL_ENABLE_SSL2: on = ssl_defaults.enableSSL2; break;
|
|
case SSL_NO_CACHE: on = ssl_defaults.noCache; break;
|
|
case SSL_ENABLE_FDX: on = ssl_defaults.fdx; break;
|
|
case SSL_V2_COMPATIBLE_HELLO: on = ssl_defaults.v2CompatibleHello; break;
|
|
case SSL_ROLLBACK_DETECTION: on = ssl_defaults.detectRollBack; break;
|
|
case SSL_NO_STEP_DOWN: on = ssl_defaults.noStepDown; break;
|
|
case SSL_BYPASS_PKCS11: on = ssl_defaults.bypassPKCS11; break;
|
|
case SSL_NO_LOCKS: on = ssl_defaults.noLocks; break;
|
|
case SSL_ENABLE_SESSION_TICKETS:
|
|
on = ssl_defaults.enableSessionTickets;
|
|
break;
|
|
case SSL_ENABLE_DEFLATE: on = ssl_defaults.enableDeflate; break;
|
|
case SSL_ENABLE_RENEGOTIATION:
|
|
on = ssl_defaults.enableRenegotiation; break;
|
|
case SSL_REQUIRE_SAFE_NEGOTIATION:
|
|
on = ssl_defaults.requireSafeNegotiation;
|
|
break;
|
|
case SSL_ENABLE_FALSE_START: on = ssl_defaults.enableFalseStart; break;
|
|
case SSL_CBC_RANDOM_IV: on = ssl_defaults.cbcRandomIV; break;
|
|
case SSL_ENABLE_OCSP_STAPLING:
|
|
on = ssl_defaults.enableOCSPStapling;
|
|
break;
|
|
case SSL_ENABLE_NPN: on = ssl_defaults.enableNPN; break;
|
|
case SSL_ENABLE_ALPN: on = ssl_defaults.enableALPN; break;
|
|
case SSL_REUSE_SERVER_ECDHE_KEY:
|
|
on = ssl_defaults.reuseServerECDHEKey;
|
|
break;
|
|
case SSL_ENABLE_FALLBACK_SCSV:
|
|
on = ssl_defaults.enableFallbackSCSV;
|
|
break;
|
|
case SSL_ENABLE_SERVER_DHE:
|
|
on = ssl_defaults.enableServerDhe;
|
|
break;
|
|
|
|
default:
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
rv = SECFailure;
|
|
}
|
|
|
|
*pOn = on;
|
|
return rv;
|
|
}
|
|
|
|
/* XXX Use Global Lock to protect this stuff. */
|
|
SECStatus
|
|
SSL_EnableDefault(int which, PRBool on)
|
|
{
|
|
return SSL_OptionSetDefault(which, on);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_OptionSetDefault(PRInt32 which, PRBool on)
|
|
{
|
|
SECStatus status = ssl_Init();
|
|
|
|
if (status != SECSuccess) {
|
|
return status;
|
|
}
|
|
|
|
ssl_SetDefaultsFromEnvironment();
|
|
|
|
switch (which) {
|
|
case SSL_SOCKS:
|
|
ssl_defaults.useSocks = PR_FALSE;
|
|
if (on) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
break;
|
|
|
|
case SSL_SECURITY:
|
|
ssl_defaults.useSecurity = on;
|
|
break;
|
|
|
|
case SSL_REQUEST_CERTIFICATE:
|
|
ssl_defaults.requestCertificate = on;
|
|
break;
|
|
|
|
case SSL_REQUIRE_CERTIFICATE:
|
|
ssl_defaults.requireCertificate = on;
|
|
break;
|
|
|
|
case SSL_HANDSHAKE_AS_CLIENT:
|
|
if ( ssl_defaults.handshakeAsServer && on ) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
ssl_defaults.handshakeAsClient = on;
|
|
break;
|
|
|
|
case SSL_HANDSHAKE_AS_SERVER:
|
|
if ( ssl_defaults.handshakeAsClient && on ) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
ssl_defaults.handshakeAsServer = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_TLS:
|
|
ssl_EnableTLS(&versions_defaults_stream, on);
|
|
break;
|
|
|
|
case SSL_ENABLE_SSL3:
|
|
ssl_EnableSSL3(&versions_defaults_stream, on);
|
|
break;
|
|
|
|
case SSL_ENABLE_SSL2:
|
|
ssl_defaults.enableSSL2 = on;
|
|
if (on) {
|
|
ssl_defaults.v2CompatibleHello = on;
|
|
}
|
|
break;
|
|
|
|
case SSL_NO_CACHE:
|
|
ssl_defaults.noCache = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FDX:
|
|
if (on && ssl_defaults.noLocks) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
ssl_defaults.fdx = on;
|
|
break;
|
|
|
|
case SSL_V2_COMPATIBLE_HELLO:
|
|
ssl_defaults.v2CompatibleHello = on;
|
|
if (!on) {
|
|
ssl_defaults.enableSSL2 = on;
|
|
}
|
|
break;
|
|
|
|
case SSL_ROLLBACK_DETECTION:
|
|
ssl_defaults.detectRollBack = on;
|
|
break;
|
|
|
|
case SSL_NO_STEP_DOWN:
|
|
ssl_defaults.noStepDown = on;
|
|
if (on)
|
|
SSL_DisableDefaultExportCipherSuites();
|
|
break;
|
|
|
|
case SSL_BYPASS_PKCS11:
|
|
if (PR_FALSE != on) {
|
|
if (PR_SUCCESS == SSL_BypassSetup()) {
|
|
#ifdef NO_PKCS11_BYPASS
|
|
ssl_defaults.bypassPKCS11 = PR_FALSE;
|
|
#else
|
|
ssl_defaults.bypassPKCS11 = on;
|
|
#endif
|
|
} else {
|
|
return SECFailure;
|
|
}
|
|
} else {
|
|
ssl_defaults.bypassPKCS11 = PR_FALSE;
|
|
}
|
|
break;
|
|
|
|
case SSL_NO_LOCKS:
|
|
if (on && ssl_defaults.fdx) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (on && ssl_force_locks)
|
|
on = PR_FALSE; /* silent override */
|
|
ssl_defaults.noLocks = on;
|
|
if (on) {
|
|
locksEverDisabled = PR_TRUE;
|
|
strcpy(lockStatus + LOCKSTATUS_OFFSET, "DISABLED.");
|
|
}
|
|
break;
|
|
|
|
case SSL_ENABLE_SESSION_TICKETS:
|
|
ssl_defaults.enableSessionTickets = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_DEFLATE:
|
|
ssl_defaults.enableDeflate = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_RENEGOTIATION:
|
|
ssl_defaults.enableRenegotiation = on;
|
|
break;
|
|
|
|
case SSL_REQUIRE_SAFE_NEGOTIATION:
|
|
ssl_defaults.requireSafeNegotiation = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FALSE_START:
|
|
ssl_defaults.enableFalseStart = on;
|
|
break;
|
|
|
|
case SSL_CBC_RANDOM_IV:
|
|
ssl_defaults.cbcRandomIV = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_OCSP_STAPLING:
|
|
ssl_defaults.enableOCSPStapling = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_NPN:
|
|
ssl_defaults.enableNPN = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_ALPN:
|
|
ssl_defaults.enableALPN = on;
|
|
break;
|
|
|
|
case SSL_REUSE_SERVER_ECDHE_KEY:
|
|
ssl_defaults.reuseServerECDHEKey = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_FALLBACK_SCSV:
|
|
ssl_defaults.enableFallbackSCSV = on;
|
|
break;
|
|
|
|
case SSL_ENABLE_SERVER_DHE:
|
|
ssl_defaults.enableServerDhe = on;
|
|
break;
|
|
|
|
default:
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* function tells us if the cipher suite is one that we no longer support. */
|
|
static PRBool
|
|
ssl_IsRemovedCipherSuite(PRInt32 suite)
|
|
{
|
|
switch (suite) {
|
|
case SSL_FORTEZZA_DMS_WITH_NULL_SHA:
|
|
case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:
|
|
case SSL_FORTEZZA_DMS_WITH_RC4_128_SHA:
|
|
return PR_TRUE;
|
|
default:
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Part of the public NSS API.
|
|
* Since this is a global (not per-socket) setting, we cannot use the
|
|
* HandshakeLock to protect this. Probably want a global lock.
|
|
*/
|
|
SECStatus
|
|
SSL_SetPolicy(long which, int policy)
|
|
{
|
|
if ((which & 0xfffe) == SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA) {
|
|
/* one of the two old FIPS ciphers */
|
|
if (which == SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA)
|
|
which = SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA;
|
|
else if (which == SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA)
|
|
which = SSL_RSA_FIPS_WITH_DES_CBC_SHA;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which))
|
|
return SECSuccess;
|
|
return SSL_CipherPolicySet(which, policy);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPolicySet(PRInt32 which, PRInt32 policy)
|
|
{
|
|
SECStatus rv = ssl_Init();
|
|
|
|
if (rv != SECSuccess) {
|
|
return rv;
|
|
}
|
|
|
|
if (ssl_IsRemovedCipherSuite(which)) {
|
|
rv = SECSuccess;
|
|
} else if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_SetPolicy(which, policy);
|
|
} else {
|
|
rv = ssl3_SetPolicy((ssl3CipherSuite)which, policy);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPolicyGet(PRInt32 which, PRInt32 *oPolicy)
|
|
{
|
|
SECStatus rv;
|
|
|
|
if (!oPolicy) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which)) {
|
|
*oPolicy = SSL_NOT_ALLOWED;
|
|
rv = SECSuccess;
|
|
} else if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_GetPolicy(which, oPolicy);
|
|
} else {
|
|
rv = ssl3_GetPolicy((ssl3CipherSuite)which, oPolicy);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/* Part of the public NSS API.
|
|
* Since this is a global (not per-socket) setting, we cannot use the
|
|
* HandshakeLock to protect this. Probably want a global lock.
|
|
* These changes have no effect on any sslSockets already created.
|
|
*/
|
|
SECStatus
|
|
SSL_EnableCipher(long which, PRBool enabled)
|
|
{
|
|
if ((which & 0xfffe) == SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA) {
|
|
/* one of the two old FIPS ciphers */
|
|
if (which == SSL_RSA_OLDFIPS_WITH_3DES_EDE_CBC_SHA)
|
|
which = SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA;
|
|
else if (which == SSL_RSA_OLDFIPS_WITH_DES_CBC_SHA)
|
|
which = SSL_RSA_FIPS_WITH_DES_CBC_SHA;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which))
|
|
return SECSuccess;
|
|
return SSL_CipherPrefSetDefault(which, enabled);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPrefSetDefault(PRInt32 which, PRBool enabled)
|
|
{
|
|
SECStatus rv = ssl_Init();
|
|
|
|
if (rv != SECSuccess) {
|
|
return rv;
|
|
}
|
|
|
|
if (ssl_IsRemovedCipherSuite(which))
|
|
return SECSuccess;
|
|
if (enabled && ssl_defaults.noStepDown && SSL_IsExportCipherSuite(which)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
|
return SECFailure;
|
|
}
|
|
if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_CipherPrefSetDefault(which, enabled);
|
|
} else {
|
|
rv = ssl3_CipherPrefSetDefault((ssl3CipherSuite)which, enabled);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPrefGetDefault(PRInt32 which, PRBool *enabled)
|
|
{
|
|
SECStatus rv;
|
|
|
|
if (!enabled) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which)) {
|
|
*enabled = PR_FALSE;
|
|
rv = SECSuccess;
|
|
} else if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_CipherPrefGetDefault(which, enabled);
|
|
} else {
|
|
rv = ssl3_CipherPrefGetDefault((ssl3CipherSuite)which, enabled);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPrefSet(PRFileDesc *fd, PRInt32 which, PRBool enabled)
|
|
{
|
|
SECStatus rv;
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in CipherPrefSet", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which))
|
|
return SECSuccess;
|
|
if (enabled && ss->opt.noStepDown && SSL_IsExportCipherSuite(which)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
|
return SECFailure;
|
|
}
|
|
if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_CipherPrefSet(ss, which, enabled);
|
|
} else {
|
|
rv = ssl3_CipherPrefSet(ss, (ssl3CipherSuite)which, enabled);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_CipherPrefGet(PRFileDesc *fd, PRInt32 which, PRBool *enabled)
|
|
{
|
|
SECStatus rv;
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!enabled) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in CipherPrefGet", SSL_GETPID(), fd));
|
|
*enabled = PR_FALSE;
|
|
return SECFailure;
|
|
}
|
|
if (ssl_IsRemovedCipherSuite(which)) {
|
|
*enabled = PR_FALSE;
|
|
rv = SECSuccess;
|
|
} else if (SSL_IS_SSL2_CIPHER(which)) {
|
|
rv = ssl2_CipherPrefGet(ss, which, enabled);
|
|
} else {
|
|
rv = ssl3_CipherPrefGet(ss, (ssl3CipherSuite)which, enabled);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
NSS_SetDomesticPolicy(void)
|
|
{
|
|
SECStatus status = SECSuccess;
|
|
const PRUint16 *cipher;
|
|
|
|
for (cipher = SSL_ImplementedCiphers; *cipher != 0; ++cipher) {
|
|
status = SSL_SetPolicy(*cipher, SSL_ALLOWED);
|
|
if (status != SECSuccess)
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
SECStatus
|
|
NSS_SetExportPolicy(void)
|
|
{
|
|
return NSS_SetDomesticPolicy();
|
|
}
|
|
|
|
SECStatus
|
|
NSS_SetFrancePolicy(void)
|
|
{
|
|
return NSS_SetDomesticPolicy();
|
|
}
|
|
|
|
SECStatus
|
|
SSL_DHEGroupPrefSet(PRFileDesc *fd,
|
|
SSLDHEGroupType *groups,
|
|
PRUint16 num_groups)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
if ((num_groups && !groups) || (!num_groups && groups)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DHEGroupPrefSet", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ss->ssl3.dheGroups) {
|
|
PORT_Free(ss->ssl3.dheGroups);
|
|
ss->ssl3.dheGroups = NULL;
|
|
ss->ssl3.numDHEGroups = 0;
|
|
}
|
|
|
|
if (groups) {
|
|
ss->ssl3.dheGroups = PORT_NewArray(SSLDHEGroupType, num_groups);
|
|
if (!ss->ssl3.dheGroups) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return SECFailure;
|
|
}
|
|
PORT_Memcpy(ss->ssl3.dheGroups, groups,
|
|
sizeof(SSLDHEGroupType) * num_groups);
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
|
|
PRCallOnceType gWeakDHParamsRegisterOnce;
|
|
int gWeakDHParamsRegisterError;
|
|
|
|
PRCallOnceType gWeakDHParamsOnce;
|
|
int gWeakDHParamsError;
|
|
/* As our code allocates type PQGParams, we'll keep it around,
|
|
* even though we only make use of it's parameters through gWeakDHParam. */
|
|
static PQGParams *gWeakParamsPQG;
|
|
static ssl3DHParams *gWeakDHParams;
|
|
|
|
static PRStatus
|
|
ssl3_CreateWeakDHParams(void)
|
|
{
|
|
PQGVerify *vfy;
|
|
SECStatus rv, passed;
|
|
|
|
PORT_Assert(!gWeakDHParams && !gWeakParamsPQG);
|
|
|
|
rv = PK11_PQG_ParamGenV2(1024, 160, 64 /*maximum seed that will work*/,
|
|
&gWeakParamsPQG, &vfy);
|
|
if (rv != SECSuccess) {
|
|
gWeakDHParamsError = PORT_GetError();
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
rv = PK11_PQG_VerifyParams(gWeakParamsPQG, vfy, &passed);
|
|
if (rv != SECSuccess || passed != SECSuccess) {
|
|
SSL_DBG(("%d: PK11_PQG_VerifyParams failed in ssl3_CreateWeakDHParams",
|
|
SSL_GETPID()));
|
|
gWeakDHParamsError = PORT_GetError();
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
gWeakDHParams = PORT_ArenaNew(gWeakParamsPQG->arena, ssl3DHParams);
|
|
if (!gWeakDHParams) {
|
|
gWeakDHParamsError = PORT_GetError();
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
gWeakDHParams->prime.data = gWeakParamsPQG->prime.data;
|
|
gWeakDHParams->prime.len = gWeakParamsPQG->prime.len;
|
|
gWeakDHParams->base.data = gWeakParamsPQG->base.data;
|
|
gWeakDHParams->base.len = gWeakParamsPQG->base.len;
|
|
|
|
PK11_PQG_DestroyVerify(vfy);
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static SECStatus
|
|
ssl3_WeakDHParamsShutdown(void *appData, void *nssData)
|
|
{
|
|
if (gWeakParamsPQG) {
|
|
PK11_PQG_DestroyParams(gWeakParamsPQG);
|
|
gWeakParamsPQG = NULL;
|
|
gWeakDHParams = NULL;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static PRStatus
|
|
ssl3_WeakDHParamsRegisterShutdown(void)
|
|
{
|
|
SECStatus rv;
|
|
rv = NSS_RegisterShutdown(ssl3_WeakDHParamsShutdown, NULL);
|
|
if (rv != SECSuccess) {
|
|
gWeakDHParamsRegisterError = PORT_GetError();
|
|
}
|
|
return (PRStatus)rv;
|
|
}
|
|
|
|
/* global init strategy inspired by ssl3_CreateECDHEphemeralKeys */
|
|
SECStatus
|
|
SSL_EnableWeakDHEPrimeGroup(PRFileDesc *fd, PRBool enabled)
|
|
{
|
|
sslSocket *ss;
|
|
PRStatus status;
|
|
|
|
if (enabled) {
|
|
status = PR_CallOnce(&gWeakDHParamsRegisterOnce,
|
|
ssl3_WeakDHParamsRegisterShutdown);
|
|
if (status != PR_SUCCESS) {
|
|
PORT_SetError(gWeakDHParamsRegisterError);
|
|
return SECFailure;
|
|
}
|
|
|
|
status = PR_CallOnce(&gWeakDHParamsOnce, ssl3_CreateWeakDHParams);
|
|
if (status != PR_SUCCESS) {
|
|
PORT_SetError(gWeakDHParamsError);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
|
|
if (!fd)
|
|
return SECSuccess;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DHEGroupPrefSet", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
ss->ssl3.dheWeakGroupEnabled = enabled;
|
|
return SECSuccess;
|
|
}
|
|
|
|
#include "dhe-param.c"
|
|
|
|
static const SSLDHEGroupType ssl_default_dhe_groups[] = {
|
|
ssl_ff_dhe_2048_group
|
|
};
|
|
|
|
/* Keep this array synchronized with the index definitions in SSLDHEGroupType */
|
|
static const ssl3DHParams *all_ssl3DHParams[] = {
|
|
NULL, /* ssl_dhe_group_none */
|
|
&ff_dhe_2048,
|
|
&ff_dhe_3072,
|
|
&ff_dhe_4096,
|
|
&ff_dhe_6144,
|
|
&ff_dhe_8192,
|
|
};
|
|
|
|
static SSLDHEGroupType
|
|
selectDHEGroup(sslSocket *ss, const SSLDHEGroupType *groups, PRUint16 num_groups)
|
|
{
|
|
if (!groups || !num_groups)
|
|
return ssl_dhe_group_none;
|
|
|
|
/* We don't have automatic group parameter selection yet
|
|
* (potentially) based on socket parameters, e.g. key sizes.
|
|
* For now, we return the first available group from the allowed list. */
|
|
return groups[0];
|
|
}
|
|
|
|
/* Ensure DH parameters have been selected */
|
|
SECStatus
|
|
ssl3_SelectDHParams(sslSocket *ss)
|
|
{
|
|
SSLDHEGroupType selectedGroup = ssl_dhe_group_none;
|
|
|
|
if (ss->ssl3.dheWeakGroupEnabled) {
|
|
ss->dheParams = gWeakDHParams;
|
|
} else {
|
|
if (ss->ssl3.dheGroups) {
|
|
selectedGroup = selectDHEGroup(ss, ss->ssl3.dheGroups,
|
|
ss->ssl3.numDHEGroups);
|
|
} else {
|
|
size_t number_of_default_groups = PR_ARRAY_SIZE(ssl_default_dhe_groups);
|
|
selectedGroup = selectDHEGroup(ss, ssl_default_dhe_groups,
|
|
number_of_default_groups);
|
|
}
|
|
|
|
if (selectedGroup == ssl_dhe_group_none ||
|
|
selectedGroup >= ssl_dhe_group_max) {
|
|
return SECFailure;
|
|
}
|
|
|
|
ss->dheParams = all_ssl3DHParams[selectedGroup];
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* LOCKS ??? XXX */
|
|
static PRFileDesc *
|
|
ssl_ImportFD(PRFileDesc *model, PRFileDesc *fd, SSLProtocolVariant variant)
|
|
{
|
|
sslSocket * ns = NULL;
|
|
PRStatus rv;
|
|
PRNetAddr addr;
|
|
SECStatus status = ssl_Init();
|
|
|
|
if (status != SECSuccess) {
|
|
return NULL;
|
|
}
|
|
|
|
if (model == NULL) {
|
|
/* Just create a default socket if we're given NULL for the model */
|
|
ns = ssl_NewSocket((PRBool)(!ssl_defaults.noLocks), variant);
|
|
} else {
|
|
sslSocket * ss = ssl_FindSocket(model);
|
|
if (ss == NULL || ss->protocolVariant != variant) {
|
|
SSL_DBG(("%d: SSL[%d]: bad model socket in ssl_ImportFD",
|
|
SSL_GETPID(), model));
|
|
return NULL;
|
|
}
|
|
ns = ssl_DupSocket(ss);
|
|
}
|
|
if (ns == NULL)
|
|
return NULL;
|
|
|
|
rv = ssl_PushIOLayer(ns, fd, PR_TOP_IO_LAYER);
|
|
if (rv != PR_SUCCESS) {
|
|
ssl_FreeSocket(ns);
|
|
SET_ERROR_CODE
|
|
return NULL;
|
|
}
|
|
#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
|
|
{
|
|
sslSocket * ss = ssl_FindSocket(fd);
|
|
PORT_Assert(ss == ns);
|
|
}
|
|
#endif
|
|
ns->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ns, &addr));
|
|
return fd;
|
|
}
|
|
|
|
PRFileDesc *
|
|
SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
|
|
{
|
|
return ssl_ImportFD(model, fd, ssl_variant_stream);
|
|
}
|
|
|
|
PRFileDesc *
|
|
DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd)
|
|
{
|
|
return ssl_ImportFD(model, fd, ssl_variant_datagram);
|
|
}
|
|
|
|
/* SSL_SetNextProtoCallback is used to select an application protocol
|
|
* for ALPN and NPN. For ALPN, this runs on the server; for NPN it
|
|
* runs on the client. */
|
|
/* Note: The ALPN version doesn't allow for the use of a default, setting a
|
|
* status of SSL_NEXT_PROTO_NO_OVERLAP is treated as a failure. */
|
|
SECStatus
|
|
SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
|
|
void *arg)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoCallback", SSL_GETPID(),
|
|
fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
ss->nextProtoCallback = callback;
|
|
ss->nextProtoArg = arg;
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* ssl_NextProtoNegoCallback is set as an ALPN/NPN callback when
|
|
* SSL_SetNextProtoNego is used.
|
|
*/
|
|
static SECStatus
|
|
ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
|
|
const unsigned char *protos, unsigned int protos_len,
|
|
unsigned char *protoOut, unsigned int *protoOutLen,
|
|
unsigned int protoMaxLen)
|
|
{
|
|
unsigned int i, j;
|
|
const unsigned char *result;
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in ssl_NextProtoNegoCallback",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
/* For each protocol in server preference, see if we support it. */
|
|
for (i = 0; i < protos_len; ) {
|
|
for (j = 0; j < ss->opt.nextProtoNego.len; ) {
|
|
if (protos[i] == ss->opt.nextProtoNego.data[j] &&
|
|
PORT_Memcmp(&protos[i+1], &ss->opt.nextProtoNego.data[j+1],
|
|
protos[i]) == 0) {
|
|
/* We found a match. */
|
|
ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED;
|
|
result = &protos[i];
|
|
goto found;
|
|
}
|
|
j += 1 + (unsigned int)ss->opt.nextProtoNego.data[j];
|
|
}
|
|
i += 1 + (unsigned int)protos[i];
|
|
}
|
|
|
|
/* The other side supports the extension, and either doesn't have any
|
|
* protocols configured, or none of its options match ours. In this case we
|
|
* request our favoured protocol. */
|
|
/* This will be treated as a failure for ALPN. */
|
|
ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP;
|
|
result = ss->opt.nextProtoNego.data;
|
|
|
|
found:
|
|
if (protoMaxLen < result[0]) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
memcpy(protoOut, result + 1, result[0]);
|
|
*protoOutLen = result[0];
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data,
|
|
unsigned int length)
|
|
{
|
|
sslSocket *ss;
|
|
SECStatus rv;
|
|
SECItem dataItem = { siBuffer, (unsigned char *) data, length };
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess)
|
|
return SECFailure;
|
|
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
|
|
rv = SECITEM_CopyItem(NULL, &ss->opt.nextProtoNego, &dataItem);
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
return SSL_SetNextProtoCallback(fd, ssl_NextProtoNegoCallback, NULL);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_GetNextProto(PRFileDesc *fd, SSLNextProtoState *state, unsigned char *buf,
|
|
unsigned int *bufLen, unsigned int bufLenMax)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNextProto", SSL_GETPID(),
|
|
fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!state || !buf || !bufLen) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
*state = ss->ssl3.nextProtoState;
|
|
|
|
if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
|
|
ss->ssl3.nextProto.data) {
|
|
if (ss->ssl3.nextProto.len > bufLenMax) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
PORT_Memcpy(buf, ss->ssl3.nextProto.data, ss->ssl3.nextProto.len);
|
|
*bufLen = ss->ssl3.nextProto.len;
|
|
} else {
|
|
*bufLen = 0;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus SSL_SetSRTPCiphers(PRFileDesc *fd,
|
|
const PRUint16 *ciphers,
|
|
unsigned int numCiphers)
|
|
{
|
|
sslSocket *ss;
|
|
unsigned int i;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss || !IS_DTLS(ss)) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetSRTPCiphers",
|
|
SSL_GETPID(), fd));
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (numCiphers > MAX_DTLS_SRTP_CIPHER_SUITES) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ss->ssl3.dtlsSRTPCipherCount = 0;
|
|
for (i = 0; i < numCiphers; i++) {
|
|
const PRUint16 *srtpCipher = srtpCiphers;
|
|
|
|
while (*srtpCipher) {
|
|
if (ciphers[i] == *srtpCipher)
|
|
break;
|
|
srtpCipher++;
|
|
}
|
|
if (*srtpCipher) {
|
|
ss->ssl3.dtlsSRTPCiphers[ss->ssl3.dtlsSRTPCipherCount++] =
|
|
ciphers[i];
|
|
} else {
|
|
SSL_DBG(("%d: SSL[%d]: invalid or unimplemented SRTP cipher "
|
|
"suite specified: 0x%04hx", SSL_GETPID(), fd,
|
|
ciphers[i]));
|
|
}
|
|
}
|
|
|
|
if (ss->ssl3.dtlsSRTPCipherCount == 0) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_GetSRTPCipher(PRFileDesc *fd, PRUint16 *cipher)
|
|
{
|
|
sslSocket * ss;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetSRTPCipher",
|
|
SSL_GETPID(), fd));
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!ss->ssl3.dtlsSRTPCipherSuite) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
*cipher = ss->ssl3.dtlsSRTPCipherSuite;
|
|
return SECSuccess;
|
|
}
|
|
|
|
PRFileDesc *
|
|
SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
|
|
{
|
|
sslSocket * sm = NULL, *ss = NULL;
|
|
int i;
|
|
sslServerCerts * mc = NULL;
|
|
sslServerCerts * sc = NULL;
|
|
|
|
if (model == NULL) {
|
|
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
|
return NULL;
|
|
}
|
|
sm = ssl_FindSocket(model);
|
|
if (sm == NULL) {
|
|
SSL_DBG(("%d: SSL[%d]: bad model socket in ssl_ReconfigFD",
|
|
SSL_GETPID(), model));
|
|
return NULL;
|
|
}
|
|
ss = ssl_FindSocket(fd);
|
|
PORT_Assert(ss);
|
|
if (ss == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
|
|
ss->opt = sm->opt;
|
|
ss->vrange = sm->vrange;
|
|
PORT_Memcpy(ss->cipherSuites, sm->cipherSuites, sizeof sm->cipherSuites);
|
|
PORT_Memcpy(ss->ssl3.dtlsSRTPCiphers, sm->ssl3.dtlsSRTPCiphers,
|
|
sizeof(PRUint16) * sm->ssl3.dtlsSRTPCipherCount);
|
|
ss->ssl3.dtlsSRTPCipherCount = sm->ssl3.dtlsSRTPCipherCount;
|
|
PORT_Memcpy(ss->ssl3.signatureAlgorithms, sm->ssl3.signatureAlgorithms,
|
|
sizeof(ss->ssl3.signatureAlgorithms[0]) *
|
|
sm->ssl3.signatureAlgorithmCount);
|
|
ss->ssl3.signatureAlgorithmCount = sm->ssl3.signatureAlgorithmCount;
|
|
|
|
if (!ss->opt.useSecurity) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
/* This int should be SSLKEAType, but CC on Irix complains,
|
|
* during the for loop.
|
|
*/
|
|
for (i=kt_null; i < kt_kea_size; i++) {
|
|
mc = &(sm->serverCerts[i]);
|
|
sc = &(ss->serverCerts[i]);
|
|
if (mc->serverCert && mc->serverCertChain) {
|
|
if (sc->serverCert) {
|
|
CERT_DestroyCertificate(sc->serverCert);
|
|
}
|
|
sc->serverCert = CERT_DupCertificate(mc->serverCert);
|
|
if (sc->serverCertChain) {
|
|
CERT_DestroyCertificateList(sc->serverCertChain);
|
|
}
|
|
sc->serverCertChain = CERT_DupCertList(mc->serverCertChain);
|
|
if (!sc->serverCertChain)
|
|
goto loser;
|
|
if (sm->certStatusArray[i]) {
|
|
if (ss->certStatusArray[i]) {
|
|
SECITEM_FreeArray(ss->certStatusArray[i], PR_TRUE);
|
|
ss->certStatusArray[i] = NULL;
|
|
}
|
|
ss->certStatusArray[i] = SECITEM_DupArray(NULL, sm->certStatusArray[i]);
|
|
if (!ss->certStatusArray[i])
|
|
goto loser;
|
|
}
|
|
}
|
|
if (mc->serverKeyPair) {
|
|
if (sc->serverKeyPair) {
|
|
ssl3_FreeKeyPair(sc->serverKeyPair);
|
|
}
|
|
sc->serverKeyPair = ssl3_GetKeyPairRef(mc->serverKeyPair);
|
|
sc->serverKeyBits = mc->serverKeyBits;
|
|
}
|
|
}
|
|
if (sm->stepDownKeyPair) {
|
|
if (ss->stepDownKeyPair) {
|
|
ssl3_FreeKeyPair(ss->stepDownKeyPair);
|
|
}
|
|
ss->stepDownKeyPair = ssl3_GetKeyPairRef(sm->stepDownKeyPair);
|
|
}
|
|
if (sm->ephemeralECDHKeyPair) {
|
|
if (ss->ephemeralECDHKeyPair) {
|
|
ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair);
|
|
}
|
|
ss->ephemeralECDHKeyPair =
|
|
ssl3_GetKeyPairRef(sm->ephemeralECDHKeyPair);
|
|
}
|
|
/* copy trust anchor names */
|
|
if (sm->ssl3.ca_list) {
|
|
if (ss->ssl3.ca_list) {
|
|
CERT_FreeDistNames(ss->ssl3.ca_list);
|
|
}
|
|
ss->ssl3.ca_list = CERT_DupDistNames(sm->ssl3.ca_list);
|
|
if (!ss->ssl3.ca_list) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
if (sm->authCertificate)
|
|
ss->authCertificate = sm->authCertificate;
|
|
if (sm->authCertificateArg)
|
|
ss->authCertificateArg = sm->authCertificateArg;
|
|
if (sm->getClientAuthData)
|
|
ss->getClientAuthData = sm->getClientAuthData;
|
|
if (sm->getClientAuthDataArg)
|
|
ss->getClientAuthDataArg = sm->getClientAuthDataArg;
|
|
if (sm->sniSocketConfig)
|
|
ss->sniSocketConfig = sm->sniSocketConfig;
|
|
if (sm->sniSocketConfigArg)
|
|
ss->sniSocketConfigArg = sm->sniSocketConfigArg;
|
|
if (sm->handleBadCert)
|
|
ss->handleBadCert = sm->handleBadCert;
|
|
if (sm->badCertArg)
|
|
ss->badCertArg = sm->badCertArg;
|
|
if (sm->handshakeCallback)
|
|
ss->handshakeCallback = sm->handshakeCallback;
|
|
if (sm->handshakeCallbackData)
|
|
ss->handshakeCallbackData = sm->handshakeCallbackData;
|
|
if (sm->pkcs11PinArg)
|
|
ss->pkcs11PinArg = sm->pkcs11PinArg;
|
|
return fd;
|
|
loser:
|
|
return NULL;
|
|
}
|
|
|
|
PRBool
|
|
ssl3_VersionIsSupported(SSLProtocolVariant protocolVariant,
|
|
SSL3ProtocolVersion version)
|
|
{
|
|
switch (protocolVariant) {
|
|
case ssl_variant_stream:
|
|
return (version >= SSL_LIBRARY_VERSION_3_0 &&
|
|
version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
|
|
case ssl_variant_datagram:
|
|
return (version >= SSL_LIBRARY_VERSION_TLS_1_1 &&
|
|
version <= SSL_LIBRARY_VERSION_MAX_SUPPORTED);
|
|
default:
|
|
/* Can't get here */
|
|
PORT_Assert(PR_FALSE);
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Returns PR_TRUE if the given version range is valid and
|
|
** fully supported; otherwise, returns PR_FALSE.
|
|
*/
|
|
static PRBool
|
|
ssl3_VersionRangeIsValid(SSLProtocolVariant protocolVariant,
|
|
const SSLVersionRange *vrange)
|
|
{
|
|
return vrange &&
|
|
vrange->min <= vrange->max &&
|
|
ssl3_VersionIsSupported(protocolVariant, vrange->min) &&
|
|
ssl3_VersionIsSupported(protocolVariant, vrange->max);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_VersionRangeGetSupported(SSLProtocolVariant protocolVariant,
|
|
SSLVersionRange *vrange)
|
|
{
|
|
if (!vrange) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
switch (protocolVariant) {
|
|
case ssl_variant_stream:
|
|
vrange->min = SSL_LIBRARY_VERSION_3_0;
|
|
vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
|
|
break;
|
|
case ssl_variant_datagram:
|
|
vrange->min = SSL_LIBRARY_VERSION_TLS_1_1;
|
|
vrange->max = SSL_LIBRARY_VERSION_MAX_SUPPORTED;
|
|
break;
|
|
default:
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_VersionRangeGetDefault(SSLProtocolVariant protocolVariant,
|
|
SSLVersionRange *vrange)
|
|
{
|
|
if ((protocolVariant != ssl_variant_stream &&
|
|
protocolVariant != ssl_variant_datagram) || !vrange) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
*vrange = *VERSIONS_DEFAULTS(protocolVariant);
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_VersionRangeSetDefault(SSLProtocolVariant protocolVariant,
|
|
const SSLVersionRange *vrange)
|
|
{
|
|
if (!ssl3_VersionRangeIsValid(protocolVariant, vrange)) {
|
|
PORT_SetError(SSL_ERROR_INVALID_VERSION_RANGE);
|
|
return SECFailure;
|
|
}
|
|
|
|
*VERSIONS_DEFAULTS(protocolVariant) = *vrange;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_VersionRangeGet(PRFileDesc *fd, SSLVersionRange *vrange)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL3_VersionRangeGet",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!vrange) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
|
|
*vrange = ss->vrange;
|
|
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
ssl_Release1stHandshakeLock(ss);
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_VersionRangeSet(PRFileDesc *fd, const SSLVersionRange *vrange)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL3_VersionRangeSet",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (!ssl3_VersionRangeIsValid(ss->protocolVariant, vrange)) {
|
|
PORT_SetError(SSL_ERROR_INVALID_VERSION_RANGE);
|
|
return SECFailure;
|
|
}
|
|
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
|
|
ss->vrange = *vrange;
|
|
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
ssl_Release1stHandshakeLock(ss);
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
const SECItemArray *
|
|
SSL_PeerStapledOCSPResponses(PRFileDesc *fd)
|
|
{
|
|
sslSocket *ss = ssl_FindSocket(fd);
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_PeerStapledOCSPResponses",
|
|
SSL_GETPID(), fd));
|
|
return NULL;
|
|
}
|
|
|
|
if (!ss->sec.ci.sid) {
|
|
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
|
|
return NULL;
|
|
}
|
|
|
|
return &ss->sec.ci.sid->peerCertStatus;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* The following functions are the TOP LEVEL SSL functions.
|
|
** They all get called through the NSPRIOMethods table below.
|
|
*/
|
|
|
|
static PRFileDesc * PR_CALLBACK
|
|
ssl_Accept(PRFileDesc *fd, PRNetAddr *sockaddr, PRIntervalTime timeout)
|
|
{
|
|
sslSocket *ss;
|
|
sslSocket *ns = NULL;
|
|
PRFileDesc *newfd = NULL;
|
|
PRFileDesc *osfd;
|
|
PRStatus status;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in accept", SSL_GETPID(), fd));
|
|
return NULL;
|
|
}
|
|
|
|
/* IF this is a listen socket, there shouldn't be any I/O going on */
|
|
SSL_LOCK_READER(ss);
|
|
SSL_LOCK_WRITER(ss);
|
|
ssl_Get1stHandshakeLock(ss);
|
|
ssl_GetSSL3HandshakeLock(ss);
|
|
|
|
ss->cTimeout = timeout;
|
|
|
|
osfd = ss->fd->lower;
|
|
|
|
/* First accept connection */
|
|
newfd = osfd->methods->accept(osfd, sockaddr, timeout);
|
|
if (newfd == NULL) {
|
|
SSL_DBG(("%d: SSL[%d]: accept failed, errno=%d",
|
|
SSL_GETPID(), ss->fd, PORT_GetError()));
|
|
} else {
|
|
/* Create ssl module */
|
|
ns = ssl_DupSocket(ss);
|
|
}
|
|
|
|
ssl_ReleaseSSL3HandshakeLock(ss);
|
|
ssl_Release1stHandshakeLock(ss);
|
|
SSL_UNLOCK_WRITER(ss);
|
|
SSL_UNLOCK_READER(ss); /* ss isn't used below here. */
|
|
|
|
if (ns == NULL)
|
|
goto loser;
|
|
|
|
/* push ssl module onto the new socket */
|
|
status = ssl_PushIOLayer(ns, newfd, PR_TOP_IO_LAYER);
|
|
if (status != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
/* Now start server connection handshake with client.
|
|
** Don't need locks here because nobody else has a reference to ns yet.
|
|
*/
|
|
if ( ns->opt.useSecurity ) {
|
|
if ( ns->opt.handshakeAsClient ) {
|
|
ns->handshake = ssl2_BeginClientHandshake;
|
|
ss->handshaking = sslHandshakingAsClient;
|
|
} else {
|
|
ns->handshake = ssl2_BeginServerHandshake;
|
|
ss->handshaking = sslHandshakingAsServer;
|
|
}
|
|
}
|
|
ns->TCPconnected = 1;
|
|
return newfd;
|
|
|
|
loser:
|
|
if (ns != NULL)
|
|
ssl_FreeSocket(ns);
|
|
if (newfd != NULL)
|
|
PR_Close(newfd);
|
|
return NULL;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_Connect(PRFileDesc *fd, const PRNetAddr *sockaddr, PRIntervalTime timeout)
|
|
{
|
|
sslSocket *ss;
|
|
PRStatus rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in connect", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
/* IF this is a listen socket, there shouldn't be any I/O going on */
|
|
SSL_LOCK_READER(ss);
|
|
SSL_LOCK_WRITER(ss);
|
|
|
|
ss->cTimeout = timeout;
|
|
rv = (PRStatus)(*ss->ops->connect)(ss, sockaddr);
|
|
|
|
SSL_UNLOCK_WRITER(ss);
|
|
SSL_UNLOCK_READER(ss);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_Bind(PRFileDesc *fd, const PRNetAddr *addr)
|
|
{
|
|
sslSocket * ss = ssl_GetPrivate(fd);
|
|
PRStatus rv;
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in bind", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
SSL_LOCK_READER(ss);
|
|
SSL_LOCK_WRITER(ss);
|
|
|
|
rv = (PRStatus)(*ss->ops->bind)(ss, addr);
|
|
|
|
SSL_UNLOCK_WRITER(ss);
|
|
SSL_UNLOCK_READER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_Listen(PRFileDesc *fd, PRIntn backlog)
|
|
{
|
|
sslSocket * ss = ssl_GetPrivate(fd);
|
|
PRStatus rv;
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in listen", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
SSL_LOCK_READER(ss);
|
|
SSL_LOCK_WRITER(ss);
|
|
|
|
rv = (PRStatus)(*ss->ops->listen)(ss, backlog);
|
|
|
|
SSL_UNLOCK_WRITER(ss);
|
|
SSL_UNLOCK_READER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_Shutdown(PRFileDesc *fd, PRIntn how)
|
|
{
|
|
sslSocket * ss = ssl_GetPrivate(fd);
|
|
PRStatus rv;
|
|
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in shutdown", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
if (how == PR_SHUTDOWN_RCV || how == PR_SHUTDOWN_BOTH) {
|
|
SSL_LOCK_READER(ss);
|
|
}
|
|
if (how == PR_SHUTDOWN_SEND || how == PR_SHUTDOWN_BOTH) {
|
|
SSL_LOCK_WRITER(ss);
|
|
}
|
|
|
|
rv = (PRStatus)(*ss->ops->shutdown)(ss, how);
|
|
|
|
if (how == PR_SHUTDOWN_SEND || how == PR_SHUTDOWN_BOTH) {
|
|
SSL_UNLOCK_WRITER(ss);
|
|
}
|
|
if (how == PR_SHUTDOWN_RCV || how == PR_SHUTDOWN_BOTH) {
|
|
SSL_UNLOCK_READER(ss);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_Close(PRFileDesc *fd)
|
|
{
|
|
sslSocket *ss;
|
|
PRStatus rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in close", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
/* There must not be any I/O going on */
|
|
SSL_LOCK_READER(ss);
|
|
SSL_LOCK_WRITER(ss);
|
|
|
|
/* By the time this function returns,
|
|
** ss is an invalid pointer, and the locks to which it points have
|
|
** been unlocked and freed. So, this is the ONE PLACE in all of SSL
|
|
** where the LOCK calls and the corresponding UNLOCK calls are not in
|
|
** the same function scope. The unlock calls are in ssl_FreeSocket().
|
|
*/
|
|
rv = (PRStatus)(*ss->ops->close)(ss);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int PR_CALLBACK
|
|
ssl_Recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags,
|
|
PRIntervalTime timeout)
|
|
{
|
|
sslSocket *ss;
|
|
int rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in recv", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
SSL_LOCK_READER(ss);
|
|
ss->rTimeout = timeout;
|
|
if (!ss->opt.fdx)
|
|
ss->wTimeout = timeout;
|
|
rv = (*ss->ops->recv)(ss, (unsigned char*)buf, len, flags);
|
|
SSL_UNLOCK_READER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static int PR_CALLBACK
|
|
ssl_Send(PRFileDesc *fd, const void *buf, PRInt32 len, PRIntn flags,
|
|
PRIntervalTime timeout)
|
|
{
|
|
sslSocket *ss;
|
|
int rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in send", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
SSL_LOCK_WRITER(ss);
|
|
ss->wTimeout = timeout;
|
|
if (!ss->opt.fdx)
|
|
ss->rTimeout = timeout;
|
|
rv = (*ss->ops->send)(ss, (const unsigned char*)buf, len, flags);
|
|
SSL_UNLOCK_WRITER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static int PR_CALLBACK
|
|
ssl_Read(PRFileDesc *fd, void *buf, PRInt32 len)
|
|
{
|
|
sslSocket *ss;
|
|
int rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in read", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
SSL_LOCK_READER(ss);
|
|
ss->rTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
if (!ss->opt.fdx)
|
|
ss->wTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
rv = (*ss->ops->read)(ss, (unsigned char*)buf, len);
|
|
SSL_UNLOCK_READER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static int PR_CALLBACK
|
|
ssl_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
|
|
{
|
|
sslSocket *ss;
|
|
int rv;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in write", SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
SSL_LOCK_WRITER(ss);
|
|
ss->wTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
if (!ss->opt.fdx)
|
|
ss->rTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
rv = (*ss->ops->write)(ss, (const unsigned char*)buf, len);
|
|
SSL_UNLOCK_WRITER(ss);
|
|
return rv;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in getpeername", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
return (PRStatus)(*ss->ops->getpeername)(ss, addr);
|
|
}
|
|
|
|
/*
|
|
*/
|
|
SECStatus
|
|
ssl_GetPeerInfo(sslSocket *ss)
|
|
{
|
|
PRFileDesc * osfd;
|
|
int rv;
|
|
PRNetAddr sin;
|
|
|
|
osfd = ss->fd->lower;
|
|
|
|
PORT_Memset(&sin, 0, sizeof(sin));
|
|
rv = osfd->methods->getpeername(osfd, &sin);
|
|
if (rv < 0) {
|
|
return SECFailure;
|
|
}
|
|
ss->TCPconnected = 1;
|
|
if (sin.inet.family == PR_AF_INET) {
|
|
PR_ConvertIPv4AddrToIPv6(sin.inet.ip, &ss->sec.ci.peer);
|
|
ss->sec.ci.port = sin.inet.port;
|
|
} else if (sin.ipv6.family == PR_AF_INET6) {
|
|
ss->sec.ci.peer = sin.ipv6.ip;
|
|
ss->sec.ci.port = sin.ipv6.port;
|
|
} else {
|
|
PORT_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR);
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_GetSockName(PRFileDesc *fd, PRNetAddr *name)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in getsockname", SSL_GETPID(), fd));
|
|
return PR_FAILURE;
|
|
}
|
|
return (PRStatus)(*ss->ops->getsockname)(ss, name);
|
|
}
|
|
|
|
SECStatus
|
|
SSL_SetStapledOCSPResponses(PRFileDesc *fd, const SECItemArray *responses,
|
|
SSLKEAType kea)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetStapledOCSPResponses",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if ( kea <= 0 || kea >= kt_kea_size) {
|
|
SSL_DBG(("%d: SSL[%d]: invalid key in SSL_SetStapledOCSPResponses",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ss->certStatusArray[kea]) {
|
|
SECITEM_FreeArray(ss->certStatusArray[kea], PR_TRUE);
|
|
ss->certStatusArray[kea] = NULL;
|
|
}
|
|
if (responses) {
|
|
ss->certStatusArray[kea] = SECITEM_DupArray(NULL, responses);
|
|
}
|
|
return (ss->certStatusArray[kea] || !responses) ? SECSuccess : SECFailure;
|
|
}
|
|
|
|
SECStatus
|
|
SSL_SetSockPeerID(PRFileDesc *fd, const char *peerID)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetSockPeerID",
|
|
SSL_GETPID(), fd));
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ss->peerID) {
|
|
PORT_Free(ss->peerID);
|
|
ss->peerID = NULL;
|
|
}
|
|
if (peerID)
|
|
ss->peerID = PORT_Strdup(peerID);
|
|
return (ss->peerID || !peerID) ? SECSuccess : SECFailure;
|
|
}
|
|
|
|
#define PR_POLL_RW (PR_POLL_WRITE | PR_POLL_READ)
|
|
|
|
static PRInt16 PR_CALLBACK
|
|
ssl_Poll(PRFileDesc *fd, PRInt16 how_flags, PRInt16 *p_out_flags)
|
|
{
|
|
sslSocket *ss;
|
|
PRInt16 new_flags = how_flags; /* should select on these flags. */
|
|
PRNetAddr addr;
|
|
|
|
*p_out_flags = 0;
|
|
ss = ssl_GetPrivate(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_Poll",
|
|
SSL_GETPID(), fd));
|
|
return 0; /* don't poll on this socket */
|
|
}
|
|
|
|
if (ss->opt.useSecurity &&
|
|
ss->handshaking != sslHandshakingUndetermined &&
|
|
!ss->firstHsDone &&
|
|
(how_flags & PR_POLL_RW)) {
|
|
if (!ss->TCPconnected) {
|
|
ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr));
|
|
}
|
|
/* If it's not connected, then presumably the application is polling
|
|
** on read or write appropriately, so don't change it.
|
|
*/
|
|
if (ss->TCPconnected) {
|
|
if (!ss->handshakeBegun) {
|
|
/* If the handshake has not begun, poll on read or write
|
|
** based on the local application's role in the handshake,
|
|
** not based on what the application requested.
|
|
*/
|
|
new_flags &= ~PR_POLL_RW;
|
|
if (ss->handshaking == sslHandshakingAsClient) {
|
|
new_flags |= PR_POLL_WRITE;
|
|
} else { /* handshaking as server */
|
|
new_flags |= PR_POLL_READ;
|
|
}
|
|
} else
|
|
/* First handshake is in progress */
|
|
if (ss->lastWriteBlocked) {
|
|
if (new_flags & PR_POLL_READ) {
|
|
/* The caller is waiting for data to be received,
|
|
** but the initial handshake is blocked on write, or the
|
|
** client's first handshake record has not been written.
|
|
** The code should select on write, not read.
|
|
*/
|
|
new_flags ^= PR_POLL_READ; /* don't select on read. */
|
|
new_flags |= PR_POLL_WRITE; /* do select on write. */
|
|
}
|
|
} else if (new_flags & PR_POLL_WRITE) {
|
|
/* The caller is trying to write, but the handshake is
|
|
** blocked waiting for data to read, and the first
|
|
** handshake has been sent. So do NOT to poll on write
|
|
** unless we did false start.
|
|
*/
|
|
if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 &&
|
|
ss->ssl3.hs.canFalseStart)) {
|
|
new_flags ^= PR_POLL_WRITE; /* don't select on write. */
|
|
}
|
|
new_flags |= PR_POLL_READ; /* do select on read. */
|
|
}
|
|
}
|
|
} else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
|
|
*p_out_flags = PR_POLL_READ; /* it's ready already. */
|
|
return new_flags;
|
|
} else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
|
|
(ss->pendingBuf.len != 0)) { /* write data waiting to be sent */
|
|
new_flags |= PR_POLL_WRITE; /* also select on write. */
|
|
}
|
|
|
|
if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
|
|
ss->ssl3.hs.restartTarget != NULL) {
|
|
/* Read and write will block until the asynchronous callback completes
|
|
* (e.g. until SSL_AuthCertificateComplete is called), so don't tell
|
|
* the caller to poll the socket unless there is pending write data.
|
|
*/
|
|
if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) {
|
|
/* Ignore any newly-received data on the socket, but do wait for
|
|
* the socket to become writable again. Here, it is OK for an error
|
|
* to be detected, because our logic for sending pending write data
|
|
* will allow us to report the error to the caller without the risk
|
|
* of the application spinning.
|
|
*/
|
|
new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT);
|
|
} else {
|
|
/* Unfortunately, clearing new_flags will make it impossible for
|
|
* the application to detect errors that it would otherwise be
|
|
* able to detect with PR_POLL_EXCEPT, until the asynchronous
|
|
* callback completes. However, we must clear all the flags to
|
|
* prevent the application from spinning (alternating between
|
|
* calling PR_Poll that would return PR_POLL_EXCEPT, and send/recv
|
|
* which won't actually report the I/O error while we are waiting
|
|
* for the asynchronous callback to complete).
|
|
*/
|
|
new_flags = 0;
|
|
}
|
|
}
|
|
|
|
if (new_flags && (fd->lower->methods->poll != NULL)) {
|
|
PRInt16 lower_out_flags = 0;
|
|
PRInt16 lower_new_flags;
|
|
lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags,
|
|
&lower_out_flags);
|
|
if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) {
|
|
PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW;
|
|
if (lower_out_flags & PR_POLL_READ)
|
|
out_flags |= PR_POLL_WRITE;
|
|
if (lower_out_flags & PR_POLL_WRITE)
|
|
out_flags |= PR_POLL_READ;
|
|
*p_out_flags = out_flags;
|
|
new_flags = how_flags;
|
|
} else {
|
|
*p_out_flags = lower_out_flags;
|
|
new_flags = lower_new_flags;
|
|
}
|
|
}
|
|
|
|
return new_flags;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_TransmitFile(PRFileDesc *sd, PRFileDesc *fd,
|
|
const void *headers, PRInt32 hlen,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
PRSendFileData sfd;
|
|
|
|
sfd.fd = fd;
|
|
sfd.file_offset = 0;
|
|
sfd.file_nbytes = 0;
|
|
sfd.header = headers;
|
|
sfd.hlen = hlen;
|
|
sfd.trailer = NULL;
|
|
sfd.tlen = 0;
|
|
|
|
return sd->methods->sendfile(sd, &sfd, flags, timeout);
|
|
}
|
|
|
|
|
|
PRBool
|
|
ssl_FdIsBlocking(PRFileDesc *fd)
|
|
{
|
|
PRSocketOptionData opt;
|
|
PRStatus status;
|
|
|
|
opt.option = PR_SockOpt_Nonblocking;
|
|
opt.value.non_blocking = PR_FALSE;
|
|
status = PR_GetSocketOption(fd, &opt);
|
|
if (status != PR_SUCCESS)
|
|
return PR_FALSE;
|
|
return (PRBool)!opt.value.non_blocking;
|
|
}
|
|
|
|
PRBool
|
|
ssl_SocketIsBlocking(sslSocket *ss)
|
|
{
|
|
return ssl_FdIsBlocking(ss->fd);
|
|
}
|
|
|
|
PRInt32 sslFirstBufSize = 8 * 1024;
|
|
PRInt32 sslCopyLimit = 1024;
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_WriteV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 vectors,
|
|
PRIntervalTime timeout)
|
|
{
|
|
PRInt32 i;
|
|
PRInt32 bufLen;
|
|
PRInt32 left;
|
|
PRInt32 rv;
|
|
PRInt32 sent = 0;
|
|
const PRInt32 first_len = sslFirstBufSize;
|
|
const PRInt32 limit = sslCopyLimit;
|
|
PRBool blocking;
|
|
PRIOVec myIov = { 0, 0 };
|
|
char buf[MAX_FRAGMENT_LENGTH];
|
|
|
|
if (vectors < 0) {
|
|
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
|
|
return -1;
|
|
}
|
|
if (vectors > PR_MAX_IOVECTOR_SIZE) {
|
|
PORT_SetError(PR_BUFFER_OVERFLOW_ERROR);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < vectors; i++) {
|
|
if (iov[i].iov_len < 0) {
|
|
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
|
|
return -1;
|
|
}
|
|
}
|
|
blocking = ssl_FdIsBlocking(fd);
|
|
|
|
#define K16 sizeof(buf)
|
|
#define KILL_VECTORS while (vectors && !iov->iov_len) { ++iov; --vectors; }
|
|
#define GET_VECTOR do { myIov = *iov++; --vectors; KILL_VECTORS } while (0)
|
|
#define HANDLE_ERR(rv, len) \
|
|
if (rv != len) { \
|
|
if (rv < 0) { \
|
|
if (!blocking \
|
|
&& (PR_GetError() == PR_WOULD_BLOCK_ERROR) \
|
|
&& (sent > 0)) { \
|
|
return sent; \
|
|
} else { \
|
|
return -1; \
|
|
} \
|
|
} \
|
|
/* Only a nonblocking socket can have partial sends */ \
|
|
PR_ASSERT(!blocking); \
|
|
return sent + rv; \
|
|
}
|
|
#define SEND(bfr, len) \
|
|
do { \
|
|
rv = ssl_Send(fd, bfr, len, 0, timeout); \
|
|
HANDLE_ERR(rv, len) \
|
|
sent += len; \
|
|
} while (0)
|
|
|
|
/* Make sure the first write is at least 8 KB, if possible. */
|
|
KILL_VECTORS
|
|
if (!vectors)
|
|
return ssl_Send(fd, 0, 0, 0, timeout);
|
|
GET_VECTOR;
|
|
if (!vectors) {
|
|
return ssl_Send(fd, myIov.iov_base, myIov.iov_len, 0, timeout);
|
|
}
|
|
if (myIov.iov_len < first_len) {
|
|
PORT_Memcpy(buf, myIov.iov_base, myIov.iov_len);
|
|
bufLen = myIov.iov_len;
|
|
left = first_len - bufLen;
|
|
while (vectors && left) {
|
|
int toCopy;
|
|
GET_VECTOR;
|
|
toCopy = PR_MIN(left, myIov.iov_len);
|
|
PORT_Memcpy(buf + bufLen, myIov.iov_base, toCopy);
|
|
bufLen += toCopy;
|
|
left -= toCopy;
|
|
myIov.iov_base += toCopy;
|
|
myIov.iov_len -= toCopy;
|
|
}
|
|
SEND( buf, bufLen );
|
|
}
|
|
|
|
while (vectors || myIov.iov_len) {
|
|
PRInt32 addLen;
|
|
if (!myIov.iov_len) {
|
|
GET_VECTOR;
|
|
}
|
|
while (myIov.iov_len >= K16) {
|
|
SEND(myIov.iov_base, K16);
|
|
myIov.iov_base += K16;
|
|
myIov.iov_len -= K16;
|
|
}
|
|
if (!myIov.iov_len)
|
|
continue;
|
|
|
|
if (!vectors || myIov.iov_len > limit) {
|
|
addLen = 0;
|
|
} else if ((addLen = iov->iov_len % K16) + myIov.iov_len <= limit) {
|
|
/* Addlen is already computed. */;
|
|
} else if (vectors > 1 &&
|
|
iov[1].iov_len % K16 + addLen + myIov.iov_len <= 2 * limit) {
|
|
addLen = limit - myIov.iov_len;
|
|
} else
|
|
addLen = 0;
|
|
|
|
if (!addLen) {
|
|
SEND( myIov.iov_base, myIov.iov_len );
|
|
myIov.iov_len = 0;
|
|
continue;
|
|
}
|
|
PORT_Memcpy(buf, myIov.iov_base, myIov.iov_len);
|
|
bufLen = myIov.iov_len;
|
|
do {
|
|
GET_VECTOR;
|
|
PORT_Memcpy(buf + bufLen, myIov.iov_base, addLen);
|
|
myIov.iov_base += addLen;
|
|
myIov.iov_len -= addLen;
|
|
bufLen += addLen;
|
|
|
|
left = PR_MIN( limit, K16 - bufLen);
|
|
if (!vectors /* no more left */
|
|
|| myIov.iov_len > 0 /* we didn't use that one all up */
|
|
|| bufLen >= K16 /* it's full. */
|
|
) {
|
|
addLen = 0;
|
|
} else if ((addLen = iov->iov_len % K16) <= left) {
|
|
/* Addlen is already computed. */;
|
|
} else if (vectors > 1 &&
|
|
iov[1].iov_len % K16 + addLen <= left + limit) {
|
|
addLen = left;
|
|
} else
|
|
addLen = 0;
|
|
|
|
} while (addLen);
|
|
SEND( buf, bufLen );
|
|
}
|
|
return sent;
|
|
}
|
|
|
|
/*
|
|
* These functions aren't implemented.
|
|
*/
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_Available(PRFileDesc *fd)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return SECFailure;
|
|
}
|
|
|
|
static PRInt64 PR_CALLBACK
|
|
ssl_Available64(PRFileDesc *fd)
|
|
{
|
|
PRInt64 res;
|
|
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
LL_I2L(res, -1L);
|
|
return res;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_FSync(PRFileDesc *fd)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence how) {
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return SECFailure;
|
|
}
|
|
|
|
static PRInt64 PR_CALLBACK
|
|
ssl_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence how) {
|
|
PRInt64 res;
|
|
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
LL_I2L(res, -1L);
|
|
return res;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_FileInfo(PRFileDesc *fd, PRFileInfo *info)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
ssl_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
|
|
PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return SECFailure;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
ssl_SendTo(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
|
|
const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
PORT_Assert(0);
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return SECFailure;
|
|
}
|
|
|
|
static const PRIOMethods ssl_methods = {
|
|
PR_DESC_LAYERED,
|
|
ssl_Close, /* close */
|
|
ssl_Read, /* read */
|
|
ssl_Write, /* write */
|
|
ssl_Available, /* available */
|
|
ssl_Available64, /* available64 */
|
|
ssl_FSync, /* fsync */
|
|
ssl_Seek, /* seek */
|
|
ssl_Seek64, /* seek64 */
|
|
ssl_FileInfo, /* fileInfo */
|
|
ssl_FileInfo64, /* fileInfo64 */
|
|
ssl_WriteV, /* writev */
|
|
ssl_Connect, /* connect */
|
|
ssl_Accept, /* accept */
|
|
ssl_Bind, /* bind */
|
|
ssl_Listen, /* listen */
|
|
ssl_Shutdown, /* shutdown */
|
|
ssl_Recv, /* recv */
|
|
ssl_Send, /* send */
|
|
ssl_RecvFrom, /* recvfrom */
|
|
ssl_SendTo, /* sendto */
|
|
ssl_Poll, /* poll */
|
|
PR_EmulateAcceptRead, /* acceptread */
|
|
ssl_TransmitFile, /* transmitfile */
|
|
ssl_GetSockName, /* getsockname */
|
|
ssl_GetPeerName, /* getpeername */
|
|
NULL, /* getsockopt OBSOLETE */
|
|
NULL, /* setsockopt OBSOLETE */
|
|
NULL, /* getsocketoption */
|
|
NULL, /* setsocketoption */
|
|
PR_EmulateSendFile, /* Send a (partial) file with header/trailer*/
|
|
NULL, /* reserved for future use */
|
|
NULL, /* reserved for future use */
|
|
NULL, /* reserved for future use */
|
|
NULL, /* reserved for future use */
|
|
NULL /* reserved for future use */
|
|
};
|
|
|
|
|
|
static PRIOMethods combined_methods;
|
|
|
|
static void
|
|
ssl_SetupIOMethods(void)
|
|
{
|
|
PRIOMethods *new_methods = &combined_methods;
|
|
const PRIOMethods *nspr_methods = PR_GetDefaultIOMethods();
|
|
const PRIOMethods *my_methods = &ssl_methods;
|
|
|
|
*new_methods = *nspr_methods;
|
|
|
|
new_methods->file_type = my_methods->file_type;
|
|
new_methods->close = my_methods->close;
|
|
new_methods->read = my_methods->read;
|
|
new_methods->write = my_methods->write;
|
|
new_methods->available = my_methods->available;
|
|
new_methods->available64 = my_methods->available64;
|
|
new_methods->fsync = my_methods->fsync;
|
|
new_methods->seek = my_methods->seek;
|
|
new_methods->seek64 = my_methods->seek64;
|
|
new_methods->fileInfo = my_methods->fileInfo;
|
|
new_methods->fileInfo64 = my_methods->fileInfo64;
|
|
new_methods->writev = my_methods->writev;
|
|
new_methods->connect = my_methods->connect;
|
|
new_methods->accept = my_methods->accept;
|
|
new_methods->bind = my_methods->bind;
|
|
new_methods->listen = my_methods->listen;
|
|
new_methods->shutdown = my_methods->shutdown;
|
|
new_methods->recv = my_methods->recv;
|
|
new_methods->send = my_methods->send;
|
|
new_methods->recvfrom = my_methods->recvfrom;
|
|
new_methods->sendto = my_methods->sendto;
|
|
new_methods->poll = my_methods->poll;
|
|
new_methods->acceptread = my_methods->acceptread;
|
|
new_methods->transmitfile = my_methods->transmitfile;
|
|
new_methods->getsockname = my_methods->getsockname;
|
|
new_methods->getpeername = my_methods->getpeername;
|
|
/* new_methods->getsocketoption = my_methods->getsocketoption; */
|
|
/* new_methods->setsocketoption = my_methods->setsocketoption; */
|
|
new_methods->sendfile = my_methods->sendfile;
|
|
|
|
}
|
|
|
|
static PRCallOnceType initIoLayerOnce;
|
|
|
|
static PRStatus
|
|
ssl_InitIOLayer(void)
|
|
{
|
|
ssl_layer_id = PR_GetUniqueIdentity("SSL");
|
|
ssl_SetupIOMethods();
|
|
ssl_inited = PR_TRUE;
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
static PRStatus
|
|
ssl_PushIOLayer(sslSocket *ns, PRFileDesc *stack, PRDescIdentity id)
|
|
{
|
|
PRFileDesc *layer = NULL;
|
|
PRStatus status;
|
|
|
|
if (!ssl_inited) {
|
|
status = PR_CallOnce(&initIoLayerOnce, &ssl_InitIOLayer);
|
|
if (status != PR_SUCCESS)
|
|
goto loser;
|
|
}
|
|
|
|
if (ns == NULL)
|
|
goto loser;
|
|
|
|
layer = PR_CreateIOLayerStub(ssl_layer_id, &combined_methods);
|
|
if (layer == NULL)
|
|
goto loser;
|
|
layer->secret = (PRFilePrivate *)ns;
|
|
|
|
/* Here, "stack" points to the PRFileDesc on the top of the stack.
|
|
** "layer" points to a new FD that is to be inserted into the stack.
|
|
** If layer is being pushed onto the top of the stack, then
|
|
** PR_PushIOLayer switches the contents of stack and layer, and then
|
|
** puts stack on top of layer, so that after it is done, the top of
|
|
** stack is the same "stack" as it was before, and layer is now the
|
|
** FD for the former top of stack.
|
|
** After this call, stack always points to the top PRFD on the stack.
|
|
** If this function fails, the contents of stack and layer are as
|
|
** they were before the call.
|
|
*/
|
|
status = PR_PushIOLayer(stack, id, layer);
|
|
if (status != PR_SUCCESS)
|
|
goto loser;
|
|
|
|
ns->fd = (id == PR_TOP_IO_LAYER) ? stack : layer;
|
|
return PR_SUCCESS;
|
|
|
|
loser:
|
|
if (layer) {
|
|
layer->dtor(layer); /* free layer */
|
|
}
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
/* if this fails, caller must destroy socket. */
|
|
static SECStatus
|
|
ssl_MakeLocks(sslSocket *ss)
|
|
{
|
|
ss->firstHandshakeLock = PZ_NewMonitor(nssILockSSL);
|
|
if (!ss->firstHandshakeLock)
|
|
goto loser;
|
|
ss->ssl3HandshakeLock = PZ_NewMonitor(nssILockSSL);
|
|
if (!ss->ssl3HandshakeLock)
|
|
goto loser;
|
|
ss->specLock = NSSRWLock_New(SSL_LOCK_RANK_SPEC, NULL);
|
|
if (!ss->specLock)
|
|
goto loser;
|
|
ss->recvBufLock = PZ_NewMonitor(nssILockSSL);
|
|
if (!ss->recvBufLock)
|
|
goto loser;
|
|
ss->xmitBufLock = PZ_NewMonitor(nssILockSSL);
|
|
if (!ss->xmitBufLock)
|
|
goto loser;
|
|
ss->writerThread = NULL;
|
|
if (ssl_lock_readers) {
|
|
ss->recvLock = PZ_NewLock(nssILockSSL);
|
|
if (!ss->recvLock)
|
|
goto loser;
|
|
ss->sendLock = PZ_NewLock(nssILockSSL);
|
|
if (!ss->sendLock)
|
|
goto loser;
|
|
}
|
|
return SECSuccess;
|
|
loser:
|
|
ssl_DestroyLocks(ss);
|
|
return SECFailure;
|
|
}
|
|
|
|
#if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_BEOS)
|
|
#define NSS_HAVE_GETENV 1
|
|
#endif
|
|
|
|
#define LOWER(x) (x | 0x20) /* cheap ToLower function ignores LOCALE */
|
|
|
|
static void
|
|
ssl_SetDefaultsFromEnvironment(void)
|
|
{
|
|
#if defined( NSS_HAVE_GETENV )
|
|
static int firsttime = 1;
|
|
|
|
if (firsttime) {
|
|
char * ev;
|
|
firsttime = 0;
|
|
#ifdef DEBUG
|
|
ev = getenv("SSLDEBUGFILE");
|
|
if (ev && ev[0]) {
|
|
ssl_trace_iob = fopen(ev, "w");
|
|
}
|
|
if (!ssl_trace_iob) {
|
|
ssl_trace_iob = stderr;
|
|
}
|
|
#ifdef TRACE
|
|
ev = getenv("SSLTRACE");
|
|
if (ev && ev[0]) {
|
|
ssl_trace = atoi(ev);
|
|
SSL_TRACE(("SSL: tracing set to %d", ssl_trace));
|
|
}
|
|
#endif /* TRACE */
|
|
ev = getenv("SSLDEBUG");
|
|
if (ev && ev[0]) {
|
|
ssl_debug = atoi(ev);
|
|
SSL_TRACE(("SSL: debugging set to %d", ssl_debug));
|
|
}
|
|
#endif /* DEBUG */
|
|
ev = getenv("SSLKEYLOGFILE");
|
|
if (ev && ev[0]) {
|
|
ssl_keylog_iob = fopen(ev, "a");
|
|
if (!ssl_keylog_iob) {
|
|
SSL_TRACE(("SSL: failed to open key log file"));
|
|
} else {
|
|
if (ftell(ssl_keylog_iob) == 0) {
|
|
fputs("# SSL/TLS secrets log file, generated by NSS\n",
|
|
ssl_keylog_iob);
|
|
}
|
|
SSL_TRACE(("SSL: logging SSL/TLS secrets to %s", ev));
|
|
}
|
|
}
|
|
#ifndef NO_PKCS11_BYPASS
|
|
ev = getenv("SSLBYPASS");
|
|
if (ev && ev[0]) {
|
|
ssl_defaults.bypassPKCS11 = (ev[0] == '1');
|
|
SSL_TRACE(("SSL: bypass default set to %d", \
|
|
ssl_defaults.bypassPKCS11));
|
|
}
|
|
#endif /* NO_PKCS11_BYPASS */
|
|
ev = getenv("SSLFORCELOCKS");
|
|
if (ev && ev[0] == '1') {
|
|
ssl_force_locks = PR_TRUE;
|
|
ssl_defaults.noLocks = 0;
|
|
strcpy(lockStatus + LOCKSTATUS_OFFSET, "FORCED. ");
|
|
SSL_TRACE(("SSL: force_locks set to %d", ssl_force_locks));
|
|
}
|
|
ev = getenv("NSS_SSL_ENABLE_RENEGOTIATION");
|
|
if (ev) {
|
|
if (ev[0] == '1' || LOWER(ev[0]) == 'u')
|
|
ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_UNRESTRICTED;
|
|
else if (ev[0] == '0' || LOWER(ev[0]) == 'n')
|
|
ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_NEVER;
|
|
else if (ev[0] == '2' || LOWER(ev[0]) == 'r')
|
|
ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_REQUIRES_XTN;
|
|
else if (ev[0] == '3' || LOWER(ev[0]) == 't')
|
|
ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_TRANSITIONAL;
|
|
SSL_TRACE(("SSL: enableRenegotiation set to %d",
|
|
ssl_defaults.enableRenegotiation));
|
|
}
|
|
ev = getenv("NSS_SSL_REQUIRE_SAFE_NEGOTIATION");
|
|
if (ev && ev[0] == '1') {
|
|
ssl_defaults.requireSafeNegotiation = PR_TRUE;
|
|
SSL_TRACE(("SSL: requireSafeNegotiation set to %d",
|
|
PR_TRUE));
|
|
}
|
|
ev = getenv("NSS_SSL_CBC_RANDOM_IV");
|
|
if (ev && ev[0] == '0') {
|
|
ssl_defaults.cbcRandomIV = PR_FALSE;
|
|
SSL_TRACE(("SSL: cbcRandomIV set to 0"));
|
|
}
|
|
}
|
|
#endif /* NSS_HAVE_GETENV */
|
|
}
|
|
|
|
/*
|
|
** Create a newsocket structure for a file descriptor.
|
|
*/
|
|
static sslSocket *
|
|
ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant)
|
|
{
|
|
sslSocket *ss;
|
|
|
|
ssl_SetDefaultsFromEnvironment();
|
|
|
|
if (ssl_force_locks)
|
|
makeLocks = PR_TRUE;
|
|
|
|
/* Make a new socket and get it ready */
|
|
ss = (sslSocket*) PORT_ZAlloc(sizeof(sslSocket));
|
|
if (ss) {
|
|
/* This should be of type SSLKEAType, but CC on IRIX
|
|
* complains during the for loop.
|
|
*/
|
|
int i;
|
|
SECStatus status;
|
|
|
|
ss->opt = ssl_defaults;
|
|
ss->opt.useSocks = PR_FALSE;
|
|
ss->opt.noLocks = !makeLocks;
|
|
ss->vrange = *VERSIONS_DEFAULTS(protocolVariant);
|
|
ss->protocolVariant = protocolVariant;
|
|
|
|
ss->peerID = NULL;
|
|
ss->rTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
ss->wTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
ss->cTimeout = PR_INTERVAL_NO_TIMEOUT;
|
|
ss->cipherSpecs = NULL;
|
|
ss->sizeCipherSpecs = 0; /* produced lazily */
|
|
ss->preferredCipher = NULL;
|
|
ss->url = NULL;
|
|
|
|
for (i=kt_null; i < kt_kea_size; i++) {
|
|
sslServerCerts * sc = ss->serverCerts + i;
|
|
sc->serverCert = NULL;
|
|
sc->serverCertChain = NULL;
|
|
sc->serverKeyPair = NULL;
|
|
sc->serverKeyBits = 0;
|
|
ss->certStatusArray[i] = NULL;
|
|
}
|
|
ss->stepDownKeyPair = NULL;
|
|
|
|
ss->dheParams = NULL;
|
|
ss->dheKeyPair = NULL;
|
|
|
|
ss->dbHandle = CERT_GetDefaultCertDB();
|
|
|
|
/* Provide default implementation of hooks */
|
|
ss->authCertificate = SSL_AuthCertificate;
|
|
ss->authCertificateArg = (void *)ss->dbHandle;
|
|
ss->sniSocketConfig = NULL;
|
|
ss->sniSocketConfigArg = NULL;
|
|
ss->getClientAuthData = NULL;
|
|
ss->handleBadCert = NULL;
|
|
ss->badCertArg = NULL;
|
|
ss->pkcs11PinArg = NULL;
|
|
ss->ephemeralECDHKeyPair = NULL;
|
|
|
|
ssl_ChooseOps(ss);
|
|
ssl2_InitSocketPolicy(ss);
|
|
ssl3_InitSocketPolicy(ss);
|
|
PR_INIT_CLIST(&ss->ssl3.hs.lastMessageFlight);
|
|
|
|
if (makeLocks) {
|
|
status = ssl_MakeLocks(ss);
|
|
if (status != SECSuccess)
|
|
goto loser;
|
|
}
|
|
status = ssl_CreateSecurityInfo(ss);
|
|
if (status != SECSuccess)
|
|
goto loser;
|
|
status = ssl_InitGather(&ss->gs);
|
|
if (status != SECSuccess) {
|
|
loser:
|
|
ssl_DestroySocketContents(ss);
|
|
ssl_DestroyLocks(ss);
|
|
PORT_Free(ss);
|
|
ss = NULL;
|
|
}
|
|
}
|
|
return ss;
|
|
}
|