summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/sslnonce.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/sslnonce.c')
-rw-r--r--security/nss/lib/ssl/sslnonce.c756
1 files changed, 738 insertions, 18 deletions
diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c
index 228834e3d..f79c23fc7 100644
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -15,6 +15,7 @@
#include "sslimpl.h"
#include "sslproto.h"
#include "nssilock.h"
+#include "sslencode.h"
#if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
#include <time.h>
#endif
@@ -24,12 +25,13 @@ PRUint32 ssl3_sid_timeout = 86400L; /* 24 hours */
static sslSessionID *cache = NULL;
static PZLock *cacheLock = NULL;
-/* sids can be in one of 4 states:
+/* sids can be in one of 5 states:
*
* never_cached, created, but not yet put into cache.
* in_client_cache, in the client cache's linked list.
* in_server_cache, entry came from the server's cache file.
* invalid_cache has been removed from the cache.
+ * in_external_cache sid comes from an external cache.
*/
#define LOCK_CACHE lock_cache()
@@ -164,8 +166,8 @@ lock_cache(void)
/* BEWARE: This function gets called for both client and server SIDs !!
* If the unreferenced sid is not in the cache, Free sid and its contents.
*/
-static void
-ssl_DestroySID(sslSessionID *sid)
+void
+ssl_DestroySID(sslSessionID *sid, PRBool freeIt)
{
SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
PORT_Assert(sid->references == 0);
@@ -186,11 +188,8 @@ ssl_DestroySID(sslSessionID *sid)
PR_DestroyRWLock(sid->u.ssl3.lock);
}
- if (sid->peerID != NULL)
- PORT_Free((void *)sid->peerID); /* CONST */
-
- if (sid->urlSvrName != NULL)
- PORT_Free((void *)sid->urlSvrName); /* CONST */
+ PORT_Free((void *)sid->peerID);
+ PORT_Free((void *)sid->urlSvrName);
if (sid->peerCert) {
CERT_DestroyCertificate(sid->peerCert);
@@ -205,7 +204,9 @@ ssl_DestroySID(sslSessionID *sid)
SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE);
- PORT_ZFree(sid, sizeof(sslSessionID));
+ if (freeIt) {
+ PORT_ZFree(sid, sizeof(sslSessionID));
+ }
}
/* BEWARE: This function gets called for both client and server SIDs !!
@@ -220,7 +221,7 @@ ssl_FreeLockedSID(sslSessionID *sid)
{
PORT_Assert(sid->references >= 1);
if (--sid->references == 0) {
- ssl_DestroySID(sid);
+ ssl_DestroySID(sid, PR_TRUE);
}
}
@@ -306,6 +307,7 @@ ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
static void
CacheSID(sslSessionID *sid)
{
+ PORT_Assert(sid);
PORT_Assert(sid->cached == never_cached);
SSL_TRC(8, ("SSL: Cache: sid=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
@@ -400,7 +402,7 @@ UncacheSID(sslSessionID *zap)
/* If sid "zap" is in the cache,
* removes sid from cache, and decrements reference count.
* Although this function is static, it is called externally via
- * ss->sec.uncache().
+ * ssl_UncacheSessionID.
*/
static void
LockAndUncacheSID(sslSessionID *zap)
@@ -410,16 +412,734 @@ LockAndUncacheSID(sslSessionID *zap)
UNLOCK_CACHE;
}
-/* choose client or server cache functions for this sslsocket. */
+SECStatus
+ReadVariableFromBuffer(sslReader *reader, sslReadBuffer *readerBuffer,
+ uint8_t lenBytes, SECItem *dest)
+{
+ if (sslRead_ReadVariable(reader, lenBytes, readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer->len) {
+ SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer->buf,
+ readerBuffer->len };
+ SECStatus rv = SECITEM_CopyItem(NULL, dest, &tempItem);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ return SECSuccess;
+}
+
+/* Fill sid with the values from the encoded resumption token.
+ * sid has to be allocated.
+ * We don't care about locks here as this cache entry is externally stored.
+ */
+SECStatus
+ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedToken,
+ PRUint32 encodedTokenLen)
+{
+ PORT_Assert(encodedTokenLen);
+ PORT_Assert(encodedToken);
+ PORT_Assert(sid);
+ if (!sid || !encodedToken || !encodedTokenLen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (encodedToken[0] != SSLResumptionTokenVersion) {
+ /* Unknown token format version. */
+ PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR);
+ return SECFailure;
+ }
+
+ /* These variables are used across macros. Don't use them outside. */
+ sslReader reader = SSL_READER(encodedToken, encodedTokenLen);
+ reader.offset += 1; // We read the version already. Skip the first byte.
+ sslReadBuffer readerBuffer = { 0 };
+ PRUint64 tmpInt = 0;
+
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->lastAccessTime = (PRTime)tmpInt;
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->expirationTime = (PRTime)tmpInt;
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.locked.sessionTicket.received_timestamp = (PRTime)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.locked.sessionTicket.flags = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.locked.sessionTicket.ticket_age_add = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.locked.sessionTicket.max_early_data_size = (PRUint32)tmpInt;
+
+ if (sslRead_ReadVariable(&reader, 3, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ PORT_Assert(!sid->peerCert);
+ SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf,
+ readerBuffer.len };
+ sid->peerCert = CERT_NewTempCertificate(NULL, /* dbHandle */
+ &tempItem,
+ NULL, PR_FALSE, PR_TRUE);
+ if (!sid->peerCert) {
+ return SECFailure;
+ }
+ }
+
+ if (sslRead_ReadVariable(&reader, 2, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ SECITEM_AllocArray(NULL, &sid->peerCertStatus, 1);
+ if (!sid->peerCertStatus.items) {
+ return SECFailure;
+ }
+ SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf,
+ readerBuffer.len };
+ SECITEM_CopyItem(NULL, &sid->peerCertStatus.items[0], &tempItem);
+ }
+
+ if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ PORT_Assert(readerBuffer.buf);
+ sid->peerID = PORT_Strdup((const char *)readerBuffer.buf);
+ }
+
+ if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ if (sid->urlSvrName) {
+ PORT_Free((void *)sid->urlSvrName);
+ }
+ PORT_Assert(readerBuffer.buf);
+ sid->urlSvrName = PORT_Strdup((const char *)readerBuffer.buf);
+ }
+
+ if (sslRead_ReadVariable(&reader, 3, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ PORT_Assert(!sid->localCert);
+ SECItem tempItem = { siBuffer, (unsigned char *)readerBuffer.buf,
+ readerBuffer.len };
+ sid->localCert = CERT_NewTempCertificate(NULL, /* dbHandle */
+ &tempItem,
+ NULL, PR_FALSE, PR_TRUE);
+ }
+
+ if (sslRead_ReadNumber(&reader, 8, &sid->addr.pr_s6_addr64[0]) != SECSuccess) {
+ return SECFailure;
+ }
+ if (sslRead_ReadNumber(&reader, 8, &sid->addr.pr_s6_addr64[1]) != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->port = (PRUint16)tmpInt;
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->version = (PRUint16)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->creationTime = (PRTime)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->authType = (SSLAuthType)tmpInt;
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->authKeyBits = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->keaType = (SSLKEAType)tmpInt;
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->keaKeyBits = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 3, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->keaGroup = (SSLNamedGroup)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 3, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->sigScheme = (SSLSignatureScheme)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.sessionIDLength = (PRUint8)tmpInt;
+
+ if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (readerBuffer.len) {
+ PORT_Assert(readerBuffer.buf);
+ PORT_Memcpy(sid->u.ssl3.sessionID, readerBuffer.buf, readerBuffer.len);
+ }
+
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.cipherSuite = (PRUint16)tmpInt;
+ if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.policy = (PRUint8)tmpInt;
+
+ if (sslRead_ReadVariable(&reader, 1, &readerBuffer) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ PORT_Assert(readerBuffer.len == WRAPPED_MASTER_SECRET_SIZE);
+ if (readerBuffer.len != WRAPPED_MASTER_SECRET_SIZE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ PORT_Assert(readerBuffer.buf);
+ PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret, readerBuffer.buf,
+ readerBuffer.len);
+
+ if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.keys.wrapped_master_secret_len = (PRUint8)tmpInt;
+ if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.keys.extendedMasterSecretUsed = (PRUint8)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterWrapMech = (unsigned long)tmpInt;
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterModuleID = (unsigned long)tmpInt;
+ if (sslRead_ReadNumber(&reader, 8, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterSlotID = (unsigned long)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 4, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterWrapIndex = (PRUint32)tmpInt;
+ if (sslRead_ReadNumber(&reader, 2, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterWrapSeries = (PRUint16)tmpInt;
+
+ if (sslRead_ReadNumber(&reader, 1, &tmpInt) != SECSuccess) {
+ return SECFailure;
+ }
+ sid->u.ssl3.masterValid = (char)tmpInt;
+
+ if (ReadVariableFromBuffer(&reader, &readerBuffer, 1,
+ &sid->u.ssl3.srvName) != SECSuccess) {
+ return SECFailure;
+ }
+ if (ReadVariableFromBuffer(&reader, &readerBuffer, 2,
+ &sid->u.ssl3.signedCertTimestamps) != SECSuccess) {
+ return SECFailure;
+ }
+ if (ReadVariableFromBuffer(&reader, &readerBuffer, 1,
+ &sid->u.ssl3.alpnSelection) != SECSuccess) {
+ return SECFailure;
+ }
+ if (ReadVariableFromBuffer(&reader, &readerBuffer, 2,
+ &sid->u.ssl3.locked.sessionTicket.ticket) != SECSuccess) {
+ return SECFailure;
+ }
+ if (!sid->u.ssl3.locked.sessionTicket.ticket.len) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* At this point we must have read everything. */
+ PORT_Assert(reader.offset == reader.buf.len);
+ if (reader.offset != reader.buf.len) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+PRBool
+ssl_IsResumptionTokenValid(sslSocket *ss)
+{
+ PORT_Assert(ss);
+ sslSessionID *sid = ss->sec.ci.sid;
+ PORT_Assert(sid);
+
+ // Check that the ticket didn't expire.
+ PRTime endTime = 0;
+ NewSessionTicket *ticket = &sid->u.ssl3.locked.sessionTicket;
+ if (ticket->ticket_lifetime_hint != 0) {
+ endTime = ticket->received_timestamp +
+ (PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_SEC);
+ if (endTime < ssl_TimeUsec()) {
+ return PR_FALSE;
+ }
+ }
+
+ // Check that the session entry didn't expire.
+ if (sid->expirationTime < ssl_TimeUsec()) {
+ return PR_FALSE;
+ }
+
+ // Check that the server name (SNI) matches the one set for this session.
+ // Don't use the token if there's no server name.
+ if (sid->urlSvrName == NULL || PORT_Strcmp(ss->url, sid->urlSvrName) != 0) {
+ return PR_FALSE;
+ }
+
+ // This shouldn't be false, but let's check it anyway.
+ if (!sid->u.ssl3.keys.resumable) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* Encode a session ticket into a byte array that can be handed out to a cache.
+ * Needed memory in encodedToken has to be allocated according to
+ * *encodedTokenLen. */
+static SECStatus
+ssl_EncodeResumptionToken(sslSessionID *sid, sslBuffer *encodedTokenBuf)
+{
+ PORT_Assert(encodedTokenBuf);
+ PORT_Assert(sid);
+ if (!sid || !sid->u.ssl3.locked.sessionTicket.ticket.len ||
+ !encodedTokenBuf || !sid->u.ssl3.keys.resumable || !sid->urlSvrName) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* Encoding format:
+ * 0-byte: version
+ * Integers are encoded according to their length.
+ * SECItems are prepended with a 64-bit length field followed by the bytes.
+ * Optional bytes are encoded as a 0-length item if not present.
+ */
+ SECStatus rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ SSLResumptionTokenVersion, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->lastAccessTime, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->expirationTime, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ // session ticket
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.received_timestamp,
+ 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint,
+ 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.flags,
+ 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.ticket_age_add,
+ 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.max_early_data_size,
+ 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->peerCert->derCert.data,
+ sid->peerCert->derCert.len, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (sid->peerCertStatus.len > 1) {
+ /* This is not implemented so it shouldn't happen.
+ * If it gets implemented, this has to change.
+ */
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (sid->peerCertStatus.len == 1 && sid->peerCertStatus.items[0].len) {
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->peerCertStatus.items[0].data,
+ sid->peerCertStatus.items[0].len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ } else {
+ rv = sslBuffer_AppendVariable(encodedTokenBuf, NULL, 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ PRUint64 len = sid->peerID ? strlen(sid->peerID) : 0;
+ if (len > PR_UINT8_MAX) {
+ // This string really shouldn't be that long.
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ (const unsigned char *)sid->peerID, len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ len = sid->urlSvrName ? strlen(sid->urlSvrName) : 0;
+ if (!len) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (len > PR_UINT8_MAX) {
+ // This string really shouldn't be that long.
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ (const unsigned char *)sid->urlSvrName,
+ len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (sid->localCert) {
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->localCert->derCert.data,
+ sid->localCert->derCert.len, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ } else {
+ rv = sslBuffer_AppendVariable(encodedTokenBuf, NULL, 0, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->addr.pr_s6_addr64[0], 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->addr.pr_s6_addr64[1], 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->port, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->version, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->creationTime, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->authType, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->authKeyBits, 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaType, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaKeyBits, 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->keaGroup, 3);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->sigScheme, 3);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.sessionIDLength, 1);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->u.ssl3.sessionID,
+ SSL3_SESSIONID_BYTES, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.cipherSuite, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.policy, 1);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->u.ssl3.keys.wrapped_master_secret,
+ WRAPPED_MASTER_SECRET_SIZE, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.keys.wrapped_master_secret_len,
+ 1);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf,
+ sid->u.ssl3.keys.extendedMasterSecretUsed,
+ 1);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapMech, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterModuleID, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterSlotID, 8);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapIndex, 4);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterWrapSeries, 2);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendNumber(encodedTokenBuf, sid->u.ssl3.masterValid, 1);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendVariable(encodedTokenBuf, sid->u.ssl3.srvName.data,
+ sid->u.ssl3.srvName.len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->u.ssl3.signedCertTimestamps.data,
+ sid->u.ssl3.signedCertTimestamps.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->u.ssl3.alpnSelection.data,
+ sid->u.ssl3.alpnSelection.len, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ PORT_Assert(sid->u.ssl3.locked.sessionTicket.ticket.len > 1);
+ rv = sslBuffer_AppendVariable(encodedTokenBuf,
+ sid->u.ssl3.locked.sessionTicket.ticket.data,
+ sid->u.ssl3.locked.sessionTicket.ticket.len,
+ 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
void
-ssl_ChooseSessionIDProcs(sslSecurityInfo *sec)
+ssl_CacheExternalToken(sslSocket *ss)
{
+ PORT_Assert(ss);
+ sslSessionID *sid = ss->sec.ci.sid;
+ PORT_Assert(sid);
+ PORT_Assert(sid->cached == never_cached);
+ PORT_Assert(ss->resumptionTokenCallback);
+
+ SSL_TRC(8, ("SSL [%d]: Cache External: sid=0x%x cached=%d "
+ "addr=0x%08x%08x%08x%08x port=0x%04x time=%x cached=%d",
+ ss->fd,
+ sid, sid->cached, sid->addr.pr_s6_addr32[0],
+ sid->addr.pr_s6_addr32[1], sid->addr.pr_s6_addr32[2],
+ sid->addr.pr_s6_addr32[3], sid->port, sid->creationTime,
+ sid->cached));
+
+ /* This is only available for stateless resumption. */
+ if (sid->u.ssl3.locked.sessionTicket.ticket.data == NULL) {
+ return;
+ }
+
+ /* Don't export token if the session used client authentication. */
+ if (sid->u.ssl3.clAuthValid) {
+ return;
+ }
+
+ if (!sid->creationTime) {
+ sid->lastAccessTime = sid->creationTime = ssl_TimeUsec();
+ }
+ if (!sid->expirationTime) {
+ sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
+ }
+
+ sslBuffer encodedToken = SSL_BUFFER_EMPTY;
+
+ if (ssl_EncodeResumptionToken(sid, &encodedToken) != SECSuccess) {
+ SSL_TRC(3, ("SSL [%d]: encoding resumption token failed", ss->fd));
+ return;
+ }
+ PORT_Assert(SSL_BUFFER_LEN(&encodedToken) > 0);
+ PRINT_BUF(40, (ss, "SSL: encoded resumption token",
+ SSL_BUFFER_BASE(&encodedToken),
+ SSL_BUFFER_LEN(&encodedToken)));
+ ss->resumptionTokenCallback(ss->fd, SSL_BUFFER_BASE(&encodedToken),
+ SSL_BUFFER_LEN(&encodedToken),
+ ss->resumptionTokenContext);
+
+ sslBuffer_Clear(&encodedToken);
+}
+
+void
+ssl_CacheSessionID(sslSocket *ss)
+{
+ sslSecurityInfo *sec = &ss->sec;
+ PORT_Assert(sec);
+
+ if (sec->ci.sid && !sec->ci.sid->u.ssl3.keys.resumable) {
+ return;
+ }
+
+ if (!ss->sec.isServer && ss->resumptionTokenCallback) {
+ ssl_CacheExternalToken(ss);
+ return;
+ }
+
+ PORT_Assert(!ss->resumptionTokenCallback);
if (sec->isServer) {
- sec->cache = ssl_sid_cache;
- sec->uncache = ssl_sid_uncache;
- } else {
- sec->cache = CacheSID;
- sec->uncache = LockAndUncacheSID;
+ ssl_ServerCacheSessionID(sec->ci.sid);
+ return;
+ }
+
+ CacheSID(sec->ci.sid);
+}
+
+void
+ssl_UncacheSessionID(sslSocket *ss)
+{
+ if (ss->opt.noCache) {
+ return;
+ }
+
+ sslSecurityInfo *sec = &ss->sec;
+ PORT_Assert(sec);
+
+ if (sec->ci.sid) {
+ if (sec->isServer) {
+ ssl_ServerUncacheSessionID(sec->ci.sid);
+ } else if (!ss->resumptionTokenCallback) {
+ LockAndUncacheSID(sec->ci.sid);
+ }
}
}