#include "ssl.h"
#include "sslMemory.h"
#include "sslDebug.h"
#include "appleSession.h"
#include <CoreFoundation/CFDate.h>
#include <pthread.h>
#include <string.h>
#define QUICK_CACHE_TEST 0
#if QUICK_CACHE_TEST
#define SESSION_CACHE_TTL ((CFTimeInterval)5)
#else
#define SESSION_CACHE_TTL ((CFTimeInterval)(10 * 60))
#endif
#define CACHE_PRINT 0
#if CACHE_PRINT
#define DUMP_ALL_CACHE 0
static void cachePrint(
const void *entry,
const SSLBuffer *key,
const SSLBuffer *data)
{
printf("entry: %p ", entry);
unsigned char *kd = key->data;
if(data != NULL) {
unsigned char *dd = data->data;
printf(" key: %02X%02X%02X%02X%02X%02X%02X%02X"
" data: %02X%02X%02X%02X... (len %d)\n",
kd[0],kd[1],kd[2],kd[3], kd[4],kd[5],kd[6],kd[7],
dd[0],dd[1],dd[2],dd[3], (unsigned)data->length);
}
else {
printf(" key: %02X%02X%02X%02X%02X%02X%02X%02X\n",
kd[0],kd[1],kd[2],kd[3], kd[4],kd[5],kd[6],kd[7]);
}
}
#else
#define cachePrint(e, k, d)
#define DUMP_ALL_CACHE 0
#endif
#if DUMP_ALL_CACHE
static void dumpAllCache(void);
#else
#define dumpAllCache()
#endif
typedef struct SessionCacheEntry SessionCacheEntry;
struct SessionCacheEntry {
SessionCacheEntry *next;
SSLBuffer mKey;
SSLBuffer mSessionData;
CFAbsoluteTime mExpiration;
};
static SessionCacheEntry *SessionCacheEntryCreate(
const SSLBuffer *key,
const SSLBuffer *sessionData,
CFAbsoluteTime expirationTime)
{
OSStatus serr;
SessionCacheEntry *entry = sslMalloc(sizeof(SessionCacheEntry));
if (entry == NULL)
return NULL;
serr = SSLCopyBuffer(key, &entry->mKey);
if(serr) {
sslFree (entry);
return NULL;
}
serr = SSLCopyBuffer(sessionData, &entry->mSessionData);
if(serr) {
SSLFreeBuffer(&entry->mKey, NULL);
sslFree (entry);
return NULL;
}
sslLogSessCacheDebug("SessionCacheEntryCreate(buf,buf) %p", entry);
entry->mExpiration = expirationTime;
return entry;
}
static void SessionCacheEntryDelete(SessionCacheEntry *entry)
{
sslLogSessCacheDebug("~SessionCacheEntryDelete() %p", entry);
SSLFreeBuffer(&entry->mKey, NULL); SSLFreeBuffer(&entry->mSessionData, NULL);
sslFree(entry);
}
static bool SessionCacheEntryMatchKey(SessionCacheEntry *entry,
const SSLBuffer *key)
{
if(key->length != entry->mKey.length) {
return false;
}
if((key->data == NULL) || (entry->mKey.data == NULL)) {
return false;
}
return (memcmp(key->data, entry->mKey.data, entry->mKey.length) == 0);
}
static bool SessionCacheEntryIsStale(SessionCacheEntry *entry,
CFAbsoluteTime now)
{
return now > entry->mExpiration;
}
static bool SessionCacheEntryIsStaleNow(SessionCacheEntry *entry)
{
return SessionCacheEntryIsStale(entry, CFAbsoluteTimeGetCurrent());
}
static OSStatus SessionCacheEntrySetSessionData(SessionCacheEntry *entry,
const SSLBuffer *data)
{
SSLFreeBuffer(&entry->mSessionData, NULL);
return SSLCopyBuffer(data, &entry->mSessionData);
}
typedef struct SessionCache {
SessionCacheEntry *head;
CFTimeInterval mTimeToLive;
} SessionCache;
static pthread_mutex_t gSessionCacheLock = PTHREAD_MUTEX_INITIALIZER;
static SessionCache *gSessionCache = NULL;
static void SessionCacheInit(void) {
gSessionCache = sslMalloc(sizeof(SessionCache));
gSessionCache->head = NULL;
gSessionCache->mTimeToLive = SESSION_CACHE_TTL;
}
static SessionCache *SessionCacheGetLockedInstance(void) {
pthread_mutex_lock(&gSessionCacheLock);
if (!gSessionCache) {
SessionCacheInit();
}
return gSessionCache;
}
static OSStatus SessionCacheAddEntry(
SessionCache *cache,
const SSLBuffer *sessionKey,
const SSLBuffer *sessionData,
uint32_t timeToLive)
{
SessionCacheEntry *entry = NULL;
SessionCacheEntry **current;
CFTimeInterval expireTime;
for (current = &(cache->head); *current; current = &((*current)->next)) {
entry = *current;
if (SessionCacheEntryMatchKey(entry, sessionKey)) {
if((entry->mSessionData.length == sessionData->length) &&
(memcmp(entry->mSessionData.data, sessionData->data,
sessionData->length) == 0)) {
sslLogSessCacheDebug("SessionCache::addEntry CACHE HIT "
"entry = %p", entry);
return noErr;
}
else {
sslLogSessCacheDebug("SessionCache::addEntry CACHE REPLACE "
"entry = %p", entry);
return SessionCacheEntrySetSessionData(entry, sessionData);
}
}
}
expireTime = CFAbsoluteTimeGetCurrent();
if(timeToLive) {
expireTime += (CFTimeInterval)timeToLive;
}
else {
expireTime += cache->mTimeToLive;
}
entry = SessionCacheEntryCreate(sessionKey, sessionData, expireTime);
sslLogSessCacheDebug("SessionCache::addEntry %p", entry);
cachePrint(entry, sessionKey, sessionData);
dumpAllCache();
entry->next = cache->head;
cache->head = entry;
return noErr;
}
static OSStatus SessionCacheLookupEntry(
SessionCache *cache,
const SSLBuffer *sessionKey,
SSLBuffer *sessionData)
{
SessionCacheEntry *entry = NULL;
SessionCacheEntry **current;
for (current = &(cache->head); *current; current = &((*current)->next)) {
entry = *current;
if (SessionCacheEntryMatchKey(entry, sessionKey))
break;
}
if (*current == NULL)
return errSSLSessionNotFound;
if (SessionCacheEntryIsStaleNow(entry)) {
sslLogSessCacheDebug("SessionCache::lookupEntry %p: STALE "
"entry, deleting; current %p, entry->next %p",
entry, current, entry->next);
cachePrint(entry, sessionKey, &entry->mSessionData);
*current = entry->next;
SessionCacheEntryDelete(entry);
return errSSLSessionNotFound;
}
return SSLCopyBuffer(&entry->mSessionData, sessionData);
}
static OSStatus SessionCacheDeleteEntry(
SessionCache *cache,
const SSLBuffer *sessionKey)
{
SessionCacheEntry **current;
for (current = &(cache->head); *current; current = &((*current)->next)) {
SessionCacheEntry *entry = *current;
if (SessionCacheEntryMatchKey(entry, sessionKey)) {
#ifndef DEBUG
sslLogSessCacheDebug("...SessionCacheDeleteEntry: deleting "
"cached session (%p)", entry);
cachePrint(entry, &entry->mKey, &entry->mSessionData);
#endif
*current = entry->next;
SessionCacheEntryDelete(entry);
return noErr;
}
}
return noErr;
}
static bool SessionCacheCleanup(SessionCache *cache)
{
bool brtn = false;
CFAbsoluteTime rightNow = CFAbsoluteTimeGetCurrent();
SessionCacheEntry **current;
for (current = &(cache->head); *current;) {
SessionCacheEntry *entry = *current;
if(SessionCacheEntryIsStale(entry, rightNow)) {
#ifndef DEBUG
sslLogSessCacheDebug("...SessionCacheCleanup: deleting "
"cached session (%p)", entry);
cachePrint(entry, &entry->mKey, &entry->mSessionData);
#endif
*current = entry->next;
SessionCacheEntryDelete(entry);
}
else {
current = &((*current)->next);
brtn = true;
}
}
return brtn;
}
#if DUMP_ALL_CACHE
static void dumpAllCache(void)
{
SessionCache *cache = gSessionCache;
SessionCacheEntry *entry;
printf("Contents of sessionCache:\n");
for(entry = cache->head; entry; entry = entry->next) {
cachePrint(entry, &entry->mKey, &entry->mSessionData);
}
}
#endif
OSStatus sslAddSession (
const SSLBuffer sessionKey,
const SSLBuffer sessionData,
uint32_t timeToLive)
{
SessionCache *cache = SessionCacheGetLockedInstance();
OSStatus serr;
if (!cache)
serr = errSSLSessionNotFound;
else
{
serr = SessionCacheAddEntry(cache, &sessionKey, &sessionData, timeToLive);
dumpAllCache();
}
pthread_mutex_unlock(&gSessionCacheLock);
return serr;
}
OSStatus sslGetSession (
const SSLBuffer sessionKey,
SSLBuffer *sessionData)
{
SessionCache *cache = SessionCacheGetLockedInstance();
OSStatus serr;
if (!cache)
serr = errSSLSessionNotFound;
else
{
serr = SessionCacheLookupEntry(cache, &sessionKey, sessionData);
sslLogSessCacheDebug("sslGetSession(%d, %p): %ld",
(int)sessionKey.length, sessionKey.data,
serr);
if(!serr) {
cachePrint(NULL, &sessionKey, sessionData);
}
else {
cachePrint(NULL, &sessionKey, NULL);
}
dumpAllCache();
}
pthread_mutex_unlock(&gSessionCacheLock);
return serr;
}
OSStatus sslDeleteSession (
const SSLBuffer sessionKey)
{
SessionCache *cache = SessionCacheGetLockedInstance();
OSStatus serr;
if (!cache)
serr = errSSLSessionNotFound;
else
{
serr = SessionCacheDeleteEntry(cache, &sessionKey);
}
pthread_mutex_unlock(&gSessionCacheLock);
return serr;
}
OSStatus sslCleanupSession(void)
{
SessionCache *cache = SessionCacheGetLockedInstance();
OSStatus serr = noErr;
bool moreToGo = false;
if (!cache)
serr = errSSLSessionNotFound;
else
{
moreToGo = SessionCacheCleanup(cache);
}
pthread_mutex_unlock(&gSessionCacheLock);
return serr;
}