#include "ssl.h"
#include "sslalloc.h"
#include "appleGlue.h"
#include "sslDebug.h"
#include "appleSession.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <deque>
#include <stdexcept>
#include <Security/threading.h>
#include <Security/globalizer.h>
#include <Security/timeflow.h>
#define QUICK_CACHE_TEST 0
#if QUICK_CACHE_TEST
#define SESSION_CACHE_TTL ((int)5)
#else
#define SESSION_CACHE_TTL ((int)(10 * 60))
#endif
#define CACHE_PRINT 0
#if CACHE_PRINT
#define cprintf(s) printf s
#define DUMP_ALL_CACHE 0
static void cachePrint(
const SSLBuffer *key,
const SSLBuffer *data)
{
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 cprintf(s)
#define cachePrint(k, d)
#define DUMP_ALL_CACHE 0
#endif
#if DUMP_ALL_CACHE
static void dumpAllCache();
#else
#define dumpAllCache()
#endif
class SessionCacheEntry {
public:
SessionCacheEntry(
const SSLBuffer &key,
const SSLBuffer &sessionData,
const Time::Absolute &expirationTime);
~SessionCacheEntry();
bool matchKey(const SSLBuffer &key) const;
bool isStale(); bool isStale(const Time::Absolute &now);
SSLBuffer &key() { return mKey; }
SSLBuffer &sessionData() { return mSessionData; }
SSLErr sessionData(const SSLBuffer &data);
private:
SSLBuffer mKey;
SSLBuffer mSessionData;
Time::Absolute mExpiration;
};
SessionCacheEntry::SessionCacheEntry(
const SSLBuffer &key,
const SSLBuffer &sessionData,
const Time::Absolute &expirationTime)
: mExpiration(expirationTime)
{
SSLErr serr;
serr = SSLCopyBuffer(&key, &mKey);
if(serr) {
throw runtime_error("memory error");
}
serr = SSLCopyBuffer(&sessionData, &mSessionData);
if(serr) {
throw runtime_error("memory error");
}
cprintf(("SessionCacheEntry(buf,buf) this %p\n", this));
mExpiration += Time::Interval(SESSION_CACHE_TTL);
}
SessionCacheEntry::~SessionCacheEntry()
{
cprintf(("~SessionCacheEntry() this %p\n", this));
SSLFreeBuffer(&mKey, NULL); SSLFreeBuffer(&mSessionData, NULL);
}
bool SessionCacheEntry::matchKey(const SSLBuffer &key) const
{
if(key.length != mKey.length) {
return false;
}
if((key.data == NULL) || (mKey.data == NULL)) {
return false;
}
return (memcmp(key.data, mKey.data, mKey.length) == 0);
}
bool SessionCacheEntry::isStale()
{
return isStale(Time::now());
}
bool SessionCacheEntry::isStale(const Time::Absolute &now)
{
if(now > mExpiration) {
return true;
}
else {
return false;
}
}
SSLErr SessionCacheEntry::sessionData(
const SSLBuffer &data)
{
SSLFreeBuffer(&mSessionData, NULL);
return SSLCopyBuffer(&data, &mSessionData);
}
typedef std::deque<SessionCacheEntry *> SessionCacheType;
typedef SessionCacheType::iterator SessionCacheIter;
class SessionCache
{
public:
SessionCache()
: mTimeToLive(SESSION_CACHE_TTL) {}
~SessionCache();
SSLErr addEntry(
const SSLBuffer sessionKey,
const SSLBuffer sessionData);
SSLErr lookupEntry(
const SSLBuffer sessionKey,
SSLBuffer *sessionData);
SSLErr deleteEntry(
const SSLBuffer sessionKey);
bool cleanup();
SessionCacheType &sessMap() { return mSessionCache; }
private:
SessionCacheIter lookupPriv(
const SSLBuffer *sessionKey);
void deletePriv(
const SSLBuffer *sessionKey);
SessionCacheIter deletePriv(
SessionCacheIter iter);
SessionCacheType mSessionCache;
Mutex mSessionLock;
const Time::Interval mTimeToLive;
};
SessionCache::~SessionCache()
{
StLock<Mutex> _(mSessionLock);
for(SessionCacheIter iter = mSessionCache.begin(); iter != mSessionCache.end(); ) {
iter = deletePriv(iter);
}
}
SSLErr SessionCache::addEntry(
const SSLBuffer sessionKey,
const SSLBuffer sessionData)
{
StLock<Mutex> _(mSessionLock);
SessionCacheIter existIter = lookupPriv(&sessionKey);
if(existIter != mSessionCache.end()) {
SessionCacheEntry *existEntry = *existIter;
SSLBuffer &existBuf = existEntry->sessionData();
if((existBuf.length == sessionData.length) &&
(memcmp(existBuf.data, sessionData.data, sessionData.length) == 0)) {
cprintf(("SessionCache::addEntry CACHE HIT entry = %p\n", existEntry));
return SSLNoErr;
}
else {
cprintf(("SessionCache::addEntry CACHE REPLACE entry = %p\n", existEntry));
return existEntry->sessionData(sessionData);
}
}
SessionCacheEntry *entry = new SessionCacheEntry(sessionKey,
sessionData,
Time::now() + mTimeToLive);
cprintf(("SessionCache::addEntry %p\n", entry));
cachePrint(&sessionKey, &sessionData);
dumpAllCache();
mSessionCache.push_front(entry);
CASSERT(lookupPriv(&sessionKey) != mSessionCache.end());
return SSLNoErr;
}
SSLErr SessionCache::lookupEntry(
const SSLBuffer sessionKey,
SSLBuffer *sessionData)
{
StLock<Mutex> _(mSessionLock);
SessionCacheIter existIter = lookupPriv(&sessionKey);
if(existIter == mSessionCache.end()) {
return SSLSessionNotFoundErr;
}
SessionCacheEntry *entry = *existIter;
if(entry->isStale()) {
cprintf(("SessionCache::lookupEntry %p: STALE entry, deleting\n", entry));
cachePrint(&sessionKey, &entry->sessionData());
deletePriv(existIter);
return SSLSessionNotFoundErr;
}
return SSLCopyBuffer(&entry->sessionData(), sessionData);
}
SSLErr SessionCache::deleteEntry(
const SSLBuffer sessionKey)
{
StLock<Mutex> _(mSessionLock);
deletePriv(&sessionKey);
return SSLNoErr;
}
bool SessionCache::cleanup()
{
StLock<Mutex> _(mSessionLock);
bool brtn = false;
Time::Absolute rightNow = Time::now();
SessionCacheIter iter;
for(iter = mSessionCache.begin(); iter != mSessionCache.end(); ) {
SessionCacheEntry *entry = *iter;
if(entry->isStale(rightNow)) {
#if CACHE_PRINT
SSLBuffer *key = &entry->key();
cprintf(("...SessionCache::cleanup: deleting cached session (%p)\n",
entry));
cachePrint(key, &entry->sessionData());
#endif
iter = deletePriv(iter);
}
else {
iter++;
brtn = true;
}
}
return brtn;
}
SessionCacheIter SessionCache::lookupPriv(
const SSLBuffer *sessionKey)
{
SessionCacheIter it;
for(it = mSessionCache.begin(); it != mSessionCache.end(); it++) {
SessionCacheEntry *entry = *it;
if(entry->matchKey(*sessionKey)) {
return it;
}
}
return it;
}
void SessionCache::deletePriv(
const SSLBuffer *sessionKey)
{
SessionCacheIter iter = lookupPriv(sessionKey);
if(iter != mSessionCache.end()) {
#if CACHE_PRINT
SessionCacheEntry *entry = *iter;
cprintf(("SessionCache::deletePriv %p\n", entry));
cachePrint(sessionKey, &entry->sessionData());
dumpAllCache();
#endif
deletePriv(iter);
}
CASSERT(lookupPriv(sessionKey) == mSessionCache.end());
}
SessionCacheIter SessionCache::deletePriv(
SessionCacheIter iter)
{
CASSERT(iter != mSessionCache.end());
SessionCacheEntry *entry = *iter;
SessionCacheIter nextIter = mSessionCache.erase(iter);
delete entry;
return nextIter;
}
static ModuleNexus<SessionCache> gSessionCache;
#if DUMP_ALL_CACHE
static void dumpAllCache()
{
SessionCacheIter it;
SessionCacheType &smap = gSessionCache().sessMap();
printf("Contents of sessionCache:\n");
for(it = smap.begin(); it != smap.end(); it++) {
SessionCacheEntry *entry = *it;
cachePrint(&entry->key(), &entry->sessionData());
}
}
#endif
SSLErr sslAddSession (
const SSLBuffer sessionKey,
const SSLBuffer sessionData)
{
SSLErr serr;
try {
serr = gSessionCache().addEntry(sessionKey, sessionData);
}
catch(...) {
serr = SSLUnsupportedErr;
}
dumpAllCache();
return serr;
}
SSLErr sslGetSession (
const SSLBuffer sessionKey,
SSLBuffer *sessionData)
{
SSLErr serr;
try {
serr = gSessionCache().lookupEntry(sessionKey, sessionData);
}
catch(...) {
serr = SSLSessionNotFoundErr;
}
cprintf(("\nsslGetSession(%d, %p): %d\n", (int)sessionKey.length, sessionKey.data,
serr));
if(serr == SSLNoErr) {
cachePrint(&sessionKey, sessionData);
}
else {
cachePrint(&sessionKey, NULL);
}
dumpAllCache();
return serr;
}
SSLErr sslDeleteSession (
const SSLBuffer sessionKey)
{
SSLErr serr;
try {
serr = gSessionCache().deleteEntry(sessionKey);
}
catch(...) {
serr = SSLSessionNotFoundErr;
}
return serr;
}
SSLErr sslCleanupSession ()
{
SSLErr serr = SSLNoErr;
bool moreToGo = false;
try {
moreToGo = gSessionCache().cleanup();
}
catch(...) {
serr = SSLSessionNotFoundErr;
}
return serr;
}