From 5c4fdb30adb86c47326818825cc5a9d91fbcf574 Mon Sep 17 00:00:00 2001 From: toddouska Date: Mon, 29 Apr 2013 14:22:32 -0700 Subject: [PATCH] add client session table lookup based on serverID, use CyaSSL_SetServerID to set/store with serverid --- cyassl/internal.h | 16 +++- cyassl/ssl.h | 5 +- src/internal.c | 4 + src/ssl.c | 204 +++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 213 insertions(+), 16 deletions(-) diff --git a/cyassl/internal.h b/cyassl/internal.h index c2a7a8adc..405758b4a 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -587,6 +587,7 @@ enum Misc { ENUM_LEN = 1, /* always a byte */ COMP_LEN = 1, /* compression length */ CURVE_LEN = 2, /* ecc named curve length */ + SERVER_ID_LEN = 20, /* server session id length */ HANDSHAKE_HEADER_SZ = 4, /* type + length(3) */ RECORD_HEADER_SZ = 5, /* type + version + len(2) */ @@ -1363,16 +1364,20 @@ struct CYASSL_X509_CHAIN { /* CyaSSL session type */ struct CYASSL_SESSION { - byte sessionID[ID_LEN]; - byte masterSecret[SECRET_LEN]; + byte sessionID[ID_LEN]; /* id for protocol */ + byte masterSecret[SECRET_LEN]; /* stored secret */ word32 bornOn; /* create time in seconds */ word32 timeout; /* timeout in seconds */ #ifdef SESSION_CERTS - CYASSL_X509_CHAIN chain; /* peer cert chain, static */ - ProtocolVersion version; + CYASSL_X509_CHAIN chain; /* peer cert chain, static */ + ProtocolVersion version; /* which version was used */ byte cipherSuite0; /* first byte, normally 0 */ byte cipherSuite; /* 2nd byte, actual suite */ #endif +#ifndef NO_CLIENT_CACHE + byte serverID[SERVER_ID_LEN]; /* for easier client lookup */ + word16 idLen; /* serverID length */ +#endif }; @@ -1383,6 +1388,9 @@ int SetSession(CYASSL*, CYASSL_SESSION*); typedef void (*hmacfp) (CYASSL*, byte*, const byte*, word32, int, int); +#ifndef NO_CLIENT_CACHE + CYASSL_SESSION* GetSessionClient(CYASSL*, const byte*, int); +#endif /* client connect state for nonblocking restart */ enum ConnectState { diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 05675b9b3..d7a2c85fe 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -214,9 +214,10 @@ CYASSL_API void CyaSSL_set_quiet_shutdown(CYASSL*, int); CYASSL_API int CyaSSL_get_error(CYASSL*, int); CYASSL_API int CyaSSL_get_alert_history(CYASSL*, CYASSL_ALERT_HISTORY *); -CYASSL_API int CyaSSL_set_session(CYASSL* ssl,CYASSL_SESSION* session); +CYASSL_API int CyaSSL_set_session(CYASSL* ssl,CYASSL_SESSION* session); CYASSL_API CYASSL_SESSION* CyaSSL_get_session(CYASSL* ssl); -CYASSL_API void CyaSSL_flush_sessions(CYASSL_CTX *ctx, long tm); +CYASSL_API void CyaSSL_flush_sessions(CYASSL_CTX *ctx, long tm); +CYASSL_API int CyaSSL_SetServerID(CYASSL* ssl, const unsigned char*,int); typedef int (*VerifyCallback)(int, CYASSL_X509_STORE_CTX*); diff --git a/src/internal.c b/src/internal.c index f3291b064..eb5eab23c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1408,6 +1408,10 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->session.chain.count = 0; #endif +#ifndef NO_CLIENT_CACHE + ssl->session.idLen = 0; +#endif + ssl->cipher.ssl = ssl; #ifdef FORTRESS diff --git a/src/ssl.c b/src/ssl.c index 1a548e2e3..05cabd8fb 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1037,9 +1037,27 @@ int AddCA(CYASSL_CERT_MANAGER* cm, buffer der, int type, int verify) static CyaSSL_Mutex session_mutex; /* SessionCache mutex */ + #ifndef NO_CLIENT_CACHE + + typedef struct ClientSession { + word16 serverRow; /* SessionCache Row id */ + word16 serverIdx; /* SessionCache Idx (column) */ + } ClientSession; + + typedef struct ClientRow { + int nextIdx; /* where to place next one */ + int totalCount; /* sessions ever on this row */ + ClientSession Clients[SESSIONS_PER_ROW]; + } ClientRow; + + static ClientRow ClientCache[SESSION_ROWS]; /* Client Cache */ + /* uses session mutex */ + + #endif /* NO_CLIENT_CACHE */ + /* for persistance, if changes to layout need to increment and modify save_session_cache() and restore_session_cache and memory versions too */ - #define CYASSL_CACHE_VERSION 1 + #define CYASSL_CACHE_VERSION 2 #endif /* NO_SESSION_CACHE */ @@ -2566,6 +2584,39 @@ int CyaSSL_set_session(CYASSL* ssl, CYASSL_SESSION* session) } +#ifndef NO_CLIENT_CACHE + +/* Assocaite client session with serverID, find existing or store for saving + SSL_SUCCESS on ok */ +int CyaSSL_SetServerID(CYASSL* ssl, const byte* id, int len) +{ + CYASSL_SESSION* session; + + CYASSL_ENTER("CyaSSL_SetServerID"); + + if (ssl == NULL || id == NULL || len <= 0) + return BAD_FUNC_ARG; + + session = GetSessionClient(ssl, id, len); + if (session) { + if (SetSession(ssl, session) != SSL_SUCCESS) { + CYASSL_MSG("SetSession failed"); + session = NULL; + } + } + + if (session == NULL) { + CYASSL_MSG("Valid ServerID not cached already"); + + ssl->session.idLen = min(SERVER_ID_LEN, (word32)len); + XMEMCPY(ssl->session.serverID, id, ssl->session.idLen); + } + + return SSL_SUCCESS; +} + +#endif + #if defined(PERSIST_SESSION_CACHE) /* Session Cache Header information */ @@ -2580,6 +2631,7 @@ typedef struct { 1) cache_header_t 2) SessionCache + 3) ClientCache update CYASSL_CACHE_VERSION if change layout for the following PERSISTENT_SESSION_CACHE functions @@ -2599,6 +2651,9 @@ int CyaSSL_memsave_session_cache(void* mem, int sz) int i; cache_header_t cache_header; SessionRow* row = (SessionRow*)((byte*)mem + sizeof(cache_header)); +#ifndef NO_CLIENT_CACHE + ClientRow* clRow; +#endif CYASSL_ENTER("CyaSSL_memsave_session_cache"); @@ -2621,6 +2676,12 @@ int CyaSSL_memsave_session_cache(void* mem, int sz) for (i = 0; i < cache_header.rows; ++i) XMEMCPY(row++, SessionCache + i, sizeof(SessionRow)); +#ifndef NO_CLIENT_CACHE + clRow = (ClientRow*)row; + for (i = 0; i < cache_header.rows; ++i) + XMEMCPY(clRow++, ClientCache + i, sizeof(ClientRow)); +#endif + UnLockMutex(&session_mutex); CYASSL_LEAVE("CyaSSL_memsave_session_cache", SSL_SUCCESS); @@ -2635,6 +2696,9 @@ int CyaSSL_memrestore_session_cache(const void* mem, int sz) int i; cache_header_t cache_header; SessionRow* row = (SessionRow*)((byte*)mem + sizeof(cache_header)); +#ifndef NO_CLIENT_CACHE + ClientRow* clRow; +#endif CYASSL_ENTER("CyaSSL_memrestore_session_cache"); @@ -2661,6 +2725,12 @@ int CyaSSL_memrestore_session_cache(const void* mem, int sz) for (i = 0; i < cache_header.rows; ++i) XMEMCPY(SessionCache + i, row++, sizeof(SessionRow)); +#ifndef NO_CLIENT_CACHE + clRow = (ClientRow*)row; + for (i = 0; i < cache_header.rows; ++i) + XMEMCPY(ClientCache + i, clRow++, sizeof(ClientRow)); +#endif + UnLockMutex(&session_mutex); CYASSL_LEAVE("CyaSSL_memrestore_session_cache", SSL_SUCCESS); @@ -2692,6 +2762,7 @@ int CyaSSL_save_session_cache(const char *fname) cache_header.columns = SESSIONS_PER_ROW; cache_header.sessionSz = (int)sizeof(CYASSL_SESSION); + /* cache header */ ret = (int)XFWRITE(&cache_header, sizeof cache_header, 1, file); if (ret != 1) { CYASSL_MSG("Session cache header file write failed"); @@ -2705,6 +2776,7 @@ int CyaSSL_save_session_cache(const char *fname) return BAD_MUTEX_ERROR; } + /* session cache */ for (i = 0; i < cache_header.rows; ++i) { ret = (int)XFWRITE(SessionCache + i, sizeof(SessionRow), 1, file); if (ret != 1) { @@ -2714,6 +2786,18 @@ int CyaSSL_save_session_cache(const char *fname) } } +#ifndef NO_CLIENT_CACHE + /* client cache */ + for (i = 0; i < cache_header.rows; ++i) { + ret = (int)XFWRITE(ClientCache + i, sizeof(ClientRow), 1, file); + if (ret != 1) { + CYASSL_MSG("Client cache member file write failed"); + rc = FWRITE_ERROR; + break; + } + } +#endif /* NO_CLIENT_CACHE */ + UnLockMutex(&session_mutex); XFCLOSE(file); @@ -2740,6 +2824,7 @@ int CyaSSL_restore_session_cache(const char *fname) CYASSL_MSG("Couldn't open session cache save file"); return SSL_BAD_FILE; } + /* cache header */ ret = (int)XFREAD(&cache_header, sizeof cache_header, 1, file); if (ret != 1) { CYASSL_MSG("Session cache header file read failed"); @@ -2762,6 +2847,7 @@ int CyaSSL_restore_session_cache(const char *fname) return BAD_MUTEX_ERROR; } + /* session cache */ for (i = 0; i < cache_header.rows; ++i) { ret = (int)XFREAD(SessionCache + i, sizeof(SessionRow), 1, file); if (ret != 1) { @@ -2772,6 +2858,20 @@ int CyaSSL_restore_session_cache(const char *fname) } } +#ifndef NO_CLIENT_CACHE + /* client cache */ + for (i = 0; i < cache_header.rows; ++i) { + ret = (int)XFREAD(ClientCache + i, sizeof(ClientRow), 1, file); + if (ret != 1) { + CYASSL_MSG("Client cache member file read failed"); + XMEMSET(ClientCache, 0, sizeof ClientCache); + rc = FREAD_ERROR; + break; + } + } + +#endif /* NO_CLIENT_CACHE */ + UnLockMutex(&session_mutex); XFCLOSE(file); @@ -3448,18 +3548,17 @@ int CyaSSL_Cleanup(void) #ifndef NO_SESSION_CACHE - #ifndef NO_MD5 /* some session IDs aren't random afterall, let's make them random */ -static INLINE word32 HashSession(const byte* sessionID) +static INLINE word32 HashSession(const byte* sessionID, word32 len) { byte digest[MD5_DIGEST_SIZE]; Md5 md5; InitMd5(&md5); - Md5Update(&md5, sessionID, ID_LEN); + Md5Update(&md5, sessionID, len); Md5Final(&md5, digest); return MakeWordFromHash(digest); @@ -3467,13 +3566,13 @@ static INLINE word32 HashSession(const byte* sessionID) #elif !defined(NO_SHA) -static INLINE word32 HashSession(const byte* sessionID) +static INLINE word32 HashSession(const byte* sessionID, word32 len) { byte digest[SHA_DIGEST_SIZE]; Sha sha; InitSha(&sha); - ShaUpdate(&sha, sessionID, ID_LEN); + ShaUpdate(&sha, sessionID, len); ShaFinal(&sha, digest); return MakeWordFromHash(digest); @@ -3481,13 +3580,13 @@ static INLINE word32 HashSession(const byte* sessionID) #elif !defined(NO_SHA256) -static INLINE word32 HashSession(const byte* sessionID) +static INLINE word32 HashSession(const byte* sessionID, word32 len) { byte digest[SHA256_DIGEST_SIZE]; Sha256 sha256; InitSha256(&sha256); - Sha256Update(&sha256, sessionID, ID_LEN); + Sha256Update(&sha256, sessionID, len); Sha256Final(&sha256, digest); return MakeWordFromHash(digest); @@ -3532,6 +3631,65 @@ int CyaSSL_CTX_set_timeout(CYASSL_CTX* ctx, unsigned int to) } +#ifndef NO_CLIENT_CACHE + +/* Get Session from Client cache based on id/len, return NULL on failure */ +CYASSL_SESSION* GetSessionClient(CYASSL* ssl, const byte* id, int len) +{ + CYASSL_SESSION* ret = NULL; + word32 row; + int idx; + + CYASSL_ENTER("GetSessionClient"); + + if (ssl->options.side == SERVER_END) + return NULL; + + len = min(SERVER_ID_LEN, (word32)len); + row = HashSession(id, len) % SESSION_ROWS; + + if (LockMutex(&session_mutex) != 0) { + CYASSL_MSG("Lock sessoin mutex failed"); + return NULL; + } + + /* start from most recently used */ + if (ClientCache[row].totalCount >= SESSIONS_PER_ROW) + idx = SESSIONS_PER_ROW - 1; + else + idx = ClientCache[row].nextIdx - 1; + + for (; idx >= 0; idx--) { + CYASSL_SESSION* current; + ClientSession clSess; + + if (idx >= SESSIONS_PER_ROW) /* client could have restarted, idx */ + break; /* would be word32(-1) and seg fault */ + + clSess = ClientCache[row].Clients[idx]; + + current = &SessionCache[clSess.serverRow].Sessions[clSess.serverIdx]; + if (XMEMCMP(current->serverID, id, len) == 0) { + if (LowResTimer() < (current->bornOn + current->timeout)) { + ret = current; + } else { + CYASSL_MSG("Session timed out"); + } + + break; + } else { + CYASSL_MSG("ServerID not a match from client table"); + } + } + + UnLockMutex(&session_mutex); + + return ret; +} + +#endif /* NO_CLIENT_CACHE */ + + CYASSL_SESSION* GetSession(CYASSL* ssl, byte* masterSecret) { CYASSL_SESSION* ret = 0; @@ -3550,11 +3708,12 @@ CYASSL_SESSION* GetSession(CYASSL* ssl, byte* masterSecret) else id = ssl->session.sessionID; - row = HashSession(id) % SESSION_ROWS; + row = HashSession(id, ID_LEN) % SESSION_ROWS; if (LockMutex(&session_mutex) != 0) return 0; + /* start from most recently used */ if (SessionCache[row].totalCount >= SESSIONS_PER_ROW) idx = SESSIONS_PER_ROW - 1; else @@ -3614,7 +3773,7 @@ int AddSession(CYASSL* ssl) if (ssl->options.haveSessionId == 0) return 0; - row = HashSession(ssl->arrays->sessionID) % SESSION_ROWS; + row = HashSession(ssl->arrays->sessionID, ID_LEN) % SESSION_ROWS; if (LockMutex(&session_mutex) != 0) return BAD_MUTEX_ERROR; @@ -3643,6 +3802,31 @@ int AddSession(CYASSL* ssl) if (SessionCache[row].nextIdx == SESSIONS_PER_ROW) SessionCache[row].nextIdx = 0; +#ifndef NO_CLIENT_CACHE + if (ssl->options.side == CLIENT_END && ssl->session.idLen) { + word32 clientRow, clientIdx; + + CYASSL_MSG("Adding client cache entry"); + + SessionCache[row].Sessions[idx].idLen = ssl->session.idLen; + XMEMCPY(SessionCache[row].Sessions[idx].serverID, ssl->session.serverID, + ssl->session.idLen); + + clientRow = HashSession(ssl->session.serverID, ssl->session.idLen) + % SESSION_ROWS; + clientIdx = ClientCache[clientRow].nextIdx++; + + ClientCache[clientRow].Clients[clientIdx].serverRow = row; + ClientCache[clientRow].Clients[clientIdx].serverIdx = idx; + + ClientCache[clientRow].totalCount++; + if (ClientCache[clientRow].nextIdx == SESSIONS_PER_ROW) + SessionCache[clientRow].nextIdx = 0; + } + else + SessionCache[row].Sessions[idx].idLen = 0; +#endif + if (UnLockMutex(&session_mutex) != 0) return BAD_MUTEX_ERROR;