mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-06 06:29:31 +00:00
1225 lines
38 KiB
C
1225 lines
38 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file implements the CLIENT Session ID cache.
|
|
*
|
|
* 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 "pk11pub.h"
|
|
#include "secitem.h"
|
|
#include "ssl.h"
|
|
#include "nss.h"
|
|
|
|
#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
|
|
|
|
static sslSessionID *cache = NULL;
|
|
static PZLock *cacheLock = NULL;
|
|
|
|
/* 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()
|
|
#define UNLOCK_CACHE PZ_Unlock(cacheLock)
|
|
|
|
static SECStatus
|
|
ssl_InitClientSessionCacheLock(void)
|
|
{
|
|
cacheLock = PZ_NewLock(nssILockCache);
|
|
return cacheLock ? SECSuccess : SECFailure;
|
|
}
|
|
|
|
static SECStatus
|
|
ssl_FreeClientSessionCacheLock(void)
|
|
{
|
|
if (cacheLock) {
|
|
PZ_DestroyLock(cacheLock);
|
|
cacheLock = NULL;
|
|
return SECSuccess;
|
|
}
|
|
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
|
|
return SECFailure;
|
|
}
|
|
|
|
static PRBool LocksInitializedEarly = PR_FALSE;
|
|
|
|
static SECStatus
|
|
FreeSessionCacheLocks()
|
|
{
|
|
SECStatus rv1, rv2;
|
|
rv1 = ssl_FreeSymWrapKeysLock();
|
|
rv2 = ssl_FreeClientSessionCacheLock();
|
|
if ((SECSuccess == rv1) && (SECSuccess == rv2)) {
|
|
return SECSuccess;
|
|
}
|
|
return SECFailure;
|
|
}
|
|
|
|
static SECStatus
|
|
InitSessionCacheLocks(void)
|
|
{
|
|
SECStatus rv1, rv2;
|
|
PRErrorCode rc;
|
|
rv1 = ssl_InitSymWrapKeysLock();
|
|
rv2 = ssl_InitClientSessionCacheLock();
|
|
if ((SECSuccess == rv1) && (SECSuccess == rv2)) {
|
|
return SECSuccess;
|
|
}
|
|
rc = PORT_GetError();
|
|
FreeSessionCacheLocks();
|
|
PORT_SetError(rc);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* free the session cache locks if they were initialized early */
|
|
SECStatus
|
|
ssl_FreeSessionCacheLocks()
|
|
{
|
|
PORT_Assert(PR_TRUE == LocksInitializedEarly);
|
|
if (!LocksInitializedEarly) {
|
|
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
|
|
return SECFailure;
|
|
}
|
|
FreeSessionCacheLocks();
|
|
LocksInitializedEarly = PR_FALSE;
|
|
return SECSuccess;
|
|
}
|
|
|
|
static PRCallOnceType lockOnce;
|
|
|
|
/* free the session cache locks if they were initialized lazily */
|
|
static SECStatus
|
|
ssl_ShutdownLocks(void *appData, void *nssData)
|
|
{
|
|
PORT_Assert(PR_FALSE == LocksInitializedEarly);
|
|
if (LocksInitializedEarly) {
|
|
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
|
return SECFailure;
|
|
}
|
|
FreeSessionCacheLocks();
|
|
memset(&lockOnce, 0, sizeof(lockOnce));
|
|
return SECSuccess;
|
|
}
|
|
|
|
static PRStatus
|
|
initSessionCacheLocksLazily(void)
|
|
{
|
|
SECStatus rv = InitSessionCacheLocks();
|
|
if (SECSuccess != rv) {
|
|
return PR_FAILURE;
|
|
}
|
|
rv = NSS_RegisterShutdown(ssl_ShutdownLocks, NULL);
|
|
PORT_Assert(SECSuccess == rv);
|
|
if (SECSuccess != rv) {
|
|
return PR_FAILURE;
|
|
}
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
/* lazyInit means that the call is not happening during a 1-time
|
|
* initialization function, but rather during dynamic, lazy initialization
|
|
*/
|
|
SECStatus
|
|
ssl_InitSessionCacheLocks(PRBool lazyInit)
|
|
{
|
|
if (LocksInitializedEarly) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
if (lazyInit) {
|
|
return (PR_SUCCESS ==
|
|
PR_CallOnce(&lockOnce, initSessionCacheLocksLazily))
|
|
? SECSuccess
|
|
: SECFailure;
|
|
}
|
|
|
|
if (SECSuccess == InitSessionCacheLocks()) {
|
|
LocksInitializedEarly = PR_TRUE;
|
|
return SECSuccess;
|
|
}
|
|
|
|
return SECFailure;
|
|
}
|
|
|
|
static void
|
|
lock_cache(void)
|
|
{
|
|
ssl_InitSessionCacheLocks(PR_TRUE);
|
|
PZ_Lock(cacheLock);
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
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);
|
|
PORT_Assert(sid->cached != in_client_cache);
|
|
|
|
if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
|
|
SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
|
|
PR_FALSE);
|
|
}
|
|
if (sid->u.ssl3.srvName.data) {
|
|
SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
|
|
}
|
|
if (sid->u.ssl3.signedCertTimestamps.data) {
|
|
SECITEM_FreeItem(&sid->u.ssl3.signedCertTimestamps, PR_FALSE);
|
|
}
|
|
|
|
if (sid->u.ssl3.lock) {
|
|
PR_DestroyRWLock(sid->u.ssl3.lock);
|
|
}
|
|
|
|
PORT_Free((void *)sid->peerID);
|
|
PORT_Free((void *)sid->urlSvrName);
|
|
|
|
if (sid->peerCert) {
|
|
CERT_DestroyCertificate(sid->peerCert);
|
|
}
|
|
if (sid->peerCertStatus.items) {
|
|
SECITEM_FreeArray(&sid->peerCertStatus, PR_FALSE);
|
|
}
|
|
|
|
if (sid->localCert) {
|
|
CERT_DestroyCertificate(sid->localCert);
|
|
}
|
|
|
|
SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE);
|
|
|
|
if (freeIt) {
|
|
PORT_ZFree(sid, sizeof(sslSessionID));
|
|
}
|
|
}
|
|
|
|
/* BEWARE: This function gets called for both client and server SIDs !!
|
|
* Decrement reference count, and
|
|
* free sid if ref count is zero, and sid is not in the cache.
|
|
* Does NOT remove from the cache first.
|
|
* If the sid is still in the cache, it is left there until next time
|
|
* the cache list is traversed.
|
|
*/
|
|
static void
|
|
ssl_FreeLockedSID(sslSessionID *sid)
|
|
{
|
|
PORT_Assert(sid->references >= 1);
|
|
if (--sid->references == 0) {
|
|
ssl_DestroySID(sid, PR_TRUE);
|
|
}
|
|
}
|
|
|
|
/* BEWARE: This function gets called for both client and server SIDs !!
|
|
* Decrement reference count, and
|
|
* free sid if ref count is zero, and sid is not in the cache.
|
|
* Does NOT remove from the cache first.
|
|
* These locks are necessary because the sid _might_ be in the cache list.
|
|
*/
|
|
void
|
|
ssl_FreeSID(sslSessionID *sid)
|
|
{
|
|
if (sid) {
|
|
LOCK_CACHE;
|
|
ssl_FreeLockedSID(sid);
|
|
UNLOCK_CACHE;
|
|
}
|
|
}
|
|
|
|
sslSessionID *
|
|
ssl_ReferenceSID(sslSessionID *sid)
|
|
{
|
|
LOCK_CACHE;
|
|
sid->references++;
|
|
UNLOCK_CACHE;
|
|
return sid;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
/*
|
|
** Lookup sid entry in cache by Address, port, and peerID string.
|
|
** If found, Increment reference count, and return pointer to caller.
|
|
** If it has timed out or ref count is zero, remove from list and free it.
|
|
*/
|
|
|
|
sslSessionID *
|
|
ssl_LookupSID(PRTime now, const PRIPv6Addr *addr, PRUint16 port, const char *peerID,
|
|
const char *urlSvrName)
|
|
{
|
|
sslSessionID **sidp;
|
|
sslSessionID *sid;
|
|
|
|
if (!urlSvrName)
|
|
return NULL;
|
|
LOCK_CACHE;
|
|
sidp = &cache;
|
|
while ((sid = *sidp) != 0) {
|
|
PORT_Assert(sid->cached == in_client_cache);
|
|
PORT_Assert(sid->references >= 1);
|
|
|
|
SSL_TRC(8, ("SSL: lookup: sid=0x%x", sid));
|
|
|
|
if (sid->expirationTime < now) {
|
|
/*
|
|
** This session-id timed out.
|
|
** Don't even care who it belongs to, blow it out of our cache.
|
|
*/
|
|
SSL_TRC(7, ("SSL: lookup, throwing sid out, age=%d refs=%d",
|
|
now - sid->creationTime, sid->references));
|
|
|
|
*sidp = sid->next; /* delink it from the list. */
|
|
sid->cached = invalid_cache; /* mark not on list. */
|
|
ssl_FreeLockedSID(sid); /* drop ref count, free. */
|
|
} else if (!memcmp(&sid->addr, addr, sizeof(PRIPv6Addr)) && /* server IP addr matches */
|
|
(sid->port == port) && /* server port matches */
|
|
/* proxy (peerID) matches */
|
|
(((peerID == NULL) && (sid->peerID == NULL)) ||
|
|
((peerID != NULL) && (sid->peerID != NULL) &&
|
|
PORT_Strcmp(sid->peerID, peerID) == 0)) &&
|
|
/* is cacheable */
|
|
(sid->u.ssl3.keys.resumable) &&
|
|
/* server hostname matches. */
|
|
(sid->urlSvrName != NULL) &&
|
|
(0 == PORT_Strcmp(urlSvrName, sid->urlSvrName))) {
|
|
/* Hit */
|
|
sid->lastAccessTime = now;
|
|
sid->references++;
|
|
break;
|
|
} else {
|
|
sidp = &sid->next;
|
|
}
|
|
}
|
|
UNLOCK_CACHE;
|
|
return sid;
|
|
}
|
|
|
|
/*
|
|
** Add an sid to the cache or return a previously cached entry to the cache.
|
|
** Although this is static, it is called via ss->sec.cache().
|
|
*/
|
|
static void
|
|
CacheSID(sslSessionID *sid, PRTime creationTime)
|
|
{
|
|
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 "
|
|
"time=%x cached=%d",
|
|
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));
|
|
|
|
if (!sid->urlSvrName) {
|
|
/* don't cache this SID because it can never be matched */
|
|
return;
|
|
}
|
|
|
|
if (sid->u.ssl3.sessionIDLength == 0 &&
|
|
sid->u.ssl3.locked.sessionTicket.ticket.data == NULL)
|
|
return;
|
|
|
|
/* Client generates the SessionID if this was a stateless resume. */
|
|
if (sid->u.ssl3.sessionIDLength == 0) {
|
|
SECStatus rv;
|
|
rv = PK11_GenerateRandom(sid->u.ssl3.sessionID,
|
|
SSL3_SESSIONID_BYTES);
|
|
if (rv != SECSuccess)
|
|
return;
|
|
sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
|
|
}
|
|
PRINT_BUF(8, (0, "sessionID:",
|
|
sid->u.ssl3.sessionID, sid->u.ssl3.sessionIDLength));
|
|
|
|
sid->u.ssl3.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, NULL);
|
|
if (!sid->u.ssl3.lock) {
|
|
return;
|
|
}
|
|
PORT_Assert(sid->creationTime != 0);
|
|
if (!sid->creationTime) {
|
|
sid->lastAccessTime = sid->creationTime = creationTime;
|
|
}
|
|
PORT_Assert(sid->expirationTime != 0);
|
|
if (!sid->expirationTime) {
|
|
sid->expirationTime = sid->creationTime + (PR_MIN(ssl_ticket_lifetime,
|
|
sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint) *
|
|
PR_USEC_PER_SEC);
|
|
}
|
|
|
|
/*
|
|
* Put sid into the cache. Bump reference count to indicate that
|
|
* cache is holding a reference. Uncache will reduce the cache
|
|
* reference.
|
|
*/
|
|
LOCK_CACHE;
|
|
sid->references++;
|
|
sid->cached = in_client_cache;
|
|
sid->next = cache;
|
|
cache = sid;
|
|
UNLOCK_CACHE;
|
|
}
|
|
|
|
/*
|
|
* If sid "zap" is in the cache,
|
|
* removes sid from cache, and decrements reference count.
|
|
* Caller must hold cache lock.
|
|
*/
|
|
static void
|
|
UncacheSID(sslSessionID *zap)
|
|
{
|
|
sslSessionID **sidp = &cache;
|
|
sslSessionID *sid;
|
|
|
|
if (zap->cached != in_client_cache) {
|
|
return;
|
|
}
|
|
|
|
SSL_TRC(8, ("SSL: Uncache: zap=0x%x cached=%d addr=0x%08x%08x%08x%08x port=0x%04x "
|
|
"time=%x cipherSuite=%d",
|
|
zap, zap->cached, zap->addr.pr_s6_addr32[0],
|
|
zap->addr.pr_s6_addr32[1], zap->addr.pr_s6_addr32[2],
|
|
zap->addr.pr_s6_addr32[3], zap->port, zap->creationTime,
|
|
zap->u.ssl3.cipherSuite));
|
|
|
|
/* See if it's in the cache, if so nuke it */
|
|
while ((sid = *sidp) != 0) {
|
|
if (sid == zap) {
|
|
/*
|
|
** Bingo. Reduce reference count by one so that when
|
|
** everyone is done with the sid we can free it up.
|
|
*/
|
|
*sidp = zap->next;
|
|
zap->cached = invalid_cache;
|
|
ssl_FreeLockedSID(zap);
|
|
return;
|
|
}
|
|
sidp = &sid->next;
|
|
}
|
|
}
|
|
|
|
/* 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
|
|
* ssl_UncacheSessionID.
|
|
*/
|
|
static void
|
|
LockAndUncacheSID(sslSessionID *zap)
|
|
{
|
|
LOCK_CACHE;
|
|
UncacheSID(zap);
|
|
UNLOCK_CACHE;
|
|
}
|
|
|
|
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_IsResumptionTokenUsable(sslSocket *ss, sslSessionID *sid)
|
|
{
|
|
PORT_Assert(ss);
|
|
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_Time(ss)) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
// Check that the session entry didn't expire.
|
|
if (sid->expirationTime < ssl_Time(ss)) {
|
|
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_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_Time(ss);
|
|
}
|
|
if (!sid->expirationTime) {
|
|
sid->expirationTime = sid->creationTime + (PR_MIN(ssl_ticket_lifetime,
|
|
sid->u.ssl3.locked.sessionTicket.ticket_lifetime_hint) *
|
|
PR_USEC_PER_SEC);
|
|
}
|
|
|
|
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)));
|
|
SECStatus rv = ss->resumptionTokenCallback(
|
|
ss->fd, SSL_BUFFER_BASE(&encodedToken), SSL_BUFFER_LEN(&encodedToken),
|
|
ss->resumptionTokenContext);
|
|
if (rv == SECSuccess) {
|
|
sid->cached = in_external_cache;
|
|
}
|
|
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) {
|
|
ssl_ServerCacheSessionID(sec->ci.sid, ssl_Time(ss));
|
|
return;
|
|
}
|
|
|
|
CacheSID(sec->ci.sid, ssl_Time(ss));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* wipe out the entire client session cache. */
|
|
void
|
|
SSL_ClearSessionCache(void)
|
|
{
|
|
LOCK_CACHE;
|
|
while (cache != NULL)
|
|
UncacheSID(cache);
|
|
UNLOCK_CACHE;
|
|
}
|
|
|
|
PRBool
|
|
ssl_TicketTimeValid(const sslSocket *ss, const NewSessionTicket *ticket)
|
|
{
|
|
PRTime endTime;
|
|
|
|
if (ticket->ticket_lifetime_hint == 0) {
|
|
return PR_TRUE;
|
|
}
|
|
|
|
endTime = ticket->received_timestamp +
|
|
(PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_SEC);
|
|
return endTime > ssl_Time(ss);
|
|
}
|
|
|
|
void
|
|
ssl3_SetSIDSessionTicket(sslSessionID *sid,
|
|
/*in/out*/ NewSessionTicket *newSessionTicket)
|
|
{
|
|
PORT_Assert(sid);
|
|
PORT_Assert(newSessionTicket);
|
|
PORT_Assert(newSessionTicket->ticket.data);
|
|
PORT_Assert(newSessionTicket->ticket.len != 0);
|
|
|
|
/* If this is in the client cache, we are updating an existing entry that is
|
|
* already cached or was once cached, so we need to acquire and release the
|
|
* write lock. Otherwise, this is a new session that isn't shared with
|
|
* anything yet, so no locking is needed.
|
|
*/
|
|
if (sid->u.ssl3.lock) {
|
|
PR_RWLock_Wlock(sid->u.ssl3.lock);
|
|
/* Another thread may have evicted, or it may be in external cache. */
|
|
PORT_Assert(sid->cached != never_cached);
|
|
}
|
|
/* If this was in the client cache, then we might have to free the old
|
|
* ticket. In TLS 1.3, we might get a replacement ticket if the server
|
|
* sends more than one ticket. */
|
|
if (sid->u.ssl3.locked.sessionTicket.ticket.data) {
|
|
PORT_Assert(sid->cached != never_cached ||
|
|
sid->version >= SSL_LIBRARY_VERSION_TLS_1_3);
|
|
SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket,
|
|
PR_FALSE);
|
|
}
|
|
|
|
PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data);
|
|
|
|
/* Do a shallow copy, moving the ticket data. */
|
|
sid->u.ssl3.locked.sessionTicket = *newSessionTicket;
|
|
newSessionTicket->ticket.data = NULL;
|
|
newSessionTicket->ticket.len = 0;
|
|
|
|
if (sid->u.ssl3.lock) {
|
|
PR_RWLock_Unlock(sid->u.ssl3.lock);
|
|
}
|
|
}
|