summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/sslsecur.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/sslsecur.c')
-rw-r--r--security/nss/lib/ssl/sslsecur.c1235
1 files changed, 1235 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c
new file mode 100644
index 000000000..eecf44396
--- /dev/null
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -0,0 +1,1235 @@
+/*
+ * Various SSL 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 "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 */
+
+/* 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
+ * ssl_BeginClientHandshake
+ * ssl_BeginServerHandshake
+ *
+ * The ss->handshake function returns SECWouldBlock if it was returned by
+ * one of the callback functions, via one of these paths:
+ *
+ * - 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;
+
+ while (ss->handshake && rv == SECSuccess) {
+ PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss));
+
+ rv = (*ss->handshake)(ss);
+ };
+
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss));
+ PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss));
+
+ if (rv == SECWouldBlock) {
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
+ rv = SECFailure;
+ }
+ return rv;
+}
+
+void
+ssl_FinishHandshake(sslSocket *ss)
+{
+ PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
+ PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
+
+ SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd));
+
+ ss->firstHsDone = PR_TRUE;
+ ss->enoughFirstHsDone = PR_TRUE;
+ ss->gs.writeOffset = 0;
+ ss->gs.readOffset = 0;
+
+ if (ss->handshakeCallback) {
+ PORT_Assert((ss->ssl3.hs.preliminaryInfo & ssl_preinfo_all) ==
+ ssl_preinfo_all);
+ (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+ }
+
+ ssl_FreeEphemeralKeyPairs(ss);
+}
+
+/*
+ * Handshake function that blocks. Used to force a
+ * retry on a connection on the next read/write.
+ */
+static SECStatus
+ssl3_AlwaysBlock(sslSocket *ss)
+{
+ PORT_SetError(PR_WOULD_BLOCK_ERROR); /* perhaps redundant. */
+ return SECWouldBlock;
+}
+
+/*
+ * set the initial handshake state machine to block
+ */
+void
+ssl3_SetAlwaysBlock(sslSocket *ss)
+{
+ if (!ss->firstHsDone) {
+ ss->handshake = ssl3_AlwaysBlock;
+ }
+}
+
+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);
+
+ ss->firstHsDone = PR_FALSE;
+ ss->enoughFirstHsDone = PR_FALSE;
+ if (asServer) {
+ ss->handshake = ssl_BeginServerHandshake;
+ ss->handshaking = sslHandshakingAsServer;
+ } else {
+ ss->handshake = ssl_BeginClientHandshake;
+ ss->handshaking = sslHandshakingAsClient;
+ }
+
+ ssl_GetRecvBufLock(ss);
+ status = ssl3_InitGather(&ss->gs);
+ ssl_ReleaseRecvBufLock(ss);
+ if (status != SECSuccess)
+ goto loser;
+
+ ssl_GetSSL3HandshakeLock(ss);
+ ss->ssl3.hs.canFalseStart = PR_FALSE;
+ ss->ssl3.hs.restartTarget = NULL;
+
+ /*
+ ** 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);
+
+ ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
+ ssl3_ResetExtensionData(&ss->xtnData);
+
+ if (!ss->TCPconnected)
+ ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr));
+
+loser:
+ 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_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;
+}
+
+/* Register an application callback to be called when false start may happen.
+** Acquires and releases HandshakeLock.
+*/
+SECStatus
+SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
+ void *arg)
+{
+ sslSocket *ss;
+
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (!ss->opt.useSecurity) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ ssl_Get1stHandshakeLock(ss);
+ ssl_GetSSL3HandshakeLock(ss);
+
+ ss->canFalseStartCallback = cb;
+ ss->canFalseStartCallbackData = arg;
+
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ ssl_Release1stHandshakeLock(ss);
+
+ return SECSuccess;
+}
+
+SECStatus
+SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
+{
+ sslSocket *ss;
+
+ *canFalseStart = PR_FALSE;
+ ss = ssl_FindSocket(fd);
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ if (!ss->ssl3.initialized) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Require a forward-secret key exchange. */
+ *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
+ ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
+ ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
+
+ return SECSuccess;
+}
+
+/* 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.
+** 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;
+
+ if (!ssl_SocketIsBlocking(ss)) {
+ ssl_GetXmitBufLock(ss);
+ if (ss->pendingBuf.len != 0) {
+ int sent = ssl_SendSavedWriteData(ss);
+ if ((sent < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure;
+ }
+ }
+ ssl_ReleaseXmitBufLock(ss);
+ }
+
+ ssl_Get1stHandshakeLock(ss);
+
+ if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+ int gatherResult;
+
+ ssl_GetRecvBufLock(ss);
+ gatherResult = ssl3_GatherCompleteHandshake(ss, 0);
+ ssl_ReleaseRecvBufLock(ss);
+ if (gatherResult > 0) {
+ rv = SECSuccess;
+ } else if (gatherResult == 0) {
+ PORT_SetError(PR_END_OF_FILE_ERROR);
+ } else if (gatherResult == SECWouldBlock) {
+ PORT_SetError(PR_WOULD_BLOCK_ERROR);
+ }
+ } else {
+ PORT_Assert(!ss->firstHsDone);
+ rv = ssl_Do1stHandshake(ss);
+ }
+
+ 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;
+}
+
+void
+sslBuffer_Clear(sslBuffer *b)
+{
+ if (b->len > 0) {
+ PORT_Free(b->buf);
+ b->buf = NULL;
+ b->len = 0;
+ b->space = 0;
+ }
+}
+
+/*
+** 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.
+** 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;
+
+ /* ssl3_GatherAppDataRecord may call ssl_FinishHandshake, which needs the
+ * 1stHandshakeLock. */
+ ssl_Get1stHandshakeLock(ss);
+ ssl_GetRecvBufLock(ss);
+
+ available = ss->gs.writeOffset - ss->gs.readOffset;
+ if (available == 0) {
+ /* Wait for application data to arrive. */
+ rv = ssl3_GatherAppDataRecord(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));
+ }
+
+ if (IS_DTLS(ss) && (len < available)) {
+ /* DTLS does not allow you to do partial reads */
+ SSL_TRC(30, ("%d: SSL[%d]: DTLS short read. len=%d available=%d",
+ SSL_GETPID(), ss->fd, len, available));
+ ss->gs.readOffset += available;
+ PORT_SetError(SSL_ERROR_RX_SHORT_DTLS_READ);
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* 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;
+ }
+ PORT_Assert(ss->gs.readOffset <= ss->gs.writeOffset);
+ rv = amount;
+
+ SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d",
+ SSL_GETPID(), ss->fd, amount, available));
+ PRINT_BUF(4, (ss, "DoRecv receiving plaintext:", out, amount));
+
+done:
+ ssl_ReleaseRecvBufLock(ss);
+ ssl_Release1stHandshakeLock(ss);
+ return rv;
+}
+
+/************************************************************************/
+
+SECStatus
+ssl_CreateSecurityInfo(sslSocket *ss)
+{
+ SECStatus status;
+
+ ssl_GetXmitBufLock(ss);
+ status = sslBuffer_Grow(&ss->sec.writeBuf, 4096);
+ ssl_ReleaseXmitBufLock(ss);
+
+ return status;
+}
+
+SECStatus
+ssl_CopySecurityInfo(sslSocket *ss, sslSocket *os)
+{
+ ss->sec.isServer = os->sec.isServer;
+
+ 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;
+
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+
+/* Reset sec back to its initial state.
+** Caller holds any relevant locks.
+*/
+void
+ssl_ResetSecurityInfo(sslSecurityInfo *sec, PRBool doMemset)
+{
+ 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->handshake = ssl_BeginServerHandshake;
+ ss->handshaking = sslHandshakingAsServer;
+ } else {
+ ss->handshake = ssl_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->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->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)
+{
+ int rv = 0;
+
+ if (ss->shutdownHow & ssl_SHUTDOWN_RCV) {
+ PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR);
+ return PR_FAILURE;
+ }
+ if (flags & ~PR_MSG_PEEK) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return PR_FAILURE;
+ }
+
+ if (!ssl_SocketIsBlocking(ss) && !ss->opt.fdx) {
+ ssl_GetXmitBufLock(ss);
+ if (ss->pendingBuf.len != 0) {
+ rv = ssl_SendSavedWriteData(ss);
+ if ((rv < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
+ ssl_ReleaseXmitBufLock(ss);
+ return SECFailure;
+ }
+ }
+ ssl_ReleaseXmitBufLock(ss);
+ }
+
+ rv = 0;
+ if (!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.bufferedEarlyData)) {
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ return tls13_Read0RttData(ss, buf, len);
+ }
+
+ /* If any of these is non-zero, the initial handshake is not done. */
+ if (!ss->firstHsDone) {
+ ssl_Get1stHandshakeLock(ss);
+ if (ss->handshake) {
+ 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();
+
+ /* Check to see if we can write even though we're not finished.
+ *
+ * Case 1: False start
+ * Case 2: TLS 1.3 0-RTT
+ */
+ if (!ss->firstHsDone) {
+ PRBool falseStart = PR_FALSE;
+ ssl_Get1stHandshakeLock(ss);
+ if (ss->opt.enableFalseStart ||
+ (ss->opt.enable0RttData && !ss->sec.isServer)) {
+ ssl_GetSSL3HandshakeLock(ss);
+ /* The client can sometimes send before the handshake is fully
+ * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
+ falseStart = ss->ssl3.hs.canFalseStart ||
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+ ssl_ReleaseSSL3HandshakeLock(ss);
+ }
+ if (!falseStart && ss->handshake) {
+ 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;
+ }
+
+ if (!ss->firstHsDone) {
+#ifdef DEBUG
+ ssl_GetSSL3HandshakeLock(ss);
+ PORT_Assert(!ss->sec.isServer &&
+ (ss->ssl3.hs.canFalseStart ||
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+ ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted));
+ ssl_ReleaseSSL3HandshakeLock(ss);
+#endif
+ SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
+ SSL_GETPID(), ss->fd));
+ }
+
+ ssl_GetXmitBufLock(ss);
+ rv = ssl3_SendApplicationData(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;
+ 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;
+}
+
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
+ */
+int
+SSL_RestartHandshakeAfterCertReq(sslSocket *ss,
+ CERTCertificate *cert,
+ SECKEYPrivateKey *key,
+ CERTCertificateList *certChain)
+{
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return -1;
+}
+
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
+ */
+int
+SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
+{
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ return -1;
+}
+
+/* See documentation in ssl.h */
+SECStatus
+SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error)
+{
+ SECStatus rv;
+ sslSocket *ss = ssl_FindSocket(fd);
+
+ if (!ss) {
+ SSL_DBG(("%d: SSL[%d]: bad socket in SSL_AuthCertificateComplete",
+ SSL_GETPID(), fd));
+ return SECFailure;
+ }
+
+ ssl_Get1stHandshakeLock(ss);
+
+ if (!ss->ssl3.initialized) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ } else {
+ rv = ssl3_AuthCertificateComplete(ss, error);
+ }
+
+ 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;
+}