michael@0: /* michael@0: * Various SSL functions. michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "cert.h" michael@0: #include "secitem.h" michael@0: #include "keyhi.h" michael@0: #include "ssl.h" michael@0: #include "sslimpl.h" michael@0: #include "sslproto.h" michael@0: #include "secoid.h" /* for SECOID_GetALgorithmTag */ michael@0: #include "pk11func.h" /* for PK11_GenerateRandom */ michael@0: #include "nss.h" /* for NSS_RegisterShutdown */ michael@0: #include "prinit.h" /* for PR_CallOnceWithArg */ michael@0: michael@0: #define MAX_BLOCK_CYPHER_SIZE 32 michael@0: michael@0: #define TEST_FOR_FAILURE /* reminder */ michael@0: #define SET_ERROR_CODE /* reminder */ michael@0: michael@0: /* Returns a SECStatus: SECSuccess or SECFailure, NOT SECWouldBlock. michael@0: * michael@0: * Currently, the list of functions called through ss->handshake is: michael@0: * michael@0: * In sslsocks.c: michael@0: * SocksGatherRecord michael@0: * SocksHandleReply michael@0: * SocksStartGather michael@0: * michael@0: * In sslcon.c: michael@0: * ssl_GatherRecord1stHandshake michael@0: * ssl2_HandleClientSessionKeyMessage michael@0: * ssl2_HandleMessage michael@0: * ssl2_HandleVerifyMessage michael@0: * ssl2_BeginClientHandshake michael@0: * ssl2_BeginServerHandshake michael@0: * ssl2_HandleClientHelloMessage michael@0: * ssl2_HandleServerHelloMessage michael@0: * michael@0: * The ss->handshake function returns SECWouldBlock under these conditions: michael@0: * 1. ssl_GatherRecord1stHandshake called ssl2_GatherData which read in michael@0: * the beginning of an SSL v3 hello message and returned SECWouldBlock michael@0: * to switch to SSL v3 handshake processing. michael@0: * michael@0: * 2. ssl2_HandleClientHelloMessage discovered version 3.0 in the incoming michael@0: * v2 client hello msg, and called ssl3_HandleV2ClientHello which michael@0: * returned SECWouldBlock. michael@0: * michael@0: * 3. SECWouldBlock was returned by one of the callback functions, via michael@0: * one of these paths: michael@0: * - ssl2_HandleMessage() -> ssl2_HandleRequestCertificate() -> michael@0: * ss->getClientAuthData() michael@0: * michael@0: * - ssl2_HandleServerHelloMessage() -> ss->handleBadCert() michael@0: * michael@0: * - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() -> michael@0: * ssl3_HandleRecord() -> ssl3_HandleHandshake() -> michael@0: * ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificate() -> michael@0: * ss->handleBadCert() michael@0: * michael@0: * - ssl_GatherRecord1stHandshake() -> ssl3_GatherCompleteHandshake() -> michael@0: * ssl3_HandleRecord() -> ssl3_HandleHandshake() -> michael@0: * ssl3_HandleHandshakeMessage() -> ssl3_HandleCertificateRequest() -> michael@0: * ss->getClientAuthData() michael@0: * michael@0: * Called from: SSL_ForceHandshake (below), michael@0: * ssl_SecureRecv (below) and michael@0: * ssl_SecureSend (below) michael@0: * from: WaitForResponse in sslsocks.c michael@0: * ssl_SocksRecv in sslsocks.c michael@0: * ssl_SocksSend in sslsocks.c michael@0: * michael@0: * Caller must hold the (write) handshakeLock. michael@0: */ michael@0: int michael@0: ssl_Do1stHandshake(sslSocket *ss) michael@0: { michael@0: int rv = SECSuccess; michael@0: int loopCount = 0; michael@0: michael@0: do { michael@0: PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss)); michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss)); michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss)); michael@0: michael@0: if (ss->handshake == 0) { michael@0: /* Previous handshake finished. Switch to next one */ michael@0: ss->handshake = ss->nextHandshake; michael@0: ss->nextHandshake = 0; michael@0: } michael@0: if (ss->handshake == 0) { michael@0: /* Previous handshake finished. Switch to security handshake */ michael@0: ss->handshake = ss->securityHandshake; michael@0: ss->securityHandshake = 0; michael@0: } michael@0: if (ss->handshake == 0) { michael@0: /* for v3 this is done in ssl3_FinishHandshake */ michael@0: if (!ss->firstHsDone && ss->version < SSL_LIBRARY_VERSION_3_0) { michael@0: ssl_GetRecvBufLock(ss); michael@0: ss->gs.recordLen = 0; michael@0: ssl_FinishHandshake(ss); michael@0: ssl_ReleaseRecvBufLock(ss); michael@0: } michael@0: break; michael@0: } michael@0: rv = (*ss->handshake)(ss); michael@0: ++loopCount; michael@0: /* This code must continue to loop on SECWouldBlock, michael@0: * or any positive value. See XXX_1 comments. michael@0: */ michael@0: } while (rv != SECFailure); /* was (rv >= 0); XXX_1 */ michael@0: michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveRecvBufLock(ss)); michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveXmitBufLock(ss)); michael@0: PORT_Assert(ss->opt.noLocks || !ssl_HaveSSL3HandshakeLock(ss)); michael@0: michael@0: if (rv == SECWouldBlock) { michael@0: PORT_SetError(PR_WOULD_BLOCK_ERROR); michael@0: rv = SECFailure; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: ssl_FinishHandshake(sslSocket *ss) michael@0: { michael@0: PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) ); michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); michael@0: michael@0: SSL_TRC(3, ("%d: SSL[%d]: handshake is completed", SSL_GETPID(), ss->fd)); michael@0: michael@0: ss->firstHsDone = PR_TRUE; michael@0: ss->enoughFirstHsDone = PR_TRUE; michael@0: ss->gs.writeOffset = 0; michael@0: ss->gs.readOffset = 0; michael@0: michael@0: if (ss->handshakeCallback) { michael@0: (ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Handshake function that blocks. Used to force a michael@0: * retry on a connection on the next read/write. michael@0: */ michael@0: static SECStatus michael@0: ssl3_AlwaysBlock(sslSocket *ss) michael@0: { michael@0: PORT_SetError(PR_WOULD_BLOCK_ERROR); /* perhaps redundant. */ michael@0: return SECWouldBlock; michael@0: } michael@0: michael@0: /* michael@0: * set the initial handshake state machine to block michael@0: */ michael@0: void michael@0: ssl3_SetAlwaysBlock(sslSocket *ss) michael@0: { michael@0: if (!ss->firstHsDone) { michael@0: ss->handshake = ssl3_AlwaysBlock; michael@0: ss->nextHandshake = 0; michael@0: } michael@0: } michael@0: michael@0: static SECStatus michael@0: ssl_SetTimeout(PRFileDesc *fd, PRIntervalTime timeout) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SetTimeout", SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: SSL_LOCK_READER(ss); michael@0: ss->rTimeout = timeout; michael@0: if (ss->opt.fdx) { michael@0: SSL_LOCK_WRITER(ss); michael@0: } michael@0: ss->wTimeout = timeout; michael@0: if (ss->opt.fdx) { michael@0: SSL_UNLOCK_WRITER(ss); michael@0: } michael@0: SSL_UNLOCK_READER(ss); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* Acquires and releases HandshakeLock. michael@0: */ michael@0: SECStatus michael@0: SSL_ResetHandshake(PRFileDesc *s, PRBool asServer) michael@0: { michael@0: sslSocket *ss; michael@0: SECStatus status; michael@0: PRNetAddr addr; michael@0: michael@0: ss = ssl_FindSocket(s); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in ResetHandshake", SSL_GETPID(), s)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Don't waste my time */ michael@0: if (!ss->opt.useSecurity) michael@0: return SECSuccess; michael@0: michael@0: SSL_LOCK_READER(ss); michael@0: SSL_LOCK_WRITER(ss); michael@0: michael@0: /* Reset handshake state */ michael@0: ssl_Get1stHandshakeLock(ss); michael@0: michael@0: ss->firstHsDone = PR_FALSE; michael@0: ss->enoughFirstHsDone = PR_FALSE; michael@0: if ( asServer ) { michael@0: ss->handshake = ssl2_BeginServerHandshake; michael@0: ss->handshaking = sslHandshakingAsServer; michael@0: } else { michael@0: ss->handshake = ssl2_BeginClientHandshake; michael@0: ss->handshaking = sslHandshakingAsClient; michael@0: } michael@0: ss->nextHandshake = 0; michael@0: ss->securityHandshake = 0; michael@0: michael@0: ssl_GetRecvBufLock(ss); michael@0: status = ssl_InitGather(&ss->gs); michael@0: ssl_ReleaseRecvBufLock(ss); michael@0: michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: ss->ssl3.hs.canFalseStart = PR_FALSE; michael@0: ss->ssl3.hs.restartTarget = NULL; michael@0: michael@0: /* michael@0: ** Blow away old security state and get a fresh setup. michael@0: */ michael@0: ssl_GetXmitBufLock(ss); michael@0: ssl_ResetSecurityInfo(&ss->sec, PR_TRUE); michael@0: status = ssl_CreateSecurityInfo(ss); michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: if (!ss->TCPconnected) michael@0: ss->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ss, &addr)); michael@0: michael@0: SSL_UNLOCK_WRITER(ss); michael@0: SSL_UNLOCK_READER(ss); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: /* For SSLv2, does nothing but return an error. michael@0: ** For SSLv3, flushes SID cache entry (if requested), michael@0: ** and then starts new client hello or hello request. michael@0: ** Acquires and releases HandshakeLock. michael@0: */ michael@0: SECStatus michael@0: SSL_ReHandshake(PRFileDesc *fd, PRBool flushCache) michael@0: { michael@0: sslSocket *ss; michael@0: SECStatus rv; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in RedoHandshake", SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (!ss->opt.useSecurity) michael@0: return SECSuccess; michael@0: michael@0: ssl_Get1stHandshakeLock(ss); michael@0: michael@0: /* SSL v2 protocol does not support subsequent handshakes. */ michael@0: if (ss->version < SSL_LIBRARY_VERSION_3_0) { michael@0: PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); michael@0: rv = SECFailure; michael@0: } else { michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: rv = ssl3_RedoHandshake(ss, flushCache); /* force full handshake. */ michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: } michael@0: michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Same as above, but with an I/O timeout. michael@0: */ michael@0: SSL_IMPORT SECStatus SSL_ReHandshakeWithTimeout(PRFileDesc *fd, michael@0: PRBool flushCache, michael@0: PRIntervalTime timeout) michael@0: { michael@0: if (SECSuccess != ssl_SetTimeout(fd, timeout)) { michael@0: return SECFailure; michael@0: } michael@0: return SSL_ReHandshake(fd, flushCache); michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_RedoHandshake(PRFileDesc *fd) michael@0: { michael@0: return SSL_ReHandshake(fd, PR_TRUE); michael@0: } michael@0: michael@0: /* Register an application callback to be called when SSL handshake completes. michael@0: ** Acquires and releases HandshakeLock. michael@0: */ michael@0: SECStatus michael@0: SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb, michael@0: void *client_data) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in HandshakeCallback", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (!ss->opt.useSecurity) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: michael@0: ss->handshakeCallback = cb; michael@0: ss->handshakeCallbackData = client_data; michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* Register an application callback to be called when false start may happen. michael@0: ** Acquires and releases HandshakeLock. michael@0: */ michael@0: SECStatus michael@0: SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb, michael@0: void *arg) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (!ss->opt.useSecurity) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: michael@0: ss->canFalseStartCallback = cb; michael@0: ss->canFalseStartCallbackData = arg; michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_RecommendedCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: *canFalseStart = PR_FALSE; michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RecommendedCanFalseStart", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (!ss->ssl3.initialized) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (ss->version < SSL_LIBRARY_VERSION_3_0) { michael@0: PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Require a forward-secret key exchange. */ michael@0: *canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss || michael@0: ss->ssl3.hs.kea_def->kea == kea_dhe_rsa || michael@0: ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa || michael@0: ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* Try to make progress on an SSL handshake by attempting to read the michael@0: ** next handshake from the peer, and sending any responses. michael@0: ** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot michael@0: ** read the next handshake from the underlying socket. michael@0: ** For SSLv2, returns when handshake is complete or fatal error occurs. michael@0: ** For SSLv3, returns when handshake is complete, or application data has michael@0: ** arrived that must be taken by application before handshake can continue, michael@0: ** or a fatal error occurs. michael@0: ** Application should use handshake completion callback to tell which. michael@0: */ michael@0: SECStatus michael@0: SSL_ForceHandshake(PRFileDesc *fd) michael@0: { michael@0: sslSocket *ss; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in ForceHandshake", michael@0: SSL_GETPID(), fd)); michael@0: return rv; michael@0: } michael@0: michael@0: /* Don't waste my time */ michael@0: if (!ss->opt.useSecurity) michael@0: return SECSuccess; michael@0: michael@0: if (!ssl_SocketIsBlocking(ss)) { michael@0: ssl_GetXmitBufLock(ss); michael@0: if (ss->pendingBuf.len != 0) { michael@0: int sent = ssl_SendSavedWriteData(ss); michael@0: if ((sent < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) { michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: } michael@0: michael@0: ssl_Get1stHandshakeLock(ss); michael@0: michael@0: if (ss->version >= SSL_LIBRARY_VERSION_3_0) { michael@0: int gatherResult; michael@0: michael@0: ssl_GetRecvBufLock(ss); michael@0: gatherResult = ssl3_GatherCompleteHandshake(ss, 0); michael@0: ssl_ReleaseRecvBufLock(ss); michael@0: if (gatherResult > 0) { michael@0: rv = SECSuccess; michael@0: } else if (gatherResult == 0) { michael@0: PORT_SetError(PR_END_OF_FILE_ERROR); michael@0: } else if (gatherResult == SECWouldBlock) { michael@0: PORT_SetError(PR_WOULD_BLOCK_ERROR); michael@0: } michael@0: } else if (!ss->firstHsDone) { michael@0: rv = ssl_Do1stHandshake(ss); michael@0: } else { michael@0: /* tried to force handshake on an SSL 2 socket that has michael@0: ** already completed the handshake. */ michael@0: rv = SECSuccess; /* just pretend we did it. */ michael@0: } michael@0: michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Same as above, but with an I/O timeout. michael@0: */ michael@0: SSL_IMPORT SECStatus SSL_ForceHandshakeWithTimeout(PRFileDesc *fd, michael@0: PRIntervalTime timeout) michael@0: { michael@0: if (SECSuccess != ssl_SetTimeout(fd, timeout)) { michael@0: return SECFailure; michael@0: } michael@0: return SSL_ForceHandshake(fd); michael@0: } michael@0: michael@0: michael@0: /************************************************************************/ michael@0: michael@0: /* michael@0: ** Grow a buffer to hold newLen bytes of data. michael@0: ** Called for both recv buffers and xmit buffers. michael@0: ** Caller must hold xmitBufLock or recvBufLock, as appropriate. michael@0: */ michael@0: SECStatus michael@0: sslBuffer_Grow(sslBuffer *b, unsigned int newLen) michael@0: { michael@0: newLen = PR_MAX(newLen, MAX_FRAGMENT_LENGTH + 2048); michael@0: if (newLen > b->space) { michael@0: unsigned char *newBuf; michael@0: if (b->buf) { michael@0: newBuf = (unsigned char *) PORT_Realloc(b->buf, newLen); michael@0: } else { michael@0: newBuf = (unsigned char *) PORT_Alloc(newLen); michael@0: } michael@0: if (!newBuf) { michael@0: return SECFailure; michael@0: } michael@0: SSL_TRC(10, ("%d: SSL: grow buffer from %d to %d", michael@0: SSL_GETPID(), b->space, newLen)); michael@0: b->buf = newBuf; michael@0: b->space = newLen; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: sslBuffer_Append(sslBuffer *b, const void * data, unsigned int len) michael@0: { michael@0: unsigned int newLen = b->len + len; michael@0: SECStatus rv; michael@0: michael@0: rv = sslBuffer_Grow(b, newLen); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: PORT_Memcpy(b->buf + b->len, data, len); michael@0: b->len += len; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: ** Save away write data that is trying to be written before the security michael@0: ** handshake has been completed. When the handshake is completed, we will michael@0: ** flush this data out. michael@0: ** Caller must hold xmitBufLock michael@0: */ michael@0: SECStatus michael@0: ssl_SaveWriteData(sslSocket *ss, const void *data, unsigned int len) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); michael@0: rv = sslBuffer_Append(&ss->pendingBuf, data, len); michael@0: SSL_TRC(5, ("%d: SSL[%d]: saving %u bytes of data (%u total saved so far)", michael@0: SSL_GETPID(), ss->fd, len, ss->pendingBuf.len)); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Send saved write data. This will flush out data sent prior to a michael@0: ** complete security handshake. Hopefully there won't be too much of it. michael@0: ** Returns count of the bytes sent, NOT a SECStatus. michael@0: ** Caller must hold xmitBufLock michael@0: */ michael@0: int michael@0: ssl_SendSavedWriteData(sslSocket *ss) michael@0: { michael@0: int rv = 0; michael@0: michael@0: PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss) ); michael@0: if (ss->pendingBuf.len != 0) { michael@0: SSL_TRC(5, ("%d: SSL[%d]: sending %d bytes of saved data", michael@0: SSL_GETPID(), ss->fd, ss->pendingBuf.len)); michael@0: rv = ssl_DefSend(ss, ss->pendingBuf.buf, ss->pendingBuf.len, 0); michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: ss->pendingBuf.len -= rv; michael@0: if (ss->pendingBuf.len > 0 && rv > 0) { michael@0: /* UGH !! This shifts the whole buffer down by copying it */ michael@0: PORT_Memmove(ss->pendingBuf.buf, ss->pendingBuf.buf + rv, michael@0: ss->pendingBuf.len); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: /* michael@0: ** Receive some application data on a socket. Reads SSL records from the input michael@0: ** stream, decrypts them and then copies them to the output buffer. michael@0: ** Called from ssl_SecureRecv() below. michael@0: ** michael@0: ** Caller does NOT hold 1stHandshakeLock because that handshake is over. michael@0: ** Caller doesn't call this until initial handshake is complete. michael@0: ** For SSLv2, there is no subsequent handshake. michael@0: ** For SSLv3, the call to ssl3_GatherAppDataRecord may encounter handshake michael@0: ** messages from a subsequent handshake. michael@0: ** michael@0: ** This code is similar to, and easily confused with, michael@0: ** ssl_GatherRecord1stHandshake() in sslcon.c michael@0: */ michael@0: static int michael@0: DoRecv(sslSocket *ss, unsigned char *out, int len, int flags) michael@0: { michael@0: int rv; michael@0: int amount; michael@0: int available; michael@0: michael@0: /* ssl3_GatherAppDataRecord may call ssl_FinishHandshake, which needs the michael@0: * 1stHandshakeLock. */ michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetRecvBufLock(ss); michael@0: michael@0: available = ss->gs.writeOffset - ss->gs.readOffset; michael@0: if (available == 0) { michael@0: /* Get some more data */ michael@0: if (ss->version >= SSL_LIBRARY_VERSION_3_0) { michael@0: /* Wait for application data to arrive. */ michael@0: rv = ssl3_GatherAppDataRecord(ss, 0); michael@0: } else { michael@0: /* See if we have a complete record */ michael@0: rv = ssl2_GatherRecord(ss, 0); michael@0: } michael@0: if (rv <= 0) { michael@0: if (rv == 0) { michael@0: /* EOF */ michael@0: SSL_TRC(10, ("%d: SSL[%d]: ssl_recv EOF", michael@0: SSL_GETPID(), ss->fd)); michael@0: goto done; michael@0: } michael@0: if ((rv != SECWouldBlock) && michael@0: (PR_GetError() != PR_WOULD_BLOCK_ERROR)) { michael@0: /* Some random error */ michael@0: goto done; michael@0: } michael@0: michael@0: /* michael@0: ** Gather record is blocked waiting for more record data to michael@0: ** arrive. Try to process what we have already received michael@0: */ michael@0: } else { michael@0: /* Gather record has finished getting a complete record */ michael@0: } michael@0: michael@0: /* See if any clear data is now available */ michael@0: available = ss->gs.writeOffset - ss->gs.readOffset; michael@0: if (available == 0) { michael@0: /* michael@0: ** No partial data is available. Force error code to michael@0: ** EWOULDBLOCK so that caller will try again later. Note michael@0: ** that the error code is probably EWOULDBLOCK already, michael@0: ** but if it isn't (for example, if we received a zero michael@0: ** length record) then this will force it to be correct. michael@0: */ michael@0: PORT_SetError(PR_WOULD_BLOCK_ERROR); michael@0: rv = SECFailure; michael@0: goto done; michael@0: } michael@0: SSL_TRC(30, ("%d: SSL[%d]: partial data ready, available=%d", michael@0: SSL_GETPID(), ss->fd, available)); michael@0: } michael@0: michael@0: /* Dole out clear data to reader */ michael@0: amount = PR_MIN(len, available); michael@0: PORT_Memcpy(out, ss->gs.buf.buf + ss->gs.readOffset, amount); michael@0: if (!(flags & PR_MSG_PEEK)) { michael@0: ss->gs.readOffset += amount; michael@0: } michael@0: PORT_Assert(ss->gs.readOffset <= ss->gs.writeOffset); michael@0: rv = amount; michael@0: michael@0: SSL_TRC(30, ("%d: SSL[%d]: amount=%d available=%d", michael@0: SSL_GETPID(), ss->fd, amount, available)); michael@0: PRINT_BUF(4, (ss, "DoRecv receiving plaintext:", out, amount)); michael@0: michael@0: done: michael@0: ssl_ReleaseRecvBufLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: return rv; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: /* michael@0: ** Return SSLKEAType derived from cert's Public Key algorithm info. michael@0: */ michael@0: SSLKEAType michael@0: NSS_FindCertKEAType(CERTCertificate * cert) michael@0: { michael@0: SSLKEAType keaType = kt_null; michael@0: int tag; michael@0: michael@0: if (!cert) goto loser; michael@0: michael@0: tag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); michael@0: michael@0: switch (tag) { michael@0: case SEC_OID_X500_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: keaType = kt_rsa; michael@0: break; michael@0: case SEC_OID_X942_DIFFIE_HELMAN_KEY: michael@0: keaType = kt_dh; michael@0: break; michael@0: #ifndef NSS_DISABLE_ECC michael@0: case SEC_OID_ANSIX962_EC_PUBLIC_KEY: michael@0: keaType = kt_ecdh; michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: default: michael@0: keaType = kt_null; michael@0: } michael@0: michael@0: loser: michael@0: michael@0: return keaType; michael@0: } michael@0: michael@0: static const PRCallOnceType pristineCallOnce; michael@0: static PRCallOnceType setupServerCAListOnce; michael@0: michael@0: static SECStatus serverCAListShutdown(void* appData, void* nssData) michael@0: { michael@0: PORT_Assert(ssl3_server_ca_list); michael@0: if (ssl3_server_ca_list) { michael@0: CERT_FreeDistNames(ssl3_server_ca_list); michael@0: ssl3_server_ca_list = NULL; michael@0: } michael@0: setupServerCAListOnce = pristineCallOnce; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: static PRStatus serverCAListSetup(void *arg) michael@0: { michael@0: CERTCertDBHandle *dbHandle = (CERTCertDBHandle *)arg; michael@0: SECStatus rv = NSS_RegisterShutdown(serverCAListShutdown, NULL); michael@0: PORT_Assert(SECSuccess == rv); michael@0: if (SECSuccess == rv) { michael@0: ssl3_server_ca_list = CERT_GetSSLCACerts(dbHandle); michael@0: return PR_SUCCESS; michael@0: } michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: SECStatus michael@0: ssl_ConfigSecureServer(sslSocket *ss, CERTCertificate *cert, michael@0: const CERTCertificateList *certChain, michael@0: ssl3KeyPair *keyPair, SSLKEAType kea) michael@0: { michael@0: CERTCertificateList *localCertChain = NULL; michael@0: sslServerCerts *sc = ss->serverCerts + kea; michael@0: michael@0: /* load the server certificate */ michael@0: if (sc->serverCert != NULL) { michael@0: CERT_DestroyCertificate(sc->serverCert); michael@0: sc->serverCert = NULL; michael@0: sc->serverKeyBits = 0; michael@0: } michael@0: /* load the server cert chain */ michael@0: if (sc->serverCertChain != NULL) { michael@0: CERT_DestroyCertificateList(sc->serverCertChain); michael@0: sc->serverCertChain = NULL; michael@0: } michael@0: if (cert) { michael@0: sc->serverCert = CERT_DupCertificate(cert); michael@0: /* get the size of the cert's public key, and remember it */ michael@0: sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->pubKey); michael@0: if (!certChain) { michael@0: localCertChain = michael@0: CERT_CertChainFromCert(sc->serverCert, certUsageSSLServer, michael@0: PR_TRUE); michael@0: if (!localCertChain) michael@0: goto loser; michael@0: } michael@0: sc->serverCertChain = (certChain) ? CERT_DupCertList(certChain) : michael@0: localCertChain; michael@0: if (!sc->serverCertChain) { michael@0: goto loser; michael@0: } michael@0: localCertChain = NULL; /* consumed */ michael@0: } michael@0: michael@0: /* get keyPair */ michael@0: if (sc->serverKeyPair != NULL) { michael@0: ssl3_FreeKeyPair(sc->serverKeyPair); michael@0: sc->serverKeyPair = NULL; michael@0: } michael@0: if (keyPair) { michael@0: SECKEY_CacheStaticFlags(keyPair->privKey); michael@0: sc->serverKeyPair = ssl3_GetKeyPairRef(keyPair); michael@0: } michael@0: if (kea == kt_rsa && cert && sc->serverKeyBits > 512 && michael@0: !ss->opt.noStepDown && !ss->stepDownKeyPair) { michael@0: if (ssl3_CreateRSAStepDownKeys(ss) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if (localCertChain) { michael@0: CERT_DestroyCertificateList(localCertChain); michael@0: } michael@0: if (sc->serverCert != NULL) { michael@0: CERT_DestroyCertificate(sc->serverCert); michael@0: sc->serverCert = NULL; michael@0: } michael@0: if (sc->serverCertChain != NULL) { michael@0: CERT_DestroyCertificateList(sc->serverCertChain); michael@0: sc->serverCertChain = NULL; michael@0: } michael@0: if (sc->serverKeyPair != NULL) { michael@0: ssl3_FreeKeyPair(sc->serverKeyPair); michael@0: sc->serverKeyPair = NULL; michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* XXX need to protect the data that gets changed here.!! */ michael@0: michael@0: SECStatus michael@0: SSL_ConfigSecureServer(PRFileDesc *fd, CERTCertificate *cert, michael@0: SECKEYPrivateKey *key, SSL3KEAType kea) michael@0: { michael@0: michael@0: return SSL_ConfigSecureServerWithCertChain(fd, cert, NULL, key, kea); michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_ConfigSecureServerWithCertChain(PRFileDesc *fd, CERTCertificate *cert, michael@0: const CERTCertificateList *certChainOpt, michael@0: SECKEYPrivateKey *key, SSL3KEAType kea) michael@0: { michael@0: sslSocket *ss; michael@0: SECKEYPublicKey *pubKey = NULL; michael@0: ssl3KeyPair *keyPair = NULL; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Both key and cert must have a value or be NULL */ michael@0: /* Passing a value of NULL will turn off key exchange algorithms that were michael@0: * previously turned on */ michael@0: if (!cert != !key) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* make sure the key exchange is recognized */ michael@0: if ((kea >= kt_kea_size) || (kea < kt_null)) { michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (kea != NSS_FindCertKEAType(cert)) { michael@0: PORT_SetError(SSL_ERROR_CERT_KEA_MISMATCH); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (cert) { michael@0: /* get the size of the cert's public key, and remember it */ michael@0: pubKey = CERT_ExtractPublicKey(cert); michael@0: if (!pubKey) michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (key) { michael@0: SECKEYPrivateKey * keyCopy = NULL; michael@0: CK_MECHANISM_TYPE keyMech = CKM_INVALID_MECHANISM; michael@0: michael@0: if (key->pkcs11Slot) { michael@0: PK11SlotInfo * bestSlot; michael@0: bestSlot = PK11_ReferenceSlot(key->pkcs11Slot); michael@0: if (bestSlot) { michael@0: keyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key); michael@0: PK11_FreeSlot(bestSlot); michael@0: } michael@0: } michael@0: if (keyCopy == NULL) michael@0: keyMech = PK11_MapSignKeyType(key->keyType); michael@0: if (keyMech != CKM_INVALID_MECHANISM) { michael@0: PK11SlotInfo * bestSlot; michael@0: /* XXX Maybe should be bestSlotMultiple? */ michael@0: bestSlot = PK11_GetBestSlot(keyMech, NULL /* wincx */); michael@0: if (bestSlot) { michael@0: keyCopy = PK11_CopyTokenPrivKeyToSessionPrivKey(bestSlot, key); michael@0: PK11_FreeSlot(bestSlot); michael@0: } michael@0: } michael@0: if (keyCopy == NULL) michael@0: keyCopy = SECKEY_CopyPrivateKey(key); michael@0: if (keyCopy == NULL) michael@0: goto loser; michael@0: keyPair = ssl3_NewKeyPair(keyCopy, pubKey); michael@0: if (keyPair == NULL) { michael@0: SECKEY_DestroyPrivateKey(keyCopy); michael@0: goto loser; michael@0: } michael@0: pubKey = NULL; /* adopted by serverKeyPair */ michael@0: } michael@0: if (ssl_ConfigSecureServer(ss, cert, certChainOpt, michael@0: keyPair, kea) == SECFailure) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* Only do this once because it's global. */ michael@0: if (PR_SUCCESS == PR_CallOnceWithArg(&setupServerCAListOnce, michael@0: &serverCAListSetup, michael@0: (void *)(ss->dbHandle))) { michael@0: rv = SECSuccess; michael@0: } michael@0: michael@0: loser: michael@0: if (keyPair) { michael@0: ssl3_FreeKeyPair(keyPair); michael@0: } michael@0: if (pubKey) { michael@0: SECKEY_DestroyPublicKey(pubKey); michael@0: pubKey = NULL; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: SECStatus michael@0: ssl_CreateSecurityInfo(sslSocket *ss) michael@0: { michael@0: SECStatus status; michael@0: michael@0: /* initialize sslv2 socket to send data in the clear. */ michael@0: ssl2_UseClearSendFunc(ss); michael@0: michael@0: ss->sec.blockSize = 1; michael@0: ss->sec.blockShift = 0; michael@0: michael@0: ssl_GetXmitBufLock(ss); michael@0: status = sslBuffer_Grow(&ss->sec.writeBuf, 4096); michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: SECStatus michael@0: ssl_CopySecurityInfo(sslSocket *ss, sslSocket *os) michael@0: { michael@0: ss->sec.send = os->sec.send; michael@0: ss->sec.isServer = os->sec.isServer; michael@0: ss->sec.keyBits = os->sec.keyBits; michael@0: ss->sec.secretKeyBits = os->sec.secretKeyBits; michael@0: michael@0: ss->sec.peerCert = CERT_DupCertificate(os->sec.peerCert); michael@0: if (os->sec.peerCert && !ss->sec.peerCert) michael@0: goto loser; michael@0: michael@0: ss->sec.cache = os->sec.cache; michael@0: ss->sec.uncache = os->sec.uncache; michael@0: michael@0: /* we don't dup the connection info. */ michael@0: michael@0: ss->sec.sendSequence = os->sec.sendSequence; michael@0: ss->sec.rcvSequence = os->sec.rcvSequence; michael@0: michael@0: if (os->sec.hash && os->sec.hashcx) { michael@0: ss->sec.hash = os->sec.hash; michael@0: ss->sec.hashcx = os->sec.hash->clone(os->sec.hashcx); michael@0: if (os->sec.hashcx && !ss->sec.hashcx) michael@0: goto loser; michael@0: } else { michael@0: ss->sec.hash = NULL; michael@0: ss->sec.hashcx = NULL; michael@0: } michael@0: michael@0: SECITEM_CopyItem(0, &ss->sec.sendSecret, &os->sec.sendSecret); michael@0: if (os->sec.sendSecret.data && !ss->sec.sendSecret.data) michael@0: goto loser; michael@0: SECITEM_CopyItem(0, &ss->sec.rcvSecret, &os->sec.rcvSecret); michael@0: if (os->sec.rcvSecret.data && !ss->sec.rcvSecret.data) michael@0: goto loser; michael@0: michael@0: /* XXX following code is wrong if either cx != 0 */ michael@0: PORT_Assert(os->sec.readcx == 0); michael@0: PORT_Assert(os->sec.writecx == 0); michael@0: ss->sec.readcx = os->sec.readcx; michael@0: ss->sec.writecx = os->sec.writecx; michael@0: ss->sec.destroy = 0; michael@0: michael@0: ss->sec.enc = os->sec.enc; michael@0: ss->sec.dec = os->sec.dec; michael@0: michael@0: ss->sec.blockShift = os->sec.blockShift; michael@0: ss->sec.blockSize = os->sec.blockSize; michael@0: michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* Reset sec back to its initial state. michael@0: ** Caller holds any relevant locks. michael@0: */ michael@0: void michael@0: ssl_ResetSecurityInfo(sslSecurityInfo *sec, PRBool doMemset) michael@0: { michael@0: /* Destroy MAC */ michael@0: if (sec->hash && sec->hashcx) { michael@0: (*sec->hash->destroy)(sec->hashcx, PR_TRUE); michael@0: sec->hashcx = NULL; michael@0: sec->hash = NULL; michael@0: } michael@0: SECITEM_ZfreeItem(&sec->sendSecret, PR_FALSE); michael@0: SECITEM_ZfreeItem(&sec->rcvSecret, PR_FALSE); michael@0: michael@0: /* Destroy ciphers */ michael@0: if (sec->destroy) { michael@0: (*sec->destroy)(sec->readcx, PR_TRUE); michael@0: (*sec->destroy)(sec->writecx, PR_TRUE); michael@0: sec->readcx = NULL; michael@0: sec->writecx = NULL; michael@0: } else { michael@0: PORT_Assert(sec->readcx == 0); michael@0: PORT_Assert(sec->writecx == 0); michael@0: } michael@0: sec->readcx = 0; michael@0: sec->writecx = 0; michael@0: michael@0: if (sec->localCert) { michael@0: CERT_DestroyCertificate(sec->localCert); michael@0: sec->localCert = NULL; michael@0: } michael@0: if (sec->peerCert) { michael@0: CERT_DestroyCertificate(sec->peerCert); michael@0: sec->peerCert = NULL; michael@0: } michael@0: if (sec->peerKey) { michael@0: SECKEY_DestroyPublicKey(sec->peerKey); michael@0: sec->peerKey = NULL; michael@0: } michael@0: michael@0: /* cleanup the ci */ michael@0: if (sec->ci.sid != NULL) { michael@0: ssl_FreeSID(sec->ci.sid); michael@0: } michael@0: PORT_ZFree(sec->ci.sendBuf.buf, sec->ci.sendBuf.space); michael@0: if (doMemset) { michael@0: memset(&sec->ci, 0, sizeof sec->ci); michael@0: } michael@0: michael@0: } michael@0: michael@0: /* michael@0: ** Called from SSL_ResetHandshake (above), and michael@0: ** from ssl_FreeSocket in sslsock.c michael@0: ** Caller should hold relevant locks (e.g. XmitBufLock) michael@0: */ michael@0: void michael@0: ssl_DestroySecurityInfo(sslSecurityInfo *sec) michael@0: { michael@0: ssl_ResetSecurityInfo(sec, PR_FALSE); michael@0: michael@0: PORT_ZFree(sec->writeBuf.buf, sec->writeBuf.space); michael@0: sec->writeBuf.buf = 0; michael@0: michael@0: memset(sec, 0, sizeof *sec); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: int michael@0: ssl_SecureConnect(sslSocket *ss, const PRNetAddr *sa) michael@0: { michael@0: PRFileDesc *osfd = ss->fd->lower; michael@0: int rv; michael@0: michael@0: if ( ss->opt.handshakeAsServer ) { michael@0: ss->securityHandshake = ssl2_BeginServerHandshake; michael@0: ss->handshaking = sslHandshakingAsServer; michael@0: } else { michael@0: ss->securityHandshake = ssl2_BeginClientHandshake; michael@0: ss->handshaking = sslHandshakingAsClient; michael@0: } michael@0: michael@0: /* connect to server */ michael@0: rv = osfd->methods->connect(osfd, sa, ss->cTimeout); michael@0: if (rv == PR_SUCCESS) { michael@0: ss->TCPconnected = 1; michael@0: } else { michael@0: int err = PR_GetError(); michael@0: SSL_DBG(("%d: SSL[%d]: connect failed, errno=%d", michael@0: SSL_GETPID(), ss->fd, err)); michael@0: if (err == PR_IS_CONNECTED_ERROR) { michael@0: ss->TCPconnected = 1; michael@0: } michael@0: } michael@0: michael@0: SSL_TRC(5, ("%d: SSL[%d]: secure connect completed, rv == %d", michael@0: SSL_GETPID(), ss->fd, rv)); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * The TLS 1.2 RFC 5246, Section 7.2.1 says: michael@0: * michael@0: * Unless some other fatal alert has been transmitted, each party is michael@0: * required to send a close_notify alert before closing the write side michael@0: * of the connection. The other party MUST respond with a close_notify michael@0: * alert of its own and close down the connection immediately, michael@0: * discarding any pending writes. It is not required for the initiator michael@0: * of the close to wait for the responding close_notify alert before michael@0: * closing the read side of the connection. michael@0: * michael@0: * The second sentence requires that we send a close_notify alert when we michael@0: * have received a close_notify alert. In practice, all SSL implementations michael@0: * close the socket immediately after sending a close_notify alert (which is michael@0: * allowed by the third sentence), so responding with a close_notify alert michael@0: * would result in a write failure with the ECONNRESET error. This is why michael@0: * we don't respond with a close_notify alert. michael@0: * michael@0: * Also, in the unlikely event that the TCP pipe is full and the peer stops michael@0: * reading, the SSL3_SendAlert call in ssl_SecureClose and ssl_SecureShutdown michael@0: * may block indefinitely in blocking mode, and may fail (without retrying) michael@0: * in non-blocking mode. michael@0: */ michael@0: michael@0: int michael@0: ssl_SecureClose(sslSocket *ss) michael@0: { michael@0: int rv; michael@0: michael@0: if (ss->version >= SSL_LIBRARY_VERSION_3_0 && michael@0: !(ss->shutdownHow & ssl_SHUTDOWN_SEND) && michael@0: ss->firstHsDone && michael@0: !ss->recvdCloseNotify && michael@0: ss->ssl3.initialized) { michael@0: michael@0: /* We don't want the final alert to be Nagle delayed. */ michael@0: if (!ss->delayDisabled) { michael@0: ssl_EnableNagleDelay(ss, PR_FALSE); michael@0: ss->delayDisabled = 1; michael@0: } michael@0: michael@0: (void) SSL3_SendAlert(ss, alert_warning, close_notify); michael@0: } michael@0: rv = ssl_DefClose(ss); michael@0: return rv; michael@0: } michael@0: michael@0: /* Caller handles all locking */ michael@0: int michael@0: ssl_SecureShutdown(sslSocket *ss, int nsprHow) michael@0: { michael@0: PRFileDesc *osfd = ss->fd->lower; michael@0: int rv; michael@0: PRIntn sslHow = nsprHow + 1; michael@0: michael@0: if ((unsigned)nsprHow > PR_SHUTDOWN_BOTH) { michael@0: PORT_SetError(PR_INVALID_ARGUMENT_ERROR); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: if ((sslHow & ssl_SHUTDOWN_SEND) != 0 && michael@0: ss->version >= SSL_LIBRARY_VERSION_3_0 && michael@0: !(ss->shutdownHow & ssl_SHUTDOWN_SEND) && michael@0: ss->firstHsDone && michael@0: !ss->recvdCloseNotify && michael@0: ss->ssl3.initialized) { michael@0: michael@0: (void) SSL3_SendAlert(ss, alert_warning, close_notify); michael@0: } michael@0: michael@0: rv = osfd->methods->shutdown(osfd, nsprHow); michael@0: michael@0: ss->shutdownHow |= sslHow; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: michael@0: int michael@0: ssl_SecureRecv(sslSocket *ss, unsigned char *buf, int len, int flags) michael@0: { michael@0: sslSecurityInfo *sec; michael@0: int rv = 0; michael@0: michael@0: sec = &ss->sec; michael@0: michael@0: if (ss->shutdownHow & ssl_SHUTDOWN_RCV) { michael@0: PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR); michael@0: return PR_FAILURE; michael@0: } michael@0: if (flags & ~PR_MSG_PEEK) { michael@0: PORT_SetError(PR_INVALID_ARGUMENT_ERROR); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: if (!ssl_SocketIsBlocking(ss) && !ss->opt.fdx) { michael@0: ssl_GetXmitBufLock(ss); michael@0: if (ss->pendingBuf.len != 0) { michael@0: rv = ssl_SendSavedWriteData(ss); michael@0: if ((rv < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) { michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: } michael@0: michael@0: rv = 0; michael@0: /* If any of these is non-zero, the initial handshake is not done. */ michael@0: if (!ss->firstHsDone) { michael@0: ssl_Get1stHandshakeLock(ss); michael@0: if (ss->handshake || ss->nextHandshake || ss->securityHandshake) { michael@0: rv = ssl_Do1stHandshake(ss); michael@0: } michael@0: ssl_Release1stHandshakeLock(ss); michael@0: } michael@0: if (rv < 0) { michael@0: return rv; michael@0: } michael@0: michael@0: if (len == 0) return 0; michael@0: michael@0: rv = DoRecv(ss, (unsigned char*) buf, len, flags); michael@0: SSL_TRC(2, ("%d: SSL[%d]: recving %d bytes securely (errno=%d)", michael@0: SSL_GETPID(), ss->fd, rv, PORT_GetError())); michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: ssl_SecureRead(sslSocket *ss, unsigned char *buf, int len) michael@0: { michael@0: return ssl_SecureRecv(ss, buf, len, 0); michael@0: } michael@0: michael@0: /* Caller holds the SSL Socket's write lock. SSL_LOCK_WRITER(ss) */ michael@0: int michael@0: ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) michael@0: { michael@0: int rv = 0; michael@0: michael@0: SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes", michael@0: SSL_GETPID(), ss->fd, len)); michael@0: michael@0: if (ss->shutdownHow & ssl_SHUTDOWN_SEND) { michael@0: PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR); michael@0: rv = PR_FAILURE; michael@0: goto done; michael@0: } michael@0: if (flags) { michael@0: PORT_SetError(PR_INVALID_ARGUMENT_ERROR); michael@0: rv = PR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: ssl_GetXmitBufLock(ss); michael@0: if (ss->pendingBuf.len != 0) { michael@0: PORT_Assert(ss->pendingBuf.len > 0); michael@0: rv = ssl_SendSavedWriteData(ss); michael@0: if (rv >= 0 && ss->pendingBuf.len != 0) { michael@0: PORT_Assert(ss->pendingBuf.len > 0); michael@0: PORT_SetError(PR_WOULD_BLOCK_ERROR); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: if (rv < 0) { michael@0: goto done; michael@0: } michael@0: michael@0: if (len > 0) michael@0: ss->writerThread = PR_GetCurrentThread(); michael@0: /* If any of these is non-zero, the initial handshake is not done. */ michael@0: if (!ss->firstHsDone) { michael@0: PRBool falseStart = PR_FALSE; michael@0: ssl_Get1stHandshakeLock(ss); michael@0: if (ss->opt.enableFalseStart && michael@0: ss->version >= SSL_LIBRARY_VERSION_3_0) { michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: falseStart = ss->ssl3.hs.canFalseStart; michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: } michael@0: if (!falseStart && michael@0: (ss->handshake || ss->nextHandshake || ss->securityHandshake)) { michael@0: rv = ssl_Do1stHandshake(ss); michael@0: } michael@0: ssl_Release1stHandshakeLock(ss); michael@0: } michael@0: if (rv < 0) { michael@0: ss->writerThread = NULL; michael@0: goto done; michael@0: } michael@0: michael@0: /* Check for zero length writes after we do housekeeping so we make forward michael@0: * progress. michael@0: */ michael@0: if (len == 0) { michael@0: rv = 0; michael@0: goto done; michael@0: } michael@0: PORT_Assert(buf != NULL); michael@0: if (!buf) { michael@0: PORT_SetError(PR_INVALID_ARGUMENT_ERROR); michael@0: rv = PR_FAILURE; michael@0: goto done; michael@0: } michael@0: michael@0: if (!ss->firstHsDone) { michael@0: PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_3_0); michael@0: #ifdef DEBUG michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: PORT_Assert(ss->ssl3.hs.canFalseStart); michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: #endif michael@0: SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start", michael@0: SSL_GETPID(), ss->fd)); michael@0: } michael@0: michael@0: /* Send out the data using one of these functions: michael@0: * ssl2_SendClear, ssl2_SendStream, ssl2_SendBlock, michael@0: * ssl3_SendApplicationData michael@0: */ michael@0: ssl_GetXmitBufLock(ss); michael@0: rv = (*ss->sec.send)(ss, buf, len, flags); michael@0: ssl_ReleaseXmitBufLock(ss); michael@0: ss->writerThread = NULL; michael@0: done: michael@0: if (rv < 0) { michael@0: SSL_TRC(2, ("%d: SSL[%d]: SecureSend: returning %d count, error %d", michael@0: SSL_GETPID(), ss->fd, rv, PORT_GetError())); michael@0: } else { michael@0: SSL_TRC(2, ("%d: SSL[%d]: SecureSend: returning %d count", michael@0: SSL_GETPID(), ss->fd, rv)); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: ssl_SecureWrite(sslSocket *ss, const unsigned char *buf, int len) michael@0: { michael@0: return ssl_SecureSend(ss, buf, len, 0); michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, void *arg) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSLBadCertHook", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ss->handleBadCert = f; michael@0: ss->badCertArg = arg; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * Allow the application to pass the url or hostname into the SSL library michael@0: * so that we can do some checking on it. It will be used for the value in michael@0: * SNI extension of client hello message. michael@0: */ michael@0: SECStatus michael@0: SSL_SetURL(PRFileDesc *fd, const char *url) michael@0: { michael@0: sslSocket * ss = ssl_FindSocket(fd); michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSLSetURL", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: michael@0: if ( ss->url ) { michael@0: PORT_Free((void *)ss->url); /* CONST */ michael@0: } michael@0: michael@0: ss->url = (const char *)PORT_Strdup(url); michael@0: if ( ss->url == NULL ) { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * Allow the application to pass the set of trust anchors michael@0: */ michael@0: SECStatus michael@0: SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList) michael@0: { michael@0: sslSocket * ss = ssl_FindSocket(fd); michael@0: CERTDistNames *names = NULL; michael@0: michael@0: if (!certList) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetTrustAnchors", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: names = CERT_DistNamesFromCertList(certList); michael@0: if (names == NULL) { michael@0: return SECFailure; michael@0: } michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: if (ss->ssl3.ca_list) { michael@0: CERT_FreeDistNames(ss->ssl3.ca_list); michael@0: } michael@0: ss->ssl3.ca_list = names; michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: ** Returns Negative number on error, zero or greater on success. michael@0: ** Returns the amount of data immediately available to be read. michael@0: */ michael@0: int michael@0: SSL_DataPending(PRFileDesc *fd) michael@0: { michael@0: sslSocket *ss; michael@0: int rv = 0; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: michael@0: if (ss && ss->opt.useSecurity) { michael@0: ssl_GetRecvBufLock(ss); michael@0: rv = ss->gs.writeOffset - ss->gs.readOffset; michael@0: ssl_ReleaseRecvBufLock(ss); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_InvalidateSession(PRFileDesc *fd) michael@0: { michael@0: sslSocket * ss = ssl_FindSocket(fd); michael@0: SECStatus rv = SECFailure; michael@0: michael@0: if (ss) { michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: michael@0: if (ss->sec.ci.sid && ss->sec.uncache) { michael@0: ss->sec.uncache(ss->sec.ci.sid); michael@0: rv = SECSuccess; michael@0: } michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: SECItem * michael@0: SSL_GetSessionID(PRFileDesc *fd) michael@0: { michael@0: sslSocket * ss; michael@0: SECItem * item = NULL; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (ss) { michael@0: ssl_Get1stHandshakeLock(ss); michael@0: ssl_GetSSL3HandshakeLock(ss); michael@0: michael@0: if (ss->opt.useSecurity && ss->firstHsDone && ss->sec.ci.sid) { michael@0: item = (SECItem *)PORT_Alloc(sizeof(SECItem)); michael@0: if (item) { michael@0: sslSessionID * sid = ss->sec.ci.sid; michael@0: if (sid->version < SSL_LIBRARY_VERSION_3_0) { michael@0: item->len = SSL2_SESSIONID_BYTES; michael@0: item->data = (unsigned char*)PORT_Alloc(item->len); michael@0: PORT_Memcpy(item->data, sid->u.ssl2.sessionID, item->len); michael@0: } else { michael@0: item->len = sid->u.ssl3.sessionIDLength; michael@0: item->data = (unsigned char*)PORT_Alloc(item->len); michael@0: PORT_Memcpy(item->data, sid->u.ssl3.sessionID, item->len); michael@0: } michael@0: } michael@0: } michael@0: michael@0: ssl_ReleaseSSL3HandshakeLock(ss); michael@0: ssl_Release1stHandshakeLock(ss); michael@0: } michael@0: return item; michael@0: } michael@0: michael@0: SECStatus michael@0: SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle) michael@0: { michael@0: sslSocket * ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) michael@0: return SECFailure; michael@0: if (!dbHandle) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: ss->dbHandle = dbHandle; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* DO NOT USE. This function was exported in ssl.def with the wrong signature; michael@0: * this implementation exists to maintain link-time compatibility. michael@0: */ michael@0: int michael@0: SSL_RestartHandshakeAfterCertReq(sslSocket * ss, michael@0: CERTCertificate * cert, michael@0: SECKEYPrivateKey * key, michael@0: CERTCertificateList *certChain) michael@0: { michael@0: PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); michael@0: return -1; michael@0: } michael@0: michael@0: /* DO NOT USE. This function was exported in ssl.def with the wrong signature; michael@0: * this implementation exists to maintain link-time compatibility. michael@0: */ michael@0: int michael@0: SSL_RestartHandshakeAfterServerCert(sslSocket * ss) michael@0: { michael@0: PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); michael@0: return -1; michael@0: } michael@0: michael@0: /* See documentation in ssl.h */ michael@0: SECStatus michael@0: SSL_AuthCertificateComplete(PRFileDesc *fd, PRErrorCode error) michael@0: { michael@0: SECStatus rv; michael@0: sslSocket *ss = ssl_FindSocket(fd); michael@0: michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SSL_AuthCertificateComplete", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ssl_Get1stHandshakeLock(ss); michael@0: michael@0: if (!ss->ssl3.initialized) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: rv = SECFailure; michael@0: } else if (ss->version < SSL_LIBRARY_VERSION_3_0) { michael@0: PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2); michael@0: rv = SECFailure; michael@0: } else { michael@0: rv = ssl3_AuthCertificateComplete(ss, error); michael@0: } michael@0: michael@0: ssl_Release1stHandshakeLock(ss); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* For more info see ssl.h */ michael@0: SECStatus michael@0: SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func, michael@0: void *arg) michael@0: { michael@0: sslSocket *ss; michael@0: michael@0: ss = ssl_FindSocket(fd); michael@0: if (!ss) { michael@0: SSL_DBG(("%d: SSL[%d]: bad socket in SNISocketConfigHook", michael@0: SSL_GETPID(), fd)); michael@0: return SECFailure; michael@0: } michael@0: michael@0: ss->sniSocketConfig = func; michael@0: ss->sniSocketConfigArg = arg; michael@0: return SECSuccess; michael@0: }