diff options
Diffstat (limited to 'mozilla/security/nss/lib/ssl/sslsecur.c')
-rw-r--r-- | mozilla/security/nss/lib/ssl/sslsecur.c | 1536 |
1 files changed, 1536 insertions, 0 deletions
diff --git a/mozilla/security/nss/lib/ssl/sslsecur.c b/mozilla/security/nss/lib/ssl/sslsecur.c new file mode 100644 index 0000000..a40dd4f --- /dev/null +++ b/mozilla/security/nss/lib/ssl/sslsecur.c @@ -0,0 +1,1536 @@ +/* + * Various SSL functions. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: sslsecur.c,v 1.43.2.4 2011/04/08 05:25:21 wtc%google.com Exp $ */ +#include "cert.h" +#include "secitem.h" +#include "keyhi.h" +#include "ssl.h" +#include "sslimpl.h" +#include "sslproto.h" +#include "secoid.h" /* for SECOID_GetALgorithmTag */ +#include "pk11func.h" /* for PK11_GenerateRandom */ +#include "nss.h" /* for NSS_RegisterShutdown */ +#include "prinit.h" /* for PR_CallOnceWithArg */ + +#define MAX_BLOCK_CYPHER_SIZE 32 + +#define TEST_FOR_FAILURE /* reminder */ +#define SET_ERROR_CODE /* reminder */ + +/* Returns a SECStatus: SECSuccess or SECFailure, NOT SECWouldBlock. + * + * Currently, the list of functions called through ss->handshake is: + * + * In sslsocks.c: + * SocksGatherRecord + * SocksHandleReply + * SocksStartGather + * + * In sslcon.c: + * ssl_GatherRecord1stHandshake + * ssl2_HandleClientSessionKeyMessage + * ssl2_HandleMessage + * ssl2_HandleVerifyMessage + * ssl2_BeginClientHandshake + * ssl2_BeginServerHandshake + * ssl2_HandleClientHelloMessage + * ssl2_HandleServerHelloMessage + * + * The ss->handshake function returns SECWouldBlock under these conditions: + * 1. ssl_GatherRecord1stHandshake called ssl2_GatherData which read in + * the beginning of an SSL v3 hello message and returned SECWouldBlock + * to switch to SSL v3 handshake processing. + * + * 2. ssl2_HandleClientHelloMessage discovered version 3.0 in the incoming + * v2 client hello msg, and called ssl3_HandleV2ClientHello which + * returned SECWouldBlock. + * + * 3. SECWouldBlock was returned by one of the callback functions, via + * one of these paths: + * - ssl2_HandleMessage() -> ssl2_HandleRequestCertificate() -> ss->getClientAuthData() + * + * - ssl2_HandleServerHelloMessage() -> ss->handleBadCert() + * + * - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() -> + * ssl3_HandleRecord() -> ssl3_HandleHandshake() -> + * ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificate() -> + * ss->handleBadCert() + * + * - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() -> + * ssl3_HandleRecord() -> ssl3_HandleHandshake() -> + * ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificateRequest() -> + * ss->getClientAuthData() + * + * Called from: SSL_ForceHandshake (below), + * ssl_SecureRecv (below) and + * ssl_SecureSend (below) + * from: WaitForResponse in sslsocks.c + * ssl_SocksRecv in sslsocks.c + * ssl_SocksSend in sslsocks.c + * + * Caller must hold the (write) handshakeLock. + */ +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)); + + if (ss->handshake == 0) { + /* Previous handshake finished. Switch to next one */ + ss->handshake = ss->nextHandshake; + ss->nextHandshake = 0; + } + if (ss->handshake == 0) { + /* Previous handshake finished. Switch to security handshake */ + ss->handshake = ss->securityHandshake; + ss->securityHandshake = 0; + } + if (ss->handshake == 0) { + ssl_GetRecvBufLock(ss); + ss->gs.recordLen = 0; + ssl_ReleaseRecvBufLock(ss); + + SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", + SSL_GETPID(), ss->fd)); + /* call handshake callback for ssl v2 */ + /* for v3 this is done in ssl3_HandleFinished() */ + if ((ss->handshakeCallback != NULL) && /* has callback */ + (!ss->firstHsDone) && /* only first time */ + (ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */ + ss->firstHsDone = PR_TRUE; + (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); + } + ss->firstHsDone = PR_TRUE; + ss->gs.writeOffset = 0; + ss->gs.readOffset = 0; + break; + } + rv = (*ss->handshake)(ss); + ++loopCount; + /* This code must continue to loop on SECWouldBlock, + * or any positive value. See XXX_1 comments. + */ + } while (rv != SECFailure); /* was (rv >= 0); XXX_1 */ + + PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss)); + + if (rv == SECWouldBlock) { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + rv = SECFailure; + } + return rv; +} + +/* + * Handshake function that blocks. Used to force a + * retry on a connection on the next read/write. + */ +static SECStatus +AlwaysBlock(sslSocket *ss) +{ + PORT_SetError(PR_WOULD_BLOCK_ERROR); /* perhaps redundant. */ + return SECWouldBlock; +} + +/* + * set the initial handshake state machine to block + */ +void +ssl_SetAlwaysBlock(sslSocket *ss) +{ + if (!ss->firstHsDone) { + ss->handshake = 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; +} + +/* Acquires and releases HandshakeLock. +*/ +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; + } + + /* Don't waste my time */ + if (!ss->opt.useSecurity) + return SECSuccess; + + SSL_LOCK_READER(ss); + SSL_LOCK_WRITER(ss); + + /* Reset handshake state */ + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + + ss->firstHsDone = 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); + + /* + ** Blow away old security state and get a fresh setup. + */ + 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; +} + +/* For SSLv2, does nothing but return an error. +** For SSLv3, flushes SID cache entry (if requested), +** and then starts new client hello or hello request. +** Acquires and releases HandshakeLock. +*/ +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); + + /* SSL v2 protocol does not support subsequent handshakes. */ + if (ss->version < SSL_LIBRARY_VERSION_3_0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + } else { + ssl_GetSSL3HandshakeLock(ss); + rv = ssl3_RedoHandshake(ss, flushCache); /* force full handshake. */ + ssl_ReleaseSSL3HandshakeLock(ss); + } + + ssl_Release1stHandshakeLock(ss); + + return rv; +} + +/* +** Same as above, but with an I/O timeout. + */ +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); +} + +/* Register an application callback to be called when SSL handshake completes. +** Acquires and releases HandshakeLock. +*/ +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; +} + +/* Try to make progress on an SSL handshake by attempting to read the +** next handshake from the peer, and sending any responses. +** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot +** read the next handshake from the underlying socket. +** For SSLv2, returns when handshake is complete or fatal error occurs. +** For SSLv3, returns when handshake is complete, or application data has +** arrived that must be taken by application before handshake can continue, +** or a fatal error occurs. +** Application should use handshake completion callback to tell which. +*/ +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; + } + + /* Don't waste my time */ + if (!ss->opt.useSecurity) + return SECSuccess; + + 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 { + /* tried to force handshake on an SSL 2 socket that has + ** already completed the handshake. */ + rv = SECSuccess; /* just pretend we did it. */ + } + + ssl_Release1stHandshakeLock(ss); + + return rv; +} + +/* + ** Same as above, but with an I/O timeout. + */ +SSL_IMPORT SECStatus SSL_ForceHandshakeWithTimeout(PRFileDesc *fd, + PRIntervalTime timeout) +{ + if (SECSuccess != ssl_SetTimeout(fd, timeout)) { + return SECFailure; + } + return SSL_ForceHandshake(fd); +} + + +/************************************************************************/ + +/* +** Grow a buffer to hold newLen bytes of data. +** Called for both recv buffers and xmit buffers. +** Caller must hold xmitBufLock or recvBufLock, as appropriate. +*/ +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; +} + +/* +** Save away write data that is trying to be written before the security +** handshake has been completed. When the handshake is completed, we will +** flush this data out. +** Caller must hold xmitBufLock +*/ +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; +} + +/* +** Send saved write data. This will flush out data sent prior to a +** complete security handshake. Hopefully there won't be too much of it. +** Returns count of the bytes sent, NOT a SECStatus. +** Caller must hold xmitBufLock +*/ +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) { + /* UGH !! This shifts the whole buffer down by copying it */ + PORT_Memmove(ss->pendingBuf.buf, ss->pendingBuf.buf + rv, + ss->pendingBuf.len); + } + } + return rv; +} + +/************************************************************************/ + +/* +** Receive some application data on a socket. Reads SSL records from the input +** stream, decrypts them and then copies them to the output buffer. +** Called from ssl_SecureRecv() below. +** +** Caller does NOT hold 1stHandshakeLock because that handshake is over. +** Caller doesn't call this until initial handshake is complete. +** For SSLv2, there is no subsequent handshake. +** For SSLv3, the call to ssl3_GatherAppDataRecord may encounter handshake +** messages from a subsequent handshake. +** +** This code is similar to, and easily confused with, +** ssl_GatherRecord1stHandshake() in sslcon.c +*/ +static int +DoRecv(sslSocket *ss, unsigned char *out, int len, int flags) +{ + int rv; + int amount; + int available; + + ssl_GetRecvBufLock(ss); + + available = ss->gs.writeOffset - ss->gs.readOffset; + if (available == 0) { + /* Get some more data */ + if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + /* Wait for application data to arrive. */ + rv = ssl3_GatherAppDataRecord(ss, 0); + } else { + /* See if we have a complete record */ + rv = ssl2_GatherRecord(ss, 0); + } + if (rv <= 0) { + if (rv == 0) { + /* EOF */ + SSL_TRC(10, ("%d: SSL[%d]: ssl_recv EOF", + SSL_GETPID(), ss->fd)); + goto done; + } + if ((rv != SECWouldBlock) && + (PR_GetError() != PR_WOULD_BLOCK_ERROR)) { + /* Some random error */ + goto done; + } + + /* + ** Gather record is blocked waiting for more record data to + ** arrive. Try to process what we have already received + */ + } else { + /* Gather record has finished getting a complete record */ + } + + /* See if any clear data is now available */ + available = ss->gs.writeOffset - ss->gs.readOffset; + if (available == 0) { + /* + ** No partial data is available. Force error code to + ** EWOULDBLOCK so that caller will try again later. Note + ** that the error code is probably EWOULDBLOCK already, + ** but if it isn't (for example, if we received a zero + ** length record) then this will force it to be correct. + */ + 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)); + } + + /* Dole out clear data to reader */ + 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; + } + 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); + return rv; +} + +/************************************************************************/ + +SSLKEAType +ssl_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 /* NSS_ENABLE_ECC */ + 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; + + /* load the server certificate */ + if (sc->serverCert != NULL) { + CERT_DestroyCertificate(sc->serverCert); + sc->serverCert = NULL; + sc->serverKeyBits = 0; + } + /* load the server cert chain */ + if (sc->serverCertChain != NULL) { + CERT_DestroyCertificateList(sc->serverCertChain); + sc->serverCertChain = NULL; + } + if (cert) { + sc->serverCert = CERT_DupCertificate(cert); + /* get the size of the cert's public key, and remember it */ + 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; /* consumed */ + } + + /* get keyPair */ + 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; +} + +/* XXX need to protect the data that gets changed here.!! */ + +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; + } + + /* Both key and cert must have a value or be NULL */ + /* Passing a value of NULL will turn off key exchange algorithms that were + * previously turned on */ + if (!cert != !key) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* make sure the key exchange is recognized */ + if ((kea >= kt_kea_size) || (kea < kt_null)) { + PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); + return SECFailure; + } + + if (kea != ssl_FindCertKEAType(cert)) { + PORT_SetError(SSL_ERROR_CERT_KEA_MISMATCH); + return SECFailure; + } + + if (cert) { + /* get the size of the cert's public key, and remember it */ + 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; + /* XXX Maybe should be bestSlotMultiple? */ + bestSlot = PK11_GetBestSlot(keyMech, NULL /* wincx */); + 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; /* adopted by serverKeyPair */ + } + if (ssl_ConfigSecureServer(ss, cert, certChainOpt, + keyPair, kea) == SECFailure) { + goto loser; + } + + /* Only do this once because it's global. */ + 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; + + /* initialize sslv2 socket to send data in the clear. */ + 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; + + /* we don't dup the connection info. */ + + 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; + + /* XXX following code is wrong if either cx != 0 */ + 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; +} + +/* Reset sec back to its initial state. +** Caller holds any relevant locks. +*/ +void +ssl_ResetSecurityInfo(sslSecurityInfo *sec, PRBool doMemset) +{ + /* Destroy MAC */ + 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); + + /* Destroy ciphers */ + 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; + } + + /* cleanup the ci */ + 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); + } + +} + +/* +** Called from SSL_ResetHandshake (above), and +** from ssl_FreeSocket in sslsock.c +** Caller should hold relevant locks (e.g. XmitBufLock) +*/ +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; + } + + /* connect to server */ + 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; +} + +/* + * The TLS 1.2 RFC 5246, Section 7.2.1 says: + * + * Unless some other fatal alert has been transmitted, each party is + * required to send a close_notify alert before closing the write side + * of the connection. The other party MUST respond with a close_notify + * alert of its own and close down the connection immediately, + * discarding any pending writes. It is not required for the initiator + * of the close to wait for the responding close_notify alert before + * closing the read side of the connection. + * + * The second sentence requires that we send a close_notify alert when we + * have received a close_notify alert. In practice, all SSL implementations + * close the socket immediately after sending a close_notify alert (which is + * allowed by the third sentence), so responding with a close_notify alert + * would result in a write failure with the ECONNRESET error. This is why + * we don't respond with a close_notify alert. + * + * Also, in the unlikely event that the TCP pipe is full and the peer stops + * reading, the SSL3_SendAlert call in ssl_SecureClose and ssl_SecureShutdown + * may block indefinitely in blocking mode, and may fail (without retrying) + * in non-blocking mode. + */ + +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) { + + /* We don't want the final alert to be Nagle delayed. */ + 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; +} + +/* Caller handles all locking */ +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; + } + /* XXX short write? */ + } + ssl_ReleaseXmitBufLock(ss); + } + + rv = 0; + /* If any of these is non-zero, the initial handshake is not done. */ + 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); +} + +/* Caller holds the SSL Socket's write lock. SSL_LOCK_WRITER(ss) */ +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 any of these is non-zero, the initial handshake is not done. */ + if (!ss->firstHsDone) { + PRBool canFalseStart = PR_FALSE; + ssl_Get1stHandshakeLock(ss); + if (ss->version >= SSL_LIBRARY_VERSION_3_0 && + (ss->ssl3.hs.ws == wait_change_cipher || + ss->ssl3.hs.ws == wait_finished || + ss->ssl3.hs.ws == wait_new_session_ticket) && + ssl3_CanFalseStart(ss)) { + canFalseStart = PR_TRUE; + } + if (!canFalseStart && + (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { + rv = ssl_Do1stHandshake(ss); + } + ssl_Release1stHandshakeLock(ss); + } + if (rv < 0) { + ss->writerThread = NULL; + goto done; + } + + /* Check for zero length writes after we do housekeeping so we make forward + * progress. + */ + if (len == 0) { + rv = 0; + goto done; + } + PORT_Assert(buf != NULL); + if (!buf) { + PORT_SetError(PR_INVALID_ARGUMENT_ERROR); + rv = PR_FAILURE; + goto done; + } + + /* Send out the data using one of these functions: + * ssl2_SendClear, ssl2_SendStream, ssl2_SendBlock, + * ssl3_SendApplicationData + */ + 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; +} + +/* + * Allow the application to pass the url or hostname into the SSL library + * so that we can do some checking on it. It will be used for the value in + * SNI extension of client hello message. + */ +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); /* CONST */ + } + + ss->url = (const char *)PORT_Strdup(url); + if ( ss->url == NULL ) { + rv = SECFailure; + } + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + + return rv; +} + +/* + * Allow the application to pass the set of trust anchors + */ +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; +} + +/* +** Returns Negative number on error, zero or greater on success. +** Returns the amount of data immediately available to be read. +*/ +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.ci.sid); + rv = SECSuccess; + } + + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + } + 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; +} + +/* + * attempt to restart the handshake after asynchronously handling + * a request for the client's certificate. + * + * inputs: + * cert Client cert chosen by application. + * Note: ssl takes this reference, and does not bump the + * reference count. The caller should drop its reference + * without calling CERT_DestroyCert after calling this function. + * + * key Private key associated with cert. This function makes a + * copy of the private key, so the caller remains responsible + * for destroying its copy after this function returns. + * + * certChain Chain of signers for cert. + * Note: ssl takes this reference, and does not copy the chain. + * The caller should drop its reference without destroying the + * chain. SSL will free the chain when it is done with it. + * + * Return value: XXX + * + * XXX This code only works on the initial handshake on a connection, XXX + * It does not work on a subsequent handshake (redo). + */ +int +SSL_RestartHandshakeAfterCertReq(sslSocket * ss, + CERTCertificate * cert, + SECKEYPrivateKey * key, + CERTCertificateList *certChain) +{ + int ret; + + ssl_Get1stHandshakeLock(ss); /************************************/ + + if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain); + } else { + ret = ssl2_RestartHandshakeAfterCertReq(ss, cert, key); + } + + ssl_Release1stHandshakeLock(ss); /************************************/ + return ret; +} + + +/* restart an SSL connection that we stopped to run certificate dialogs +** XXX Need to document here how an application marks a cert to show that +** the application has accepted it (overridden CERT_VerifyCert). + * + * XXX This code only works on the initial handshake on a connection, XXX + * It does not work on a subsequent handshake (redo). + * + * Return value: XXX +*/ +int +SSL_RestartHandshakeAfterServerCert(sslSocket *ss) +{ + int rv = SECSuccess; + + ssl_Get1stHandshakeLock(ss); + + if (ss->version >= SSL_LIBRARY_VERSION_3_0) { + rv = ssl3_RestartHandshakeAfterServerCert(ss); + } else { + rv = ssl2_RestartHandshakeAfterServerCert(ss); + } + + ssl_Release1stHandshakeLock(ss); + return rv; +} + +/* For more info see ssl.h */ +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; +} |