/* ssl_sess.c * * Copyright (C) 2006-2024 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #ifdef HAVE_CONFIG_H #include #endif #include #if !defined(WOLFSSL_SSL_SESS_INCLUDED) #ifndef WOLFSSL_IGNORE_FILE_WARN #warning ssl_sess.c does not need to be compiled separately from ssl.c #endif #else #ifndef NO_SESSION_CACHE /* basic config gives a cache with 33 sessions, adequate for clients and embedded servers TITAN_SESSION_CACHE allows just over 2 million sessions, for servers with titanic amounts of memory with long session ID timeouts and high levels of traffic. ENABLE_SESSION_CACHE_ROW_LOCK: Allows row level locking for increased performance with large session caches HUGE_SESSION_CACHE yields 65,791 sessions, for servers under heavy load, allows over 13,000 new sessions per minute or over 200 new sessions per second BIG_SESSION_CACHE yields 20,027 sessions MEDIUM_SESSION_CACHE allows 1055 sessions, adequate for servers that aren't under heavy load, basically allows 200 new sessions per minute SMALL_SESSION_CACHE only stores 6 sessions, good for embedded clients or systems where the default of is too much RAM. SessionCache takes about 2K, ClientCache takes about 3Kbytes MICRO_SESSION_CACHE only stores 1 session, good for embedded clients or systems where memory is at a premium. SessionCache takes about 400 bytes, ClientCache takes 576 bytes default SESSION_CACHE stores 33 sessions (no XXX_SESSION_CACHE defined) SessionCache takes about 13K bytes, ClientCache takes 17K bytes */ #if defined(TITAN_SESSION_CACHE) #define SESSIONS_PER_ROW 31 #define SESSION_ROWS 64937 #ifndef ENABLE_SESSION_CACHE_ROW_LOCK #define ENABLE_SESSION_CACHE_ROW_LOCK #endif #elif defined(HUGE_SESSION_CACHE) #define SESSIONS_PER_ROW 11 #define SESSION_ROWS 5981 #elif defined(BIG_SESSION_CACHE) #define SESSIONS_PER_ROW 7 #define SESSION_ROWS 2861 #elif defined(MEDIUM_SESSION_CACHE) #define SESSIONS_PER_ROW 5 #define SESSION_ROWS 211 #elif defined(SMALL_SESSION_CACHE) #define SESSIONS_PER_ROW 2 #define SESSION_ROWS 3 #elif defined(MICRO_SESSION_CACHE) #define SESSIONS_PER_ROW 1 #define SESSION_ROWS 1 #else #define SESSIONS_PER_ROW 3 #define SESSION_ROWS 11 #endif #define INVALID_SESSION_ROW (-1) #ifdef NO_SESSION_CACHE_ROW_LOCK #undef ENABLE_SESSION_CACHE_ROW_LOCK #endif typedef struct SessionRow { int nextIdx; /* where to place next one */ int totalCount; /* sessions ever on this row */ #ifdef SESSION_CACHE_DYNAMIC_MEM WOLFSSL_SESSION* Sessions[SESSIONS_PER_ROW]; void* heap; #else WOLFSSL_SESSION Sessions[SESSIONS_PER_ROW]; #endif #ifdef ENABLE_SESSION_CACHE_ROW_LOCK /* not included in import/export */ wolfSSL_RwLock row_lock; int lock_valid; #endif } SessionRow; #define SIZEOF_SESSION_ROW (sizeof(WOLFSSL_SESSION) + (sizeof(int) * 2)) static WOLFSSL_GLOBAL SessionRow SessionCache[SESSION_ROWS]; #if defined(WOLFSSL_SESSION_STATS) && defined(WOLFSSL_PEAK_SESSIONS) static WOLFSSL_GLOBAL word32 PeakSessions; #endif #ifdef ENABLE_SESSION_CACHE_ROW_LOCK #define SESSION_ROW_RD_LOCK(row) wc_LockRwLock_Rd(&(row)->row_lock) #define SESSION_ROW_WR_LOCK(row) wc_LockRwLock_Wr(&(row)->row_lock) #define SESSION_ROW_UNLOCK(row) wc_UnLockRwLock(&(row)->row_lock); #else static WOLFSSL_GLOBAL wolfSSL_RwLock session_lock; /* SessionCache lock */ static WOLFSSL_GLOBAL int session_lock_valid = 0; #define SESSION_ROW_RD_LOCK(row) wc_LockRwLock_Rd(&session_lock) #define SESSION_ROW_WR_LOCK(row) wc_LockRwLock_Wr(&session_lock) #define SESSION_ROW_UNLOCK(row) wc_UnLockRwLock(&session_lock); #endif #if !defined(NO_SESSION_CACHE_REF) && defined(NO_CLIENT_CACHE) #error ClientCache is required when not using NO_SESSION_CACHE_REF #endif #ifndef NO_CLIENT_CACHE #ifndef CLIENT_SESSIONS_MULTIPLIER #ifdef NO_SESSION_CACHE_REF #define CLIENT_SESSIONS_MULTIPLIER 1 #else /* ClientSession objects are lightweight (compared to * WOLFSSL_SESSION) so to decrease chance that user will reuse * the wrong session, increase the ClientCache size. This will * make the entire ClientCache about the size of one * WOLFSSL_SESSION object. */ #define CLIENT_SESSIONS_MULTIPLIER 8 #endif #endif #define CLIENT_SESSIONS_PER_ROW \ (SESSIONS_PER_ROW * CLIENT_SESSIONS_MULTIPLIER) #define CLIENT_SESSION_ROWS (SESSION_ROWS * CLIENT_SESSIONS_MULTIPLIER) #if CLIENT_SESSIONS_PER_ROW > 65535 #error CLIENT_SESSIONS_PER_ROW too big #endif #if CLIENT_SESSION_ROWS > 65535 #error CLIENT_SESSION_ROWS too big #endif struct ClientSession { word16 serverRow; /* SessionCache Row id */ word16 serverIdx; /* SessionCache Idx (column) */ word32 sessionIDHash; }; #ifndef WOLFSSL_CLIENT_SESSION_DEFINED typedef struct ClientSession ClientSession; #define WOLFSSL_CLIENT_SESSION_DEFINED #endif typedef struct ClientRow { int nextIdx; /* where to place next one */ int totalCount; /* sessions ever on this row */ ClientSession Clients[CLIENT_SESSIONS_PER_ROW]; } ClientRow; static WOLFSSL_GLOBAL ClientRow ClientCache[CLIENT_SESSION_ROWS]; /* Client Cache */ /* uses session mutex */ /* ClientCache mutex */ static WOLFSSL_GLOBAL wolfSSL_Mutex clisession_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(clisession_mutex); #ifndef WOLFSSL_MUTEX_INITIALIZER static WOLFSSL_GLOBAL int clisession_mutex_valid = 0; #endif #endif /* !NO_CLIENT_CACHE */ void EvictSessionFromCache(WOLFSSL_SESSION* session) { #ifdef HAVE_EX_DATA int save_ownExData = session->ownExData; session->ownExData = 1; /* Make sure ex_data access doesn't lead back * into the cache. */ #endif #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) if (session->rem_sess_cb != NULL) { session->rem_sess_cb(NULL, session); session->rem_sess_cb = NULL; } #endif ForceZero(session->masterSecret, SECRET_LEN); XMEMSET(session->sessionID, 0, ID_LEN); session->sessionIDSz = 0; #ifdef HAVE_SESSION_TICKET if (session->ticketLenAlloc > 0) { XFREE(session->ticket, NULL, DYNAMIC_TYPE_SESSION_TICK); session->ticket = session->staticTicket; session->ticketLen = 0; session->ticketLenAlloc = 0; } #endif #ifdef HAVE_EX_DATA session->ownExData = save_ownExData; #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if ((session->ticketNonce.data != NULL) && (session->ticketNonce.data != session->ticketNonce.dataStatic)) { XFREE(session->ticketNonce.data, NULL, DYNAMIC_TYPE_SESSION_TICK); session->ticketNonce.data = NULL; } #endif } WOLFSSL_ABI WOLFSSL_SESSION* wolfSSL_get_session(WOLFSSL* ssl) { WOLFSSL_ENTER("wolfSSL_get_session"); if (ssl) { #ifdef NO_SESSION_CACHE_REF return ssl->session; #else if (ssl->options.side == WOLFSSL_CLIENT_END) { /* On the client side we want to return a persistent reference for * backwards compatibility. */ #ifndef NO_CLIENT_CACHE if (ssl->clientSession) { return (WOLFSSL_SESSION*)ssl->clientSession; } else { /* Try to add a ClientCache entry to associate with the current * session. Ignore any session cache options. */ int err; const byte* id = ssl->session->sessionID; byte idSz = ssl->session->sessionIDSz; if (ssl->session->haveAltSessionID) { id = ssl->session->altSessionID; idSz = ID_LEN; } err = AddSessionToCache(ssl->ctx, ssl->session, id, idSz, NULL, ssl->session->side, #ifdef HAVE_SESSION_TICKET ssl->session->ticketLen > 0, #else 0, #endif &ssl->clientSession); if (err == 0) { return (WOLFSSL_SESSION*)ssl->clientSession; } } #endif } else { return ssl->session; } #endif } return NULL; } /* The get1 version requires caller to call SSL_SESSION_free */ WOLFSSL_SESSION* wolfSSL_get1_session(WOLFSSL* ssl) { WOLFSSL_SESSION* sess = NULL; WOLFSSL_ENTER("wolfSSL_get1_session"); if (ssl != NULL) { sess = ssl->session; if (sess != NULL) { /* increase reference count if allocated session */ if (sess->type == WOLFSSL_SESSION_TYPE_HEAP) { if (wolfSSL_SESSION_up_ref(sess) != WOLFSSL_SUCCESS) sess = NULL; } } } return sess; } /* session is a private struct, return if it is setup or not */ WOLFSSL_API int wolfSSL_SessionIsSetup(WOLFSSL_SESSION* session) { if (session != NULL) return session->isSetup; return 0; } /* * Sets the session object to use when establishing a TLS/SSL session using * the ssl object. Therefore, this function must be called before * wolfSSL_connect. The session object to use can be obtained in a previous * TLS/SSL connection using wolfSSL_get_session. * * This function rejects the session if it has been expired when this function * is called. Note that this expiration check is wolfSSL specific and differs * from OpenSSL return code behavior. * * By default, wolfSSL_set_session returns WOLFSSL_SUCCESS on successfully * setting the session, WOLFSSL_FAILURE on failure due to the session cache * being disabled, or the session has expired. * * To match OpenSSL return code behavior when session is expired, define * OPENSSL_EXTRA and WOLFSSL_ERROR_CODE_OPENSSL. This behavior will return * WOLFSSL_SUCCESS even when the session is expired and rejected. */ WOLFSSL_ABI int wolfSSL_set_session(WOLFSSL* ssl, WOLFSSL_SESSION* session) { WOLFSSL_ENTER("wolfSSL_set_session"); if (session) return wolfSSL_SetSession(ssl, session); return WOLFSSL_FAILURE; } #ifndef NO_CLIENT_CACHE /* Associate client session with serverID, find existing or store for saving if newSession flag on, don't reuse existing session WOLFSSL_SUCCESS on ok */ int wolfSSL_SetServerID(WOLFSSL* ssl, const byte* id, int len, int newSession) { WOLFSSL_SESSION* session = NULL; byte idHash[SERVER_ID_LEN]; WOLFSSL_ENTER("wolfSSL_SetServerID"); if (ssl == NULL || id == NULL || len <= 0) return BAD_FUNC_ARG; if (len > SERVER_ID_LEN) { #if defined(NO_SHA) && !defined(NO_SHA256) if (wc_Sha256Hash(id, len, idHash) != 0) return WOLFSSL_FAILURE; #else if (wc_ShaHash(id, (word32)len, idHash) != 0) return WOLFSSL_FAILURE; #endif id = idHash; len = SERVER_ID_LEN; } if (newSession == 0) { session = wolfSSL_GetSessionClient(ssl, id, len); if (session) { if (wolfSSL_SetSession(ssl, session) != WOLFSSL_SUCCESS) { #ifdef HAVE_EXT_CACHE wolfSSL_FreeSession(ssl->ctx, session); #endif WOLFSSL_MSG("wolfSSL_SetSession failed"); session = NULL; } } } if (session == NULL) { WOLFSSL_MSG("Valid ServerID not cached already"); ssl->session->idLen = (word16)len; XMEMCPY(ssl->session->serverID, id, len); } #ifdef HAVE_EXT_CACHE else { wolfSSL_FreeSession(ssl->ctx, session); } #endif return WOLFSSL_SUCCESS; } #endif /* !NO_CLIENT_CACHE */ /* TODO: Add SESSION_CACHE_DYNAMIC_MEM support for PERSIST_SESSION_CACHE. * Need a count of current sessions to get an accurate memsize (totalCount is * not decremented when sessions are removed). * Need to determine ideal layout for mem/filesave. * Also need mem/filesave checking to ensure not restoring non DYNAMIC_MEM * cache. */ #if defined(PERSIST_SESSION_CACHE) && !defined(SESSION_CACHE_DYNAMIC_MEM) /* for persistence, if changes to layout need to increment and modify save_session_cache() and restore_session_cache and memory versions too */ #define WOLFSSL_CACHE_VERSION 2 /* Session Cache Header information */ typedef struct { int version; /* cache layout version id */ int rows; /* session rows */ int columns; /* session columns */ int sessionSz; /* sizeof WOLFSSL_SESSION */ } cache_header_t; /* current persistence layout is: 1) cache_header_t 2) SessionCache 3) ClientCache update WOLFSSL_CACHE_VERSION if change layout for the following PERSISTENT_SESSION_CACHE functions */ /* get how big the the session cache save buffer needs to be */ int wolfSSL_get_session_cache_memsize(void) { int sz = (int)(sizeof(SessionCache) + sizeof(cache_header_t)); #ifndef NO_CLIENT_CACHE sz += (int)(sizeof(ClientCache)); #endif return sz; } /* Persist session cache to memory */ int wolfSSL_memsave_session_cache(void* mem, int sz) { int i; cache_header_t cache_header; SessionRow* row = (SessionRow*)((byte*)mem + sizeof(cache_header)); WOLFSSL_ENTER("wolfSSL_memsave_session_cache"); if (sz < wolfSSL_get_session_cache_memsize()) { WOLFSSL_MSG("Memory buffer too small"); return BUFFER_E; } cache_header.version = WOLFSSL_CACHE_VERSION; cache_header.rows = SESSION_ROWS; cache_header.columns = SESSIONS_PER_ROW; cache_header.sessionSz = (int)sizeof(WOLFSSL_SESSION); XMEMCPY(mem, &cache_header, sizeof(cache_header)); #ifndef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_RD_LOCK(row) != 0) { WOLFSSL_MSG("Session cache mutex lock failed"); return BAD_MUTEX_E; } #endif for (i = 0; i < cache_header.rows; ++i) { #ifdef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_RD_LOCK(&SessionCache[i]) != 0) { WOLFSSL_MSG("Session row cache mutex lock failed"); return BAD_MUTEX_E; } #endif XMEMCPY(row++, &SessionCache[i], SIZEOF_SESSION_ROW); #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(row); #endif #ifndef NO_CLIENT_CACHE if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); return BAD_MUTEX_E; } XMEMCPY(row, ClientCache, sizeof(ClientCache)); wc_UnLockMutex(&clisession_mutex); #endif WOLFSSL_LEAVE("wolfSSL_memsave_session_cache", WOLFSSL_SUCCESS); return WOLFSSL_SUCCESS; } /* Restore the persistent session cache from memory */ int wolfSSL_memrestore_session_cache(const void* mem, int sz) { int i; cache_header_t cache_header; SessionRow* row = (SessionRow*)((byte*)mem + sizeof(cache_header)); WOLFSSL_ENTER("wolfSSL_memrestore_session_cache"); if (sz < wolfSSL_get_session_cache_memsize()) { WOLFSSL_MSG("Memory buffer too small"); return BUFFER_E; } XMEMCPY(&cache_header, mem, sizeof(cache_header)); if (cache_header.version != WOLFSSL_CACHE_VERSION || cache_header.rows != SESSION_ROWS || cache_header.columns != SESSIONS_PER_ROW || cache_header.sessionSz != (int)sizeof(WOLFSSL_SESSION)) { WOLFSSL_MSG("Session cache header match failed"); return CACHE_MATCH_ERROR; } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_WR_LOCK(&SessionCache[0]) != 0) { WOLFSSL_MSG("Session cache mutex lock failed"); return BAD_MUTEX_E; } #endif for (i = 0; i < cache_header.rows; ++i) { #ifdef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_WR_LOCK(&SessionCache[i]) != 0) { WOLFSSL_MSG("Session row cache mutex lock failed"); return BAD_MUTEX_E; } #endif XMEMCPY(&SessionCache[i], row++, SIZEOF_SESSION_ROW); #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[0]); #endif #ifndef NO_CLIENT_CACHE if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); return BAD_MUTEX_E; } XMEMCPY(ClientCache, row, sizeof(ClientCache)); wc_UnLockMutex(&clisession_mutex); #endif WOLFSSL_LEAVE("wolfSSL_memrestore_session_cache", WOLFSSL_SUCCESS); return WOLFSSL_SUCCESS; } #if !defined(NO_FILESYSTEM) /* Persist session cache to file */ /* doesn't use memsave because of additional memory use */ int wolfSSL_save_session_cache(const char *fname) { XFILE file; int ret; int rc = WOLFSSL_SUCCESS; int i; cache_header_t cache_header; WOLFSSL_ENTER("wolfSSL_save_session_cache"); file = XFOPEN(fname, "w+b"); if (file == XBADFILE) { WOLFSSL_MSG("Couldn't open session cache save file"); return WOLFSSL_BAD_FILE; } cache_header.version = WOLFSSL_CACHE_VERSION; cache_header.rows = SESSION_ROWS; cache_header.columns = SESSIONS_PER_ROW; cache_header.sessionSz = (int)sizeof(WOLFSSL_SESSION); /* cache header */ ret = (int)XFWRITE(&cache_header, sizeof cache_header, 1, file); if (ret != 1) { WOLFSSL_MSG("Session cache header file write failed"); XFCLOSE(file); return FWRITE_ERROR; } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_RD_LOCK(&SessionCache[0]) != 0) { WOLFSSL_MSG("Session cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } #endif /* session cache */ for (i = 0; i < cache_header.rows; ++i) { #ifdef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_RD_LOCK(&SessionCache[i]) != 0) { WOLFSSL_MSG("Session row cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } #endif ret = (int)XFWRITE(&SessionCache[i], SIZEOF_SESSION_ROW, 1, file); #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif if (ret != 1) { WOLFSSL_MSG("Session cache member file write failed"); rc = FWRITE_ERROR; break; } } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[0]); #endif #ifndef NO_CLIENT_CACHE /* client cache */ if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } ret = (int)XFWRITE(ClientCache, sizeof(ClientCache), 1, file); if (ret != 1) { WOLFSSL_MSG("Client cache member file write failed"); rc = FWRITE_ERROR; } wc_UnLockMutex(&clisession_mutex); #endif /* !NO_CLIENT_CACHE */ XFCLOSE(file); WOLFSSL_LEAVE("wolfSSL_save_session_cache", rc); return rc; } /* Restore the persistent session cache from file */ /* doesn't use memstore because of additional memory use */ int wolfSSL_restore_session_cache(const char *fname) { XFILE file; int rc = WOLFSSL_SUCCESS; int ret; int i; cache_header_t cache_header; WOLFSSL_ENTER("wolfSSL_restore_session_cache"); file = XFOPEN(fname, "rb"); if (file == XBADFILE) { WOLFSSL_MSG("Couldn't open session cache save file"); return WOLFSSL_BAD_FILE; } /* cache header */ ret = (int)XFREAD(&cache_header, sizeof(cache_header), 1, file); if (ret != 1) { WOLFSSL_MSG("Session cache header file read failed"); XFCLOSE(file); return FREAD_ERROR; } if (cache_header.version != WOLFSSL_CACHE_VERSION || cache_header.rows != SESSION_ROWS || cache_header.columns != SESSIONS_PER_ROW || cache_header.sessionSz != (int)sizeof(WOLFSSL_SESSION)) { WOLFSSL_MSG("Session cache header match failed"); XFCLOSE(file); return CACHE_MATCH_ERROR; } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_WR_LOCK(&SessionCache[0]) != 0) { WOLFSSL_MSG("Session cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } #endif /* session cache */ for (i = 0; i < cache_header.rows; ++i) { #ifdef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_WR_LOCK(&SessionCache[i]) != 0) { WOLFSSL_MSG("Session row cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } #endif ret = (int)XFREAD(&SessionCache[i], SIZEOF_SESSION_ROW, 1, file); #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif if (ret != 1) { WOLFSSL_MSG("Session cache member file read failed"); XMEMSET(SessionCache, 0, sizeof SessionCache); rc = FREAD_ERROR; break; } } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[0]); #endif #ifndef NO_CLIENT_CACHE /* client cache */ if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); XFCLOSE(file); return BAD_MUTEX_E; } ret = (int)XFREAD(ClientCache, sizeof(ClientCache), 1, file); if (ret != 1) { WOLFSSL_MSG("Client cache member file read failed"); XMEMSET(ClientCache, 0, sizeof ClientCache); rc = FREAD_ERROR; } wc_UnLockMutex(&clisession_mutex); #endif /* !NO_CLIENT_CACHE */ XFCLOSE(file); WOLFSSL_LEAVE("wolfSSL_restore_session_cache", rc); return rc; } #endif /* !NO_FILESYSTEM */ #endif /* PERSIST_SESSION_CACHE && !SESSION_CACHE_DYNAMIC_MEM */ /* on by default if built in but allow user to turn off */ WOLFSSL_ABI long wolfSSL_CTX_set_session_cache_mode(WOLFSSL_CTX* ctx, long mode) { WOLFSSL_ENTER("wolfSSL_CTX_set_session_cache_mode"); if (ctx == NULL) return WOLFSSL_FAILURE; if (mode == WOLFSSL_SESS_CACHE_OFF) { ctx->sessionCacheOff = 1; #ifdef HAVE_EXT_CACHE ctx->internalCacheOff = 1; ctx->internalCacheLookupOff = 1; #endif } if ((mode & WOLFSSL_SESS_CACHE_NO_AUTO_CLEAR) != 0) ctx->sessionCacheFlushOff = 1; #ifdef HAVE_EXT_CACHE /* WOLFSSL_SESS_CACHE_NO_INTERNAL activates both if's */ if ((mode & WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE) != 0) ctx->internalCacheOff = 1; if ((mode & WOLFSSL_SESS_CACHE_NO_INTERNAL_LOOKUP) != 0) ctx->internalCacheLookupOff = 1; #endif return WOLFSSL_SUCCESS; } #ifdef OPENSSL_EXTRA #ifdef HAVE_MAX_FRAGMENT /* return the max fragment size set when handshake was negotiated */ unsigned char wolfSSL_SESSION_get_max_fragment_length(WOLFSSL_SESSION* session) { session = ClientSessionToSession(session); if (session == NULL) { return 0; } return session->mfl; } #endif /* Get the session cache mode for CTX * * ctx WOLFSSL_CTX struct to get cache mode from * * Returns a bit mask that has the session cache mode */ long wolfSSL_CTX_get_session_cache_mode(WOLFSSL_CTX* ctx) { long m = 0; WOLFSSL_ENTER("wolfSSL_CTX_get_session_cache_mode"); if (ctx == NULL) { return m; } if (ctx->sessionCacheOff != 1) { m |= WOLFSSL_SESS_CACHE_SERVER; } if (ctx->sessionCacheFlushOff == 1) { m |= WOLFSSL_SESS_CACHE_NO_AUTO_CLEAR; } #ifdef HAVE_EXT_CACHE if (ctx->internalCacheOff == 1) { m |= WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE; } if (ctx->internalCacheLookupOff == 1) { m |= WOLFSSL_SESS_CACHE_NO_INTERNAL_LOOKUP; } #endif return m; } #endif /* OPENSSL_EXTRA */ #endif /* !NO_SESSION_CACHE */ #ifndef NO_SESSION_CACHE WOLFSSL_ABI void wolfSSL_flush_sessions(WOLFSSL_CTX* ctx, long tm) { /* static table now, no flushing needed */ (void)ctx; (void)tm; } void wolfSSL_CTX_flush_sessions(WOLFSSL_CTX* ctx, long tm) { int i, j; byte id[ID_LEN]; (void)ctx; XMEMSET(id, 0, ID_LEN); WOLFSSL_ENTER("wolfSSL_flush_sessions"); for (i = 0; i < SESSION_ROWS; ++i) { if (SESSION_ROW_WR_LOCK(&SessionCache[i]) != 0) { WOLFSSL_MSG("Session cache mutex lock failed"); return; } for (j = 0; j < SESSIONS_PER_ROW; j++) { #ifdef SESSION_CACHE_DYNAMIC_MEM WOLFSSL_SESSION* s = SessionCache[i].Sessions[j]; #else WOLFSSL_SESSION* s = &SessionCache[i].Sessions[j]; #endif if ( #ifdef SESSION_CACHE_DYNAMIC_MEM s != NULL && #endif XMEMCMP(s->sessionID, id, ID_LEN) != 0 && s->bornOn + s->timeout < (word32)tm ) { EvictSessionFromCache(s); #ifdef SESSION_CACHE_DYNAMIC_MEM XFREE(s, s->heap, DYNAMIC_TYPE_SESSION); SessionCache[i].Sessions[j] = NULL; #endif } } SESSION_ROW_UNLOCK(&SessionCache[i]); } } /* set ssl session timeout in seconds */ WOLFSSL_ABI int wolfSSL_set_timeout(WOLFSSL* ssl, unsigned int to) { if (ssl == NULL) return BAD_FUNC_ARG; if (to == 0) to = WOLFSSL_SESSION_TIMEOUT; ssl->timeout = to; return WOLFSSL_SUCCESS; } /** * Sets ctx session timeout in seconds. * The timeout value set here should be reflected in the * "session ticket lifetime hint" if this API works in the openssl compat-layer. * Therefore wolfSSL_CTX_set_TicketHint is called internally. * Arguments: * - ctx WOLFSSL_CTX object which the timeout is set to * - to timeout value in second * Returns: * WOLFSSL_SUCCESS on success, BAD_FUNC_ARG on failure. * When WOLFSSL_ERROR_CODE_OPENSSL is defined, returns previous timeout value * on success, BAD_FUNC_ARG on failure. */ WOLFSSL_ABI int wolfSSL_CTX_set_timeout(WOLFSSL_CTX* ctx, unsigned int to) { #if defined(WOLFSSL_ERROR_CODE_OPENSSL) word32 prev_timeout = 0; #endif int ret = WOLFSSL_SUCCESS; (void)ret; if (ctx == NULL) ret = BAD_FUNC_ARG; if (ret == WOLFSSL_SUCCESS) { #if defined(WOLFSSL_ERROR_CODE_OPENSSL) prev_timeout = ctx->timeout; #endif if (to == 0) { ctx->timeout = WOLFSSL_SESSION_TIMEOUT; } else { ctx->timeout = to; } } #if defined(OPENSSL_EXTRA) && defined(HAVE_SESSION_TICKET) && \ !defined(NO_WOLFSSL_SERVER) if (ret == WOLFSSL_SUCCESS) { if (to == 0) { ret = wolfSSL_CTX_set_TicketHint(ctx, SESSION_TICKET_HINT_DEFAULT); } else { ret = wolfSSL_CTX_set_TicketHint(ctx, (int)to); } } #endif /* OPENSSL_EXTRA && HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER */ #if defined(WOLFSSL_ERROR_CODE_OPENSSL) if (ret == WOLFSSL_SUCCESS) { return (int)prev_timeout; } else { return ret; } #else return ret; #endif /* WOLFSSL_ERROR_CODE_OPENSSL */ } #ifndef NO_CLIENT_CACHE /* Get Session from Client cache based on id/len, return NULL on failure */ WOLFSSL_SESSION* wolfSSL_GetSessionClient(WOLFSSL* ssl, const byte* id, int len) { WOLFSSL_SESSION* ret = NULL; word32 row; int idx; int count; int error = 0; ClientSession* clSess; WOLFSSL_ENTER("wolfSSL_GetSessionClient"); if (ssl->ctx->sessionCacheOff) { WOLFSSL_MSG("Session Cache off"); return NULL; } if (ssl->options.side == WOLFSSL_SERVER_END) return NULL; len = (int)min(SERVER_ID_LEN, (word32)len); /* Do not access ssl->ctx->get_sess_cb from here. It is using a different * set of ID's */ row = HashObject(id, (word32)len, &error) % CLIENT_SESSION_ROWS; if (error != 0) { WOLFSSL_MSG("Hash session failed"); return NULL; } if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); return NULL; } /* start from most recently used */ count = (int)min((word32)ClientCache[row].totalCount, CLIENT_SESSIONS_PER_ROW); idx = ClientCache[row].nextIdx - 1; if (idx < 0 || idx >= CLIENT_SESSIONS_PER_ROW) { /* if back to front, the previous was end */ idx = CLIENT_SESSIONS_PER_ROW - 1; } clSess = ClientCache[row].Clients; for (; count > 0; --count) { WOLFSSL_SESSION* current; SessionRow* sessRow; if (clSess[idx].serverRow >= SESSION_ROWS) { WOLFSSL_MSG("Client cache serverRow invalid"); break; } /* lock row */ sessRow = &SessionCache[clSess[idx].serverRow]; if (SESSION_ROW_RD_LOCK(sessRow) != 0) { WOLFSSL_MSG("Session cache row lock failure"); break; } #ifdef SESSION_CACHE_DYNAMIC_MEM current = sessRow->Sessions[clSess[idx].serverIdx]; #else current = &sessRow->Sessions[clSess[idx].serverIdx]; #endif if (current && XMEMCMP(current->serverID, id, len) == 0) { WOLFSSL_MSG("Found a serverid match for client"); if (LowResTimer() < (current->bornOn + current->timeout)) { WOLFSSL_MSG("Session valid"); ret = current; SESSION_ROW_UNLOCK(sessRow); break; } else { WOLFSSL_MSG("Session timed out"); /* could have more for id */ } } else { WOLFSSL_MSG("ServerID not a match from client table"); } SESSION_ROW_UNLOCK(sessRow); idx = idx > 0 ? idx - 1 : CLIENT_SESSIONS_PER_ROW - 1; } wc_UnLockMutex(&clisession_mutex); return ret; } #endif /* !NO_CLIENT_CACHE */ static int SslSessionCacheOff(const WOLFSSL* ssl, const WOLFSSL_SESSION* session) { (void)session; return ssl->options.sessionCacheOff #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_FORCE_CACHE_ON_TICKET) && session->ticketLen == 0 #endif ; } #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) /** * SessionTicketNoncePrealloc() - prealloc a buffer for ticket nonces * @output: [in] pointer to WOLFSSL_SESSION object that will soon be a * destination of a session duplication * @buf: [out] address of the preallocated buf * @len: [out] len of the preallocated buf * * prealloc a buffer that will likely suffice to contain a ticket nonce. It's * used when copying session under lock, when syscalls need to be avoided. If * output already has a dynamic buffer, it's reused. */ static int SessionTicketNoncePrealloc(byte** buf, byte* len, void *heap) { (void)heap; *buf = (byte*)XMALLOC(PREALLOC_SESSION_TICKET_NONCE_LEN, heap, DYNAMIC_TYPE_SESSION_TICK); if (*buf == NULL) { WOLFSSL_MSG("Failed to preallocate ticket nonce buffer"); *len = 0; return 1; } *len = PREALLOC_SESSION_TICKET_NONCE_LEN; return 0; } #endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 */ static int wolfSSL_DupSessionEx(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output, int avoidSysCalls, byte* ticketNonceBuf, byte* ticketNonceLen, byte* preallocUsed); void TlsSessionCacheUnlockRow(word32 row) { SessionRow* sessRow; sessRow = &SessionCache[row]; (void)sessRow; SESSION_ROW_UNLOCK(sessRow); } /* Don't use this function directly. Use TlsSessionCacheGetAndRdLock and * TlsSessionCacheGetAndWrLock to fully utilize compiler const support. */ static int TlsSessionCacheGetAndLock(const byte *id, const WOLFSSL_SESSION **sess, word32 *lockedRow, byte readOnly, byte side) { SessionRow *sessRow; const WOLFSSL_SESSION *s; word32 row; int count; int error; int idx; *sess = NULL; row = HashObject(id, ID_LEN, &error) % SESSION_ROWS; if (error != 0) return error; sessRow = &SessionCache[row]; if (readOnly) error = SESSION_ROW_RD_LOCK(sessRow); else error = SESSION_ROW_WR_LOCK(sessRow); if (error != 0) return FATAL_ERROR; /* start from most recently used */ count = (int)min((word32)sessRow->totalCount, SESSIONS_PER_ROW); idx = sessRow->nextIdx - 1; if (idx < 0 || idx >= SESSIONS_PER_ROW) { idx = SESSIONS_PER_ROW - 1; /* if back to front, the previous was end */ } for (; count > 0; --count) { #ifdef SESSION_CACHE_DYNAMIC_MEM s = sessRow->Sessions[idx]; #else s = &sessRow->Sessions[idx]; #endif if (s && XMEMCMP(s->sessionID, id, ID_LEN) == 0 && s->side == side) { *sess = s; break; } idx = idx > 0 ? idx - 1 : SESSIONS_PER_ROW - 1; } if (*sess == NULL) { SESSION_ROW_UNLOCK(sessRow); } else { *lockedRow = row; } return 0; } static int CheckSessionMatch(const WOLFSSL* ssl, const WOLFSSL_SESSION* sess) { if (ssl == NULL || sess == NULL) return 0; #ifdef OPENSSL_EXTRA if (ssl->sessionCtxSz > 0 && (ssl->sessionCtxSz != sess->sessionCtxSz || XMEMCMP(ssl->sessionCtx, sess->sessionCtx, sess->sessionCtxSz) != 0)) return 0; #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) if (IsAtLeastTLSv1_3(ssl->version) != IsAtLeastTLSv1_3(sess->version)) return 0; #endif return 1; } int TlsSessionCacheGetAndRdLock(const byte *id, const WOLFSSL_SESSION **sess, word32 *lockedRow, byte side) { return TlsSessionCacheGetAndLock(id, sess, lockedRow, 1, side); } int TlsSessionCacheGetAndWrLock(const byte *id, WOLFSSL_SESSION **sess, word32 *lockedRow, byte side) { return TlsSessionCacheGetAndLock(id, (const WOLFSSL_SESSION**)sess, lockedRow, 0, side); } int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) { const WOLFSSL_SESSION* sess = NULL; const byte* id = NULL; word32 row; int error = 0; #ifdef HAVE_SESSION_TICKET #ifndef WOLFSSL_SMALL_STACK byte tmpTicket[PREALLOC_SESSION_TICKET_LEN]; #else byte* tmpTicket = NULL; #endif #ifdef WOLFSSL_TLS13 byte *preallocNonce = NULL; byte preallocNonceLen = 0; byte preallocNonceUsed = 0; #endif /* WOLFSSL_TLS13 */ byte tmpBufSet = 0; #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) WOLFSSL_X509* peer = NULL; #endif byte bogusID[ID_LEN]; byte bogusIDSz = 0; WOLFSSL_ENTER("wolfSSL_GetSessionFromCache"); if (output == NULL) { WOLFSSL_MSG("NULL output"); return WOLFSSL_FAILURE; } if (SslSessionCacheOff(ssl, ssl->session)) return WOLFSSL_FAILURE; if (ssl->options.haveSessionId == 0 && !ssl->session->haveAltSessionID) return WOLFSSL_FAILURE; #ifdef HAVE_SESSION_TICKET if (ssl->options.side == WOLFSSL_SERVER_END && ssl->options.useTicket == 1) return WOLFSSL_FAILURE; #endif XMEMSET(bogusID, 0, sizeof(bogusID)); if (!IsAtLeastTLSv1_3(ssl->version) && ssl->arrays != NULL && !ssl->session->haveAltSessionID) id = ssl->arrays->sessionID; else if (ssl->session->haveAltSessionID) { id = ssl->session->altSessionID; /* We want to restore the bogus ID for TLS compatibility */ if (output == ssl->session) { XMEMCPY(bogusID, ssl->session->sessionID, ID_LEN); bogusIDSz = ssl->session->sessionIDSz; } } else id = ssl->session->sessionID; #ifdef HAVE_EXT_CACHE if (ssl->ctx->get_sess_cb != NULL) { int copy = 0; int found = 0; WOLFSSL_SESSION* extSess; /* Attempt to retrieve the session from the external cache. */ WOLFSSL_MSG("Calling external session cache"); extSess = ssl->ctx->get_sess_cb(ssl, (byte*)id, ID_LEN, ©); if ((extSess != NULL) && CheckSessionMatch(ssl, extSess) ) { WOLFSSL_MSG("Session found in external cache"); found = 1; error = wolfSSL_DupSession(extSess, output, 0); #ifdef HAVE_EX_DATA extSess->ownExData = 1; output->ownExData = 0; #endif /* We want to restore the bogus ID for TLS compatibility */ if (ssl->session->haveAltSessionID && output == ssl->session) { XMEMCPY(ssl->session->sessionID, bogusID, ID_LEN); ssl->session->sessionIDSz = bogusIDSz; } } /* If copy not set then free immediately */ if (extSess != NULL && !copy) wolfSSL_FreeSession(ssl->ctx, extSess); if (found) return error; WOLFSSL_MSG("Session not found in external cache"); } if (ssl->options.internalCacheLookupOff) { WOLFSSL_MSG("Internal cache lookup turned off"); return WOLFSSL_FAILURE; } #endif #ifdef HAVE_SESSION_TICKET if (output->ticket == NULL || output->ticketLenAlloc < PREALLOC_SESSION_TICKET_LEN) { #ifdef WOLFSSL_SMALL_STACK tmpTicket = (byte*)XMALLOC(PREALLOC_SESSION_TICKET_LEN, output->heap, DYNAMIC_TYPE_TMP_BUFFER); if (tmpTicket == NULL) { WOLFSSL_MSG("tmpTicket malloc failed"); return WOLFSSL_FAILURE; } #endif if (output->ticketLenAlloc) XFREE(output->ticket, output->heap, DYNAMIC_TYPE_SESSION_TICK); output->ticket = tmpTicket; /* cppcheck-suppress autoVariables */ output->ticketLenAlloc = PREALLOC_SESSION_TICKET_LEN; output->ticketLen = 0; tmpBufSet = 1; } #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (output->peer != NULL) { wolfSSL_X509_free(output->peer); output->peer = NULL; } #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (output->ticketNonce.data != output->ticketNonce.dataStatic) { XFREE(output->ticketNonce.data, output->heap, DYNAMIC_TYPE_SESSION_TICK); output->ticketNonce.data = output->ticketNonce.dataStatic; output->ticketNonce.len = 0; } error = SessionTicketNoncePrealloc(&preallocNonce, &preallocNonceLen, output->heap); if (error != 0) { if (tmpBufSet) { output->ticket = output->staticTicket; output->ticketLenAlloc = 0; } #ifdef WOLFSSL_SMALL_STACK if (tmpTicket != NULL) XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif return WOLFSSL_FAILURE; } #endif /* WOLFSSL_TLS13 && HAVE_SESSION_TICKET*/ /* init to avoid clang static analyzer false positive */ row = 0; error = TlsSessionCacheGetAndRdLock(id, &sess, &row, (byte)ssl->options.side); error = (error == 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; if (error != WOLFSSL_SUCCESS || sess == NULL) { WOLFSSL_MSG("Get Session from cache failed"); error = WOLFSSL_FAILURE; #ifdef HAVE_SESSION_TICKET if (tmpBufSet) { output->ticket = output->staticTicket; output->ticketLenAlloc = 0; } #ifdef WOLFSSL_TLS13 if (preallocNonce != NULL) { XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK); preallocNonce = NULL; } #endif /* WOLFSSL_TLS13 */ #ifdef WOLFSSL_SMALL_STACK if (tmpTicket != NULL) { XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER); tmpTicket = NULL; } #endif #endif } else { if (!CheckSessionMatch(ssl, sess)) { WOLFSSL_MSG("Invalid session: can't be used in this context"); TlsSessionCacheUnlockRow(row); error = WOLFSSL_FAILURE; } else if (LowResTimer() >= (sess->bornOn + sess->timeout)) { WOLFSSL_SESSION* wrSess = NULL; WOLFSSL_MSG("Invalid session: timed out"); sess = NULL; TlsSessionCacheUnlockRow(row); /* Attempt to get a write lock */ error = TlsSessionCacheGetAndWrLock(id, &wrSess, &row, (byte)ssl->options.side); if (error == 0 && wrSess != NULL) { EvictSessionFromCache(wrSess); TlsSessionCacheUnlockRow(row); } error = WOLFSSL_FAILURE; } } /* mollify confused cppcheck nullPointer warning. */ if (sess == NULL) error = WOLFSSL_FAILURE; if (error == WOLFSSL_SUCCESS) { #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) error = wolfSSL_DupSessionEx(sess, output, 1, preallocNonce, &preallocNonceLen, &preallocNonceUsed); #else error = wolfSSL_DupSession(sess, output, 1); #endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 */ #ifdef HAVE_EX_DATA output->ownExData = !sess->ownExData; /* Session may own ex_data */ #endif TlsSessionCacheUnlockRow(row); } /* We want to restore the bogus ID for TLS compatibility */ if (ssl->session->haveAltSessionID && output == ssl->session) { XMEMCPY(ssl->session->sessionID, bogusID, ID_LEN); ssl->session->sessionIDSz = bogusIDSz; } #ifdef HAVE_SESSION_TICKET if (tmpBufSet) { if (error == WOLFSSL_SUCCESS) { if (output->ticketLen > SESSION_TICKET_LEN) { output->ticket = (byte*)XMALLOC(output->ticketLen, output->heap, DYNAMIC_TYPE_SESSION_TICK); if (output->ticket == NULL) { error = WOLFSSL_FAILURE; output->ticket = output->staticTicket; output->ticketLenAlloc = 0; output->ticketLen = 0; } } else { output->ticket = output->staticTicket; output->ticketLenAlloc = 0; } } else { output->ticket = output->staticTicket; output->ticketLenAlloc = 0; output->ticketLen = 0; } if (error == WOLFSSL_SUCCESS) { XMEMCPY(output->ticket, tmpTicket, output->ticketLen); } } #ifdef WOLFSSL_SMALL_STACK if (tmpTicket != NULL) XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (error == WOLFSSL_SUCCESS && preallocNonceUsed) { if (preallocNonceLen < PREALLOC_SESSION_TICKET_NONCE_LEN) { /* buffer bigger than needed */ #ifndef XREALLOC output->ticketNonce.data = (byte*)XMALLOC(preallocNonceLen, output->heap, DYNAMIC_TYPE_SESSION_TICK); if (output->ticketNonce.data != NULL) XMEMCPY(output->ticketNonce.data, preallocNonce, preallocNonceLen); XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK); preallocNonce = NULL; #else output->ticketNonce.data = (byte*)XREALLOC(preallocNonce, preallocNonceLen, output->heap, DYNAMIC_TYPE_SESSION_TICK); if (output->ticketNonce.data != NULL) { /* don't free the reallocated pointer */ preallocNonce = NULL; } #endif /* !XREALLOC */ if (output->ticketNonce.data == NULL) { output->ticketNonce.data = output->ticketNonce.dataStatic; output->ticketNonce.len = 0; error = WOLFSSL_FAILURE; /* preallocNonce will be free'd after the if */ } } else { output->ticketNonce.data = preallocNonce; output->ticketNonce.len = preallocNonceLen; preallocNonce = NULL; } } if (preallocNonce != NULL) XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK); #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (peer != NULL) { wolfSSL_X509_free(peer); } #endif return error; } WOLFSSL_SESSION* wolfSSL_GetSession(WOLFSSL* ssl, byte* masterSecret, byte restoreSessionCerts) { WOLFSSL_SESSION* ret = NULL; (void)restoreSessionCerts; /* Kept for compatibility */ if (wolfSSL_GetSessionFromCache(ssl, ssl->session) == WOLFSSL_SUCCESS) { ret = ssl->session; } else { WOLFSSL_MSG("wolfSSL_GetSessionFromCache did not return a session"); } if (ret != NULL && masterSecret != NULL) XMEMCPY(masterSecret, ret->masterSecret, SECRET_LEN); return ret; } int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session) { SessionRow* sessRow = NULL; int ret = WOLFSSL_SUCCESS; session = ClientSessionToSession(session); if (ssl == NULL || session == NULL || !session->isSetup) { WOLFSSL_MSG("ssl or session NULL or not set up"); return WOLFSSL_FAILURE; } /* We need to lock the session as the first step if its in the cache */ if (session->type == WOLFSSL_SESSION_TYPE_CACHE) { if (session->cacheRow < SESSION_ROWS) { sessRow = &SessionCache[session->cacheRow]; if (SESSION_ROW_RD_LOCK(sessRow) != 0) { WOLFSSL_MSG("Session row lock failed"); return WOLFSSL_FAILURE; } } } if (ret == WOLFSSL_SUCCESS && ssl->options.side != WOLFSSL_NEITHER_END && (byte)ssl->options.side != session->side) { WOLFSSL_MSG("Setting session for wrong role"); ret = WOLFSSL_FAILURE; } if (ret == WOLFSSL_SUCCESS) { if (ssl->session == session) { WOLFSSL_MSG("ssl->session and session same"); } else if (session->type != WOLFSSL_SESSION_TYPE_CACHE) { if (wolfSSL_SESSION_up_ref(session) == WOLFSSL_SUCCESS) { wolfSSL_FreeSession(ssl->ctx, ssl->session); ssl->session = session; } else ret = WOLFSSL_FAILURE; } else { ret = wolfSSL_DupSession(session, ssl->session, 0); if (ret != WOLFSSL_SUCCESS) WOLFSSL_MSG("Session duplicate failed"); } } /* Let's copy over the altSessionID for local cache purposes */ if (ret == WOLFSSL_SUCCESS && session->haveAltSessionID && ssl->session != session) { ssl->session->haveAltSessionID = 1; XMEMCPY(ssl->session->altSessionID, session->altSessionID, ID_LEN); } if (sessRow != NULL) { SESSION_ROW_UNLOCK(sessRow); sessRow = NULL; } /* Note: the `session` variable cannot be used below, since the row is * un-locked */ if (ret != WOLFSSL_SUCCESS) return ret; #ifdef WOLFSSL_SESSION_ID_CTX /* check for application context id */ if (ssl->sessionCtxSz > 0) { if (XMEMCMP(ssl->sessionCtx, ssl->session->sessionCtx, ssl->sessionCtxSz)) { /* context id did not match! */ WOLFSSL_MSG("Session context did not match"); return WOLFSSL_FAILURE; } } #endif /* WOLFSSL_SESSION_ID_CTX */ if (LowResTimer() >= (ssl->session->bornOn + ssl->session->timeout)) { #if !defined(OPENSSL_EXTRA) || !defined(WOLFSSL_ERROR_CODE_OPENSSL) return WOLFSSL_FAILURE; /* session timed out */ #else /* defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL) */ WOLFSSL_MSG("Session is expired but return success for " "OpenSSL compatibility"); #endif } ssl->options.resuming = 1; ssl->options.haveEMS = ssl->session->haveEMS; #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) ssl->version = ssl->session->version; if (IsAtLeastTLSv1_3(ssl->version)) ssl->options.tls1_3 = 1; #endif #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) ssl->options.cipherSuite0 = ssl->session->cipherSuite0; ssl->options.cipherSuite = ssl->session->cipherSuite; #endif #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) ssl->peerVerifyRet = (unsigned long)ssl->session->peerVerifyRet; #endif return WOLFSSL_SUCCESS; } #ifdef WOLFSSL_SESSION_STATS static int get_locked_session_stats(word32* active, word32* total, word32* peak); #endif #ifndef NO_CLIENT_CACHE ClientSession* AddSessionToClientCache(int side, int row, int idx, byte* serverID, word16 idLen, const byte* sessionID, word16 useTicket) { int error = -1; word32 clientRow = 0, clientIdx = 0; ClientSession* ret = NULL; (void)useTicket; if (side == WOLFSSL_CLIENT_END && row != INVALID_SESSION_ROW && (idLen #ifdef HAVE_SESSION_TICKET || useTicket == 1 #endif || serverID != NULL )) { WOLFSSL_MSG("Trying to add client cache entry"); if (idLen) { clientRow = HashObject(serverID, idLen, &error) % CLIENT_SESSION_ROWS; } else if (serverID != NULL) { clientRow = HashObject(sessionID, ID_LEN, &error) % CLIENT_SESSION_ROWS; } else { error = -1; } if (error == 0 && wc_LockMutex(&clisession_mutex) == 0) { clientIdx = (word32)ClientCache[clientRow].nextIdx; if (clientIdx < CLIENT_SESSIONS_PER_ROW) { ClientCache[clientRow].Clients[clientIdx].serverRow = (word16)row; ClientCache[clientRow].Clients[clientIdx].serverIdx = (word16)idx; if (sessionID != NULL) { word32 sessionIDHash = HashObject(sessionID, ID_LEN, &error); if (error == 0) { ClientCache[clientRow].Clients[clientIdx].sessionIDHash = sessionIDHash; } } } else { error = -1; ClientCache[clientRow].nextIdx = 0; /* reset index as safety */ WOLFSSL_MSG("Invalid client cache index! " "Possible corrupted memory"); } if (error == 0) { WOLFSSL_MSG("Adding client cache entry"); ret = &ClientCache[clientRow].Clients[clientIdx]; if (ClientCache[clientRow].totalCount < CLIENT_SESSIONS_PER_ROW) ClientCache[clientRow].totalCount++; ClientCache[clientRow].nextIdx++; ClientCache[clientRow].nextIdx %= CLIENT_SESSIONS_PER_ROW; } wc_UnLockMutex(&clisession_mutex); } else { WOLFSSL_MSG("Hash session or lock failed"); } } else { WOLFSSL_MSG("Skipping client cache"); } return ret; } #endif /* !NO_CLIENT_CACHE */ /** * For backwards compatibility, this API needs to be used in *ALL* functions * that access the WOLFSSL_SESSION members directly. * * This API checks if the passed in session is actually a ClientSession object * and returns the matching session cache object. Otherwise just return the * input. ClientSession objects only occur in the ClientCache. They are not * allocated anywhere else. */ WOLFSSL_SESSION* ClientSessionToSession(const WOLFSSL_SESSION* session) { WOLFSSL_ENTER("ClientSessionToSession"); #ifdef NO_SESSION_CACHE_REF return (WOLFSSL_SESSION*)session; #else #ifndef NO_CLIENT_CACHE if (session == NULL) return NULL; /* Check if session points into ClientCache */ if ((byte*)session >= (byte*)ClientCache && /* Cast to byte* to make pointer arithmetic work per byte */ (byte*)session < ((byte*)ClientCache) + sizeof(ClientCache)) { ClientSession* clientSession = (ClientSession*)session; SessionRow* sessRow = NULL; WOLFSSL_SESSION* cacheSession = NULL; word32 sessionIDHash = 0; int error = 0; session = NULL; /* Default to NULL for failure case */ if (wc_LockMutex(&clisession_mutex) != 0) { WOLFSSL_MSG("Client cache mutex lock failed"); return NULL; } if (clientSession->serverRow >= SESSION_ROWS || clientSession->serverIdx >= SESSIONS_PER_ROW) { WOLFSSL_MSG("Client cache serverRow or serverIdx invalid"); error = -1; } /* Prevent memory access before clientSession->serverRow and * clientSession->serverIdx are sanitized. */ XFENCE(); if (error == 0) { /* Lock row */ sessRow = &SessionCache[clientSession->serverRow]; error = SESSION_ROW_RD_LOCK(sessRow); if (error != 0) { WOLFSSL_MSG("Session cache row lock failure"); sessRow = NULL; } } if (error == 0) { #ifdef SESSION_CACHE_DYNAMIC_MEM cacheSession = sessRow->Sessions[clientSession->serverIdx]; #else cacheSession = &sessRow->Sessions[clientSession->serverIdx]; #endif if (cacheSession && cacheSession->sessionIDSz == 0) { cacheSession = NULL; WOLFSSL_MSG("Session cache entry not set"); error = -1; } } if (error == 0) { /* Calculate the hash of the session ID */ sessionIDHash = HashObject(cacheSession->sessionID, ID_LEN, &error); } if (error == 0) { /* Check the session ID hash matches */ error = clientSession->sessionIDHash != sessionIDHash; if (error != 0) WOLFSSL_MSG("session ID hashes don't match"); } if (error == 0) { /* Hashes match */ session = cacheSession; WOLFSSL_MSG("Found session cache matching client session object"); } if (sessRow != NULL) { SESSION_ROW_UNLOCK(sessRow); } wc_UnLockMutex(&clisession_mutex); return (WOLFSSL_SESSION*)session; } else { /* Plain WOLFSSL_SESSION object */ return (WOLFSSL_SESSION*)session; } #else return (WOLFSSL_SESSION*)session; #endif #endif } int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession, const byte* id, byte idSz, int* sessionIndex, int side, word16 useTicket, ClientSession** clientCacheEntry) { WOLFSSL_SESSION* cacheSession = NULL; SessionRow* sessRow = NULL; word32 idx = 0; #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) WOLFSSL_X509* cachePeer = NULL; WOLFSSL_X509* addPeer = NULL; #endif #ifdef HAVE_SESSION_TICKET byte* cacheTicBuff = NULL; byte ticBuffUsed = 0; byte* ticBuff = NULL; int ticLen = 0; #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) byte *preallocNonce = NULL; byte preallocNonceLen = 0; byte preallocNonceUsed = 0; byte *toFree = NULL; #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC */ #endif /* HAVE_SESSION_TICKET */ int ret = 0; int row; int i; int overwrite = 0; (void)ctx; (void)sessionIndex; (void)useTicket; (void)clientCacheEntry; WOLFSSL_ENTER("AddSessionToCache"); if (idSz == 0) { WOLFSSL_MSG("AddSessionToCache idSz == 0"); return BAD_FUNC_ARG; } addSession = ClientSessionToSession(addSession); if (addSession == NULL) { WOLFSSL_MSG("AddSessionToCache is NULL"); return MEMORY_E; } #ifdef HAVE_SESSION_TICKET ticLen = addSession->ticketLen; /* Alloc Memory here to avoid syscalls during lock */ if (ticLen > SESSION_TICKET_LEN) { ticBuff = (byte*)XMALLOC(ticLen, NULL, DYNAMIC_TYPE_SESSION_TICK); if (ticBuff == NULL) { return MEMORY_E; } } #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (addSession->ticketNonce.data != addSession->ticketNonce.dataStatic) { /* use the AddSession->heap even if the buffer maybe saved in * CachedSession objects. CachedSession heap and AddSession heap should * be the same */ preallocNonce = (byte*)XMALLOC(addSession->ticketNonce.len, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); if (preallocNonce == NULL) { if (ticBuff != NULL) XFREE(ticBuff, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); return MEMORY_E; } preallocNonceLen = addSession->ticketNonce.len; } #endif /* WOLFSSL_TLS13 && WOLFSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3) */ #endif /* HAVE_SESSION_TICKET */ /* Find a position for the new session in cache and use that */ /* Use the session object in the cache for external cache if required */ row = (int)(HashObject(id, ID_LEN, &ret) % SESSION_ROWS); if (ret != 0) { WOLFSSL_MSG("Hash session failed"); #ifdef HAVE_SESSION_TICKET XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); #endif #endif return ret; } sessRow = &SessionCache[row]; if (SESSION_ROW_WR_LOCK(sessRow) != 0) { #ifdef HAVE_SESSION_TICKET XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); #endif #endif WOLFSSL_MSG("Session row lock failed"); return BAD_MUTEX_E; } for (i = 0; i < SESSIONS_PER_ROW && i < sessRow->totalCount; i++) { #ifdef SESSION_CACHE_DYNAMIC_MEM cacheSession = sessRow->Sessions[i]; #else cacheSession = &sessRow->Sessions[i]; #endif if (cacheSession && XMEMCMP(id, cacheSession->sessionID, ID_LEN) == 0 && cacheSession->side == side) { WOLFSSL_MSG("Session already exists. Overwriting."); overwrite = 1; idx = (word32)i; break; } } if (!overwrite) idx = (word32)sessRow->nextIdx; #ifdef SESSION_INDEX if (sessionIndex != NULL) *sessionIndex = (row << SESSIDX_ROW_SHIFT) | idx; #endif #ifdef SESSION_CACHE_DYNAMIC_MEM cacheSession = sessRow->Sessions[idx]; if (cacheSession == NULL) { cacheSession = (WOLFSSL_SESSION*) XMALLOC(sizeof(WOLFSSL_SESSION), sessRow->heap, DYNAMIC_TYPE_SESSION); if (cacheSession == NULL) { #ifdef HAVE_SESSION_TICKET XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); #endif #endif SESSION_ROW_UNLOCK(sessRow); return MEMORY_E; } XMEMSET(cacheSession, 0, sizeof(WOLFSSL_SESSION)); sessRow->Sessions[idx] = cacheSession; } #else cacheSession = &sessRow->Sessions[idx]; #endif #ifdef HAVE_EX_DATA if (overwrite) { /* Figure out who owns the ex_data */ if (cacheSession->ownExData) { /* Prioritize cacheSession copy */ XMEMCPY(&addSession->ex_data, &cacheSession->ex_data, sizeof(WOLFSSL_CRYPTO_EX_DATA)); } /* else will be copied in wolfSSL_DupSession call */ } else if (cacheSession->ownExData) { crypto_ex_cb_free_data(cacheSession, crypto_ex_cb_ctx_session, &cacheSession->ex_data); cacheSession->ownExData = 0; } #endif if (!overwrite) EvictSessionFromCache(cacheSession); cacheSession->type = WOLFSSL_SESSION_TYPE_CACHE; cacheSession->cacheRow = row; #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) /* Save the peer field to free after unlocking the row */ if (cacheSession->peer != NULL) cachePeer = cacheSession->peer; cacheSession->peer = NULL; #endif #ifdef HAVE_SESSION_TICKET /* If we can reuse the existing buffer in cacheSession then we won't touch * ticBuff at all making it a very cheap malloc/free. The page on a modern * OS will most likely not even be allocated to the process. */ if (ticBuff != NULL && cacheSession->ticketLenAlloc < ticLen) { /* Save pointer only if separately allocated */ if (cacheSession->ticket != cacheSession->staticTicket) cacheTicBuff = cacheSession->ticket; ticBuffUsed = 1; cacheSession->ticket = ticBuff; cacheSession->ticketLenAlloc = (word16) ticLen; } #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) /* cache entry never used */ if (cacheSession->ticketNonce.data == NULL) cacheSession->ticketNonce.data = cacheSession->ticketNonce.dataStatic; if (cacheSession->ticketNonce.data != cacheSession->ticketNonce.dataStatic) { toFree = cacheSession->ticketNonce.data; cacheSession->ticketNonce.data = cacheSession->ticketNonce.dataStatic; cacheSession->ticketNonce.len = 0; } #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif #ifdef SESSION_CERTS if (overwrite && addSession->chain.count == 0 && cacheSession->chain.count > 0) { /* Copy in the certs from the session */ addSession->chain.count = cacheSession->chain.count; XMEMCPY(addSession->chain.certs, cacheSession->chain.certs, sizeof(x509_buffer) * cacheSession->chain.count); } #endif /* SESSION_CERTS */ #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) /* Don't copy the peer cert into cache */ addPeer = addSession->peer; addSession->peer = NULL; #endif cacheSession->heap = NULL; /* Copy data into the cache object */ #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) ret = wolfSSL_DupSessionEx(addSession, cacheSession, 1, preallocNonce, &preallocNonceLen, &preallocNonceUsed) == WOLFSSL_FAILURE; #else ret = wolfSSL_DupSession(addSession, cacheSession, 1) == WOLFSSL_FAILURE; #endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) addSession->peer = addPeer; #endif if (ret == 0) { if (!overwrite) { /* Increment the totalCount and the nextIdx */ if (sessRow->totalCount < SESSIONS_PER_ROW) sessRow->totalCount++; sessRow->nextIdx = (sessRow->nextIdx + 1) % SESSIONS_PER_ROW; } if (id != addSession->sessionID) { /* ssl->session->sessionID may contain the bogus ID or we want the * ID from the arrays object */ XMEMCPY(cacheSession->sessionID, id, ID_LEN); cacheSession->sessionIDSz = ID_LEN; } #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) if (ctx->rem_sess_cb != NULL) cacheSession->rem_sess_cb = ctx->rem_sess_cb; #endif #ifdef HAVE_EX_DATA /* The session in cache now owns the ex_data */ addSession->ownExData = 0; cacheSession->ownExData = 1; #endif #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (preallocNonce != NULL && preallocNonceUsed) { cacheSession->ticketNonce.data = preallocNonce; cacheSession->ticketNonce.len = preallocNonceLen; preallocNonce = NULL; preallocNonceLen = 0; } #endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC * && FIPS_VERSION_GE(5,3)*/ } #ifdef HAVE_SESSION_TICKET else if (ticBuffUsed) { /* Error occurred. Need to clean up the ticket buffer. */ cacheSession->ticket = cacheSession->staticTicket; cacheSession->ticketLenAlloc = 0; cacheSession->ticketLen = 0; } #endif SESSION_ROW_UNLOCK(sessRow); cacheSession = NULL; /* Can't access after unlocked */ #ifndef NO_CLIENT_CACHE if (ret == 0 && clientCacheEntry != NULL) { ClientSession* clientCache = AddSessionToClientCache(side, row, (int)idx, addSession->serverID, addSession->idLen, id, useTicket); if (clientCache != NULL) *clientCacheEntry = clientCache; } #endif #ifdef HAVE_SESSION_TICKET if (ticBuff != NULL && !ticBuffUsed) XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK); XFREE(cacheTicBuff, NULL, DYNAMIC_TYPE_SESSION_TICK); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); XFREE(toFree, addSession->heap, DYNAMIC_TYPE_SESSION_TICK); #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (cachePeer != NULL) { wolfSSL_X509_free(cachePeer); cachePeer = NULL; /* Make sure not use after this point */ } #endif return ret; } void AddSession(WOLFSSL* ssl) { int error = 0; const byte* id = NULL; byte idSz = 0; WOLFSSL_SESSION* session = ssl->session; (void)error; WOLFSSL_ENTER("AddSession"); if (SslSessionCacheOff(ssl, session)) { WOLFSSL_MSG("Cache off"); return; } if (session->haveAltSessionID) { id = session->altSessionID; idSz = ID_LEN; } else { id = session->sessionID; idSz = session->sessionIDSz; } /* Do this only for the client because if the server doesn't have an ID at * this point, it won't on resumption. */ if (idSz == 0 && ssl->options.side == WOLFSSL_CLIENT_END) { WC_RNG* rng = NULL; if (ssl->rng != NULL) rng = ssl->rng; #if defined(HAVE_GLOBAL_RNG) && defined(OPENSSL_EXTRA) else if (initGlobalRNG == 1 || wolfSSL_RAND_Init() == WOLFSSL_SUCCESS) { rng = &globalRNG; } #endif if (wc_RNG_GenerateBlock(rng, ssl->session->altSessionID, ID_LEN) != 0) return; ssl->session->haveAltSessionID = 1; id = ssl->session->altSessionID; idSz = ID_LEN; } #ifdef HAVE_EXT_CACHE if (!ssl->options.internalCacheOff) #endif { /* Try to add the session to internal cache or external cache if a new_sess_cb is set. Its ok if we don't succeed. */ (void)AddSessionToCache(ssl->ctx, session, id, idSz, #ifdef SESSION_INDEX &ssl->sessionIndex, #else NULL, #endif ssl->options.side, #ifdef HAVE_SESSION_TICKET ssl->options.useTicket, #else 0, #endif #ifdef NO_SESSION_CACHE_REF NULL #else (ssl->options.side == WOLFSSL_CLIENT_END) ? &ssl->clientSession : NULL #endif ); } #ifdef HAVE_EXT_CACHE if (error == 0 && ssl->ctx->new_sess_cb != NULL) { int cbRet = 0; wolfSSL_SESSION_up_ref(session); cbRet = ssl->ctx->new_sess_cb(ssl, session); if (cbRet == 0) wolfSSL_FreeSession(ssl->ctx, session); } #endif #if defined(WOLFSSL_SESSION_STATS) && defined(WOLFSSL_PEAK_SESSIONS) if (error == 0) { word32 active = 0; error = get_locked_session_stats(&active, NULL, NULL); if (error == WOLFSSL_SUCCESS) { error = 0; /* back to this function ok */ if (PeakSessions < active) { PeakSessions = active; } } } #endif /* WOLFSSL_SESSION_STATS && WOLFSSL_PEAK_SESSIONS */ (void)error; } #ifdef SESSION_INDEX int wolfSSL_GetSessionIndex(WOLFSSL* ssl) { WOLFSSL_ENTER("wolfSSL_GetSessionIndex"); WOLFSSL_LEAVE("wolfSSL_GetSessionIndex", ssl->sessionIndex); return ssl->sessionIndex; } int wolfSSL_GetSessionAtIndex(int idx, WOLFSSL_SESSION* session) { int row, col, result = WOLFSSL_FAILURE; SessionRow* sessRow; WOLFSSL_SESSION* cacheSession; WOLFSSL_ENTER("wolfSSL_GetSessionAtIndex"); session = ClientSessionToSession(session); row = idx >> SESSIDX_ROW_SHIFT; col = idx & SESSIDX_IDX_MASK; if (session == NULL || row < 0 || row >= SESSION_ROWS || col >= SESSIONS_PER_ROW) { return WOLFSSL_FAILURE; } sessRow = &SessionCache[row]; if (SESSION_ROW_RD_LOCK(sessRow) != 0) { return BAD_MUTEX_E; } #ifdef SESSION_CACHE_DYNAMIC_MEM cacheSession = sessRow->Sessions[col]; #else cacheSession = &sessRow->Sessions[col]; #endif if (cacheSession) { XMEMCPY(session, cacheSession, sizeof(WOLFSSL_SESSION)); result = WOLFSSL_SUCCESS; } else { result = WOLFSSL_FAILURE; } SESSION_ROW_UNLOCK(sessRow); WOLFSSL_LEAVE("wolfSSL_GetSessionAtIndex", result); return result; } #endif /* SESSION_INDEX */ #if defined(SESSION_CERTS) WOLFSSL_X509_CHAIN* wolfSSL_SESSION_get_peer_chain(WOLFSSL_SESSION* session) { WOLFSSL_X509_CHAIN* chain = NULL; WOLFSSL_ENTER("wolfSSL_SESSION_get_peer_chain"); session = ClientSessionToSession(session); if (session) chain = &session->chain; WOLFSSL_LEAVE("wolfSSL_SESSION_get_peer_chain", chain ? 1 : 0); return chain; } #ifdef OPENSSL_EXTRA /* gets the peer certificate associated with the session passed in * returns null on failure, the caller should not free the returned pointer */ WOLFSSL_X509* wolfSSL_SESSION_get0_peer(WOLFSSL_SESSION* session) { WOLFSSL_ENTER("wolfSSL_SESSION_get_peer_chain"); session = ClientSessionToSession(session); if (session) { int count; count = wolfSSL_get_chain_count(&session->chain); if (count < 1 || count >= MAX_CHAIN_DEPTH) { WOLFSSL_MSG("bad count found"); return NULL; } if (session->peer == NULL) { session->peer = wolfSSL_get_chain_X509(&session->chain, 0); } return session->peer; } WOLFSSL_MSG("No session passed in"); return NULL; } #endif /* OPENSSL_EXTRA */ #endif /* SESSION_INDEX && SESSION_CERTS */ #ifdef WOLFSSL_SESSION_STATS static int get_locked_session_stats(word32* active, word32* total, word32* peak) { int result = WOLFSSL_SUCCESS; int i; int count; int idx; word32 now = 0; word32 seen = 0; word32 ticks = LowResTimer(); WOLFSSL_ENTER("get_locked_session_stats"); #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_RD_LOCK(&SessionCache[0]); #endif for (i = 0; i < SESSION_ROWS; i++) { SessionRow* row = &SessionCache[i]; #ifdef ENABLE_SESSION_CACHE_ROW_LOCK if (SESSION_ROW_RD_LOCK(row) != 0) { WOLFSSL_MSG("Session row cache mutex lock failed"); return BAD_MUTEX_E; } #endif seen += row->totalCount; if (active == NULL) { SESSION_ROW_UNLOCK(row); continue; } count = min((word32)row->totalCount, SESSIONS_PER_ROW); idx = row->nextIdx - 1; if (idx < 0 || idx >= SESSIONS_PER_ROW) { idx = SESSIONS_PER_ROW - 1; /* if back to front previous was end */ } for (; count > 0; --count) { /* if not expired then good */ #ifdef SESSION_CACHE_DYNAMIC_MEM if (row->Sessions[idx] && ticks < (row->Sessions[idx]->bornOn + row->Sessions[idx]->timeout) ) #else if (ticks < (row->Sessions[idx].bornOn + row->Sessions[idx].timeout) ) #endif { now++; } idx = idx > 0 ? idx - 1 : SESSIONS_PER_ROW - 1; } #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(row); #endif } #ifndef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[0]); #endif if (active) { *active = now; } if (total) { *total = seen; } #ifdef WOLFSSL_PEAK_SESSIONS if (peak) { *peak = PeakSessions; } #else (void)peak; #endif WOLFSSL_LEAVE("get_locked_session_stats", result); return result; } /* return WOLFSSL_SUCCESS on ok */ int wolfSSL_get_session_stats(word32* active, word32* total, word32* peak, word32* maxSessions) { int result = WOLFSSL_SUCCESS; WOLFSSL_ENTER("wolfSSL_get_session_stats"); if (maxSessions) { *maxSessions = SESSIONS_PER_ROW * SESSION_ROWS; if (active == NULL && total == NULL && peak == NULL) return result; /* we're done */ } /* user must provide at least one query value */ if (active == NULL && total == NULL && peak == NULL) { return BAD_FUNC_ARG; } result = get_locked_session_stats(active, total, peak); WOLFSSL_LEAVE("wolfSSL_get_session_stats", result); return result; } #endif /* WOLFSSL_SESSION_STATS */ #ifdef PRINT_SESSION_STATS /* WOLFSSL_SUCCESS on ok */ int wolfSSL_PrintSessionStats(void) { word32 totalSessionsSeen = 0; word32 totalSessionsNow = 0; word32 peak = 0; word32 maxSessions = 0; int i; int ret; double E; /* expected freq */ double chiSquare = 0; ret = wolfSSL_get_session_stats(&totalSessionsNow, &totalSessionsSeen, &peak, &maxSessions); if (ret != WOLFSSL_SUCCESS) return ret; printf("Total Sessions Seen = %u\n", totalSessionsSeen); printf("Total Sessions Now = %u\n", totalSessionsNow); #ifdef WOLFSSL_PEAK_SESSIONS printf("Peak Sessions = %u\n", peak); #endif printf("Max Sessions = %u\n", maxSessions); E = (double)totalSessionsSeen / SESSION_ROWS; for (i = 0; i < SESSION_ROWS; i++) { double diff = SessionCache[i].totalCount - E; diff *= diff; /* square */ diff /= E; /* normalize */ chiSquare += diff; } printf(" chi-square = %5.1f, d.f. = %d\n", chiSquare, SESSION_ROWS - 1); #if (SESSION_ROWS == 11) printf(" .05 p value = 18.3, chi-square should be less\n"); #elif (SESSION_ROWS == 211) printf(".05 p value = 244.8, chi-square should be less\n"); #elif (SESSION_ROWS == 5981) printf(".05 p value = 6161.0, chi-square should be less\n"); #elif (SESSION_ROWS == 3) printf(".05 p value = 6.0, chi-square should be less\n"); #elif (SESSION_ROWS == 2861) printf(".05 p value = 2985.5, chi-square should be less\n"); #endif printf("\n"); return ret; } #endif /* SESSION_STATS */ #else /* NO_SESSION_CACHE */ WOLFSSL_SESSION* ClientSessionToSession(const WOLFSSL_SESSION* session) { return (WOLFSSL_SESSION*)session; } /* No session cache version */ WOLFSSL_SESSION* wolfSSL_GetSession(WOLFSSL* ssl, byte* masterSecret, byte restoreSessionCerts) { (void)ssl; (void)masterSecret; (void)restoreSessionCerts; return NULL; } #endif /* NO_SESSION_CACHE */ #ifdef OPENSSL_EXTRA /* returns previous set cache size which stays constant */ long wolfSSL_CTX_sess_set_cache_size(WOLFSSL_CTX* ctx, long sz) { /* cache size fixed at compile time in wolfSSL */ (void)ctx; (void)sz; WOLFSSL_MSG("session cache is set at compile time"); #ifndef NO_SESSION_CACHE return (long)(SESSIONS_PER_ROW * SESSION_ROWS); #else return 0; #endif } long wolfSSL_CTX_sess_get_cache_size(WOLFSSL_CTX* ctx) { (void)ctx; #ifndef NO_SESSION_CACHE return (long)(SESSIONS_PER_ROW * SESSION_ROWS); #else return 0; #endif } #endif #ifndef NO_SESSION_CACHE int wolfSSL_CTX_add_session(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session) { int error = 0; const byte* id = NULL; byte idSz = 0; WOLFSSL_ENTER("wolfSSL_CTX_add_session"); session = ClientSessionToSession(session); if (session == NULL) return WOLFSSL_FAILURE; /* Session cache is global */ (void)ctx; if (session->haveAltSessionID) { id = session->altSessionID; idSz = ID_LEN; } else { id = session->sessionID; idSz = session->sessionIDSz; } error = AddSessionToCache(ctx, session, id, idSz, NULL, session->side, #ifdef HAVE_SESSION_TICKET session->ticketLen > 0, #else 0, #endif NULL); return error == 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } #endif #if !defined(NO_SESSION_CACHE) && (defined(OPENSSL_EXTRA) || \ defined(HAVE_EXT_CACHE)) /* stunnel 4.28 needs * * Callback that is called if a session tries to resume but could not find * the session to resume it. */ void wolfSSL_CTX_sess_set_get_cb(WOLFSSL_CTX* ctx, WOLFSSL_SESSION*(*f)(WOLFSSL*, const unsigned char*, int, int*)) { if (ctx == NULL) return; #ifdef HAVE_EXT_CACHE ctx->get_sess_cb = f; #else (void)f; #endif } void wolfSSL_CTX_sess_set_new_cb(WOLFSSL_CTX* ctx, int (*f)(WOLFSSL*, WOLFSSL_SESSION*)) { if (ctx == NULL) return; #ifdef HAVE_EXT_CACHE ctx->new_sess_cb = f; #else (void)f; #endif } void wolfSSL_CTX_sess_set_remove_cb(WOLFSSL_CTX* ctx, void (*f)(WOLFSSL_CTX*, WOLFSSL_SESSION*)) { if (ctx == NULL) return; #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) ctx->rem_sess_cb = f; #else (void)f; #endif } /* * * Note: It is expected that the importing and exporting function have been * built with the same settings. For example if session tickets was * enabled with the wolfSSL library exporting a session then it is * expected to be turned on with the wolfSSL library importing the * session. */ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) { int size = 0; #ifdef HAVE_EXT_CACHE int idx = 0; #ifdef SESSION_CERTS int i; #endif WOLFSSL_ENTER("wolfSSL_i2d_SSL_SESSION"); sess = ClientSessionToSession(sess); if (sess == NULL) { return BAD_FUNC_ARG; } /* side | bornOn | timeout | sessionID len | sessionID | masterSecret | * haveEMS */ size += OPAQUE8_LEN + OPAQUE32_LEN + OPAQUE32_LEN + OPAQUE8_LEN + sess->sessionIDSz + SECRET_LEN + OPAQUE8_LEN; /* altSessionID */ size += OPAQUE8_LEN + (sess->haveAltSessionID ? ID_LEN : 0); #ifdef SESSION_CERTS /* Peer chain */ size += OPAQUE8_LEN; for (i = 0; i < sess->chain.count; i++) size += OPAQUE16_LEN + sess->chain.certs[i].length; #endif #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) /* Protocol version */ size += OPAQUE16_LEN; #endif #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) /* cipher suite */ size += OPAQUE16_LEN; #endif #ifndef NO_CLIENT_CACHE /* ServerID len | ServerID */ size += OPAQUE16_LEN + sess->idLen; #endif #ifdef WOLFSSL_SESSION_ID_CTX /* session context ID len | session context ID */ size += OPAQUE8_LEN + sess->sessionCtxSz; #endif #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) /* peerVerifyRet */ size += OPAQUE8_LEN; #endif #ifdef WOLFSSL_TLS13 /* namedGroup */ size += OPAQUE16_LEN; #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #ifdef WOLFSSL_TLS13 #ifdef WOLFSSL_32BIT_MILLI_TIME /* ticketSeen | ticketAdd */ size += OPAQUE32_LEN + OPAQUE32_LEN; #else /* ticketSeen Hi 32 bits | ticketSeen Lo 32 bits | ticketAdd */ size += OPAQUE32_LEN + OPAQUE32_LEN + OPAQUE32_LEN; #endif /* ticketNonce */ size += OPAQUE8_LEN + sess->ticketNonce.len; #endif #ifdef WOLFSSL_EARLY_DATA size += OPAQUE32_LEN; #endif #endif #ifdef HAVE_SESSION_TICKET /* ticket len | ticket */ size += OPAQUE16_LEN + sess->ticketLen; #endif if (p != NULL) { unsigned char *data; if (*p == NULL) *p = (unsigned char*)XMALLOC(size, NULL, DYNAMIC_TYPE_OPENSSL); if (*p == NULL) return 0; data = *p; data[idx++] = sess->side; c32toa(sess->bornOn, data + idx); idx += OPAQUE32_LEN; c32toa(sess->timeout, data + idx); idx += OPAQUE32_LEN; data[idx++] = sess->sessionIDSz; XMEMCPY(data + idx, sess->sessionID, sess->sessionIDSz); idx += sess->sessionIDSz; XMEMCPY(data + idx, sess->masterSecret, SECRET_LEN); idx += SECRET_LEN; data[idx++] = (byte)sess->haveEMS; data[idx++] = sess->haveAltSessionID ? ID_LEN : 0; if (sess->haveAltSessionID) { XMEMCPY(data + idx, sess->altSessionID, ID_LEN); idx += ID_LEN; } #ifdef SESSION_CERTS data[idx++] = (byte)sess->chain.count; for (i = 0; i < sess->chain.count; i++) { c16toa((word16)sess->chain.certs[i].length, data + idx); idx += OPAQUE16_LEN; XMEMCPY(data + idx, sess->chain.certs[i].buffer, sess->chain.certs[i].length); idx += sess->chain.certs[i].length; } #endif #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) data[idx++] = sess->version.major; data[idx++] = sess->version.minor; #endif #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) data[idx++] = sess->cipherSuite0; data[idx++] = sess->cipherSuite; #endif #ifndef NO_CLIENT_CACHE c16toa(sess->idLen, data + idx); idx += OPAQUE16_LEN; XMEMCPY(data + idx, sess->serverID, sess->idLen); idx += sess->idLen; #endif #ifdef WOLFSSL_SESSION_ID_CTX data[idx++] = sess->sessionCtxSz; XMEMCPY(data + idx, sess->sessionCtx, sess->sessionCtxSz); idx += sess->sessionCtxSz; #endif #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) data[idx++] = sess->peerVerifyRet; #endif #ifdef WOLFSSL_TLS13 c16toa(sess->namedGroup, data + idx); idx += OPAQUE16_LEN; #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #ifdef WOLFSSL_TLS13 #ifdef WOLFSSL_32BIT_MILLI_TIME c32toa(sess->ticketSeen, data + idx); idx += OPAQUE32_LEN; #else c32toa((word32)(sess->ticketSeen >> 32), data + idx); idx += OPAQUE32_LEN; c32toa((word32)sess->ticketSeen, data + idx); idx += OPAQUE32_LEN; #endif c32toa(sess->ticketAdd, data + idx); idx += OPAQUE32_LEN; data[idx++] = sess->ticketNonce.len; XMEMCPY(data + idx, sess->ticketNonce.data, sess->ticketNonce.len); idx += sess->ticketNonce.len; #endif #ifdef WOLFSSL_EARLY_DATA c32toa(sess->maxEarlyDataSz, data + idx); idx += OPAQUE32_LEN; #endif #endif #ifdef HAVE_SESSION_TICKET c16toa(sess->ticketLen, data + idx); idx += OPAQUE16_LEN; XMEMCPY(data + idx, sess->ticket, sess->ticketLen); idx += sess->ticketLen; #endif } #endif (void)sess; (void)p; #ifdef HAVE_EXT_CACHE (void)idx; #endif return size; } /* TODO: no function to free new session. * * Note: It is expected that the importing and exporting function have been * built with the same settings. For example if session tickets was * enabled with the wolfSSL library exporting a session then it is * expected to be turned on with the wolfSSL library importing the * session. */ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, const unsigned char** p, long i) { WOLFSSL_SESSION* s = NULL; int ret = 0; #if defined(HAVE_EXT_CACHE) int idx = 0; byte* data; #ifdef SESSION_CERTS int j; word16 length; #endif #endif /* HAVE_EXT_CACHE */ (void)p; (void)i; (void)ret; (void)sess; #ifdef HAVE_EXT_CACHE if (p == NULL || *p == NULL) return NULL; s = wolfSSL_SESSION_new(); if (s == NULL) return NULL; idx = 0; data = (byte*)*p; /* side | bornOn | timeout | sessionID len */ if (i < OPAQUE8_LEN + OPAQUE32_LEN + OPAQUE32_LEN + OPAQUE8_LEN) { ret = BUFFER_ERROR; goto end; } s->side = data[idx++]; ato32(data + idx, &s->bornOn); idx += OPAQUE32_LEN; ato32(data + idx, &s->timeout); idx += OPAQUE32_LEN; s->sessionIDSz = data[idx++]; /* sessionID | secret | haveEMS | haveAltSessionID */ if (i - idx < s->sessionIDSz + SECRET_LEN + OPAQUE8_LEN + OPAQUE8_LEN) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->sessionID, data + idx, s->sessionIDSz); idx += s->sessionIDSz; XMEMCPY(s->masterSecret, data + idx, SECRET_LEN); idx += SECRET_LEN; s->haveEMS = data[idx++]; if (data[idx] != ID_LEN && data[idx] != 0) { ret = BUFFER_ERROR; goto end; } s->haveAltSessionID = data[idx++] == ID_LEN; /* altSessionID */ if (s->haveAltSessionID) { if (i - idx < ID_LEN) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->altSessionID, data + idx, ID_LEN); idx += ID_LEN; } #ifdef SESSION_CERTS /* Certificate chain */ if (i - idx == 0) { ret = BUFFER_ERROR; goto end; } s->chain.count = data[idx++]; for (j = 0; j < s->chain.count; j++) { if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } ato16(data + idx, &length); idx += OPAQUE16_LEN; s->chain.certs[j].length = length; if (i - idx < length) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->chain.certs[j].buffer, data + idx, length); idx += length; } #endif #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) /* Protocol Version */ if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } s->version.major = data[idx++]; s->version.minor = data[idx++]; #endif #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) /* Cipher suite */ if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } s->cipherSuite0 = data[idx++]; s->cipherSuite = data[idx++]; #endif #ifndef NO_CLIENT_CACHE /* ServerID len */ if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } ato16(data + idx, &s->idLen); idx += OPAQUE16_LEN; /* ServerID */ if (i - idx < s->idLen) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->serverID, data + idx, s->idLen); idx += s->idLen; #endif #ifdef WOLFSSL_SESSION_ID_CTX /* byte for length of session context ID */ if (i - idx < OPAQUE8_LEN) { ret = BUFFER_ERROR; goto end; } s->sessionCtxSz = data[idx++]; /* app session context ID */ if (i - idx < s->sessionCtxSz) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->sessionCtx, data + idx, s->sessionCtxSz); idx += s->sessionCtxSz; #endif #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) /* byte for peerVerifyRet */ if (i - idx < OPAQUE8_LEN) { ret = BUFFER_ERROR; goto end; } s->peerVerifyRet = data[idx++]; #endif #ifdef WOLFSSL_TLS13 if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } ato16(data + idx, &s->namedGroup); idx += OPAQUE16_LEN; #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #ifdef WOLFSSL_TLS13 if (i - idx < (OPAQUE32_LEN * 2)) { ret = BUFFER_ERROR; goto end; } #ifdef WOLFSSL_32BIT_MILLI_TIME ato32(data + idx, &s->ticketSeen); idx += OPAQUE32_LEN; #else { word32 seenHi, seenLo; ato32(data + idx, &seenHi); idx += OPAQUE32_LEN; ato32(data + idx, &seenLo); idx += OPAQUE32_LEN; s->ticketSeen = ((sword64)seenHi << 32) + seenLo; } #endif ato32(data + idx, &s->ticketAdd); idx += OPAQUE32_LEN; if (i - idx < OPAQUE8_LEN) { ret = BUFFER_ERROR; goto end; } s->ticketNonce.len = data[idx++]; if (i - idx < s->ticketNonce.len) { ret = BUFFER_ERROR; goto end; } #if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) ret = SessionTicketNoncePopulate(s, data + idx, s->ticketNonce.len); if (ret != 0) goto end; #else if (s->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->ticketNonce.data, data + idx, s->ticketNonce.len); #endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */ idx += s->ticketNonce.len; #endif #ifdef WOLFSSL_EARLY_DATA if (i - idx < OPAQUE32_LEN) { ret = BUFFER_ERROR; goto end; } ato32(data + idx, &s->maxEarlyDataSz); idx += OPAQUE32_LEN; #endif #endif #ifdef HAVE_SESSION_TICKET /* ticket len */ if (i - idx < OPAQUE16_LEN) { ret = BUFFER_ERROR; goto end; } ato16(data + idx, &s->ticketLen); idx += OPAQUE16_LEN; /* Dispose of ol dynamic ticket and ensure space for new ticket. */ if (s->ticketLenAlloc > 0) { XFREE(s->ticket, NULL, DYNAMIC_TYPE_SESSION_TICK); } if (s->ticketLen <= SESSION_TICKET_LEN) s->ticket = s->staticTicket; else { s->ticket = (byte*)XMALLOC(s->ticketLen, NULL, DYNAMIC_TYPE_SESSION_TICK); if (s->ticket == NULL) { ret = MEMORY_ERROR; goto end; } s->ticketLenAlloc = (word16)s->ticketLen; } /* ticket */ if (i - idx < s->ticketLen) { ret = BUFFER_ERROR; goto end; } XMEMCPY(s->ticket, data + idx, s->ticketLen); idx += s->ticketLen; #endif (void)idx; if (sess != NULL) { *sess = s; } s->isSetup = 1; *p += idx; end: if (ret != 0 && (sess == NULL || *sess != s)) { wolfSSL_FreeSession(NULL, s); s = NULL; } #endif /* HAVE_EXT_CACHE */ return s; } /* Check if there is a session ticket associated with this WOLFSSL_SESSION. * * sess - pointer to WOLFSSL_SESSION struct * * Returns 1 if has session ticket, otherwise 0 */ int wolfSSL_SESSION_has_ticket(const WOLFSSL_SESSION* sess) { WOLFSSL_ENTER("wolfSSL_SESSION_has_ticket"); #ifdef HAVE_SESSION_TICKET sess = ClientSessionToSession(sess); if (sess) { if ((sess->ticketLen > 0) && (sess->ticket != NULL)) { return WOLFSSL_SUCCESS; } } #else (void)sess; #endif return WOLFSSL_FAILURE; } unsigned long wolfSSL_SESSION_get_ticket_lifetime_hint( const WOLFSSL_SESSION* sess) { WOLFSSL_ENTER("wolfSSL_SESSION_get_ticket_lifetime_hint"); sess = ClientSessionToSession(sess); if (sess) { return sess->timeout; } return 0; } long wolfSSL_SESSION_get_timeout(const WOLFSSL_SESSION* sess) { long timeout = 0; WOLFSSL_ENTER("wolfSSL_SESSION_get_timeout"); sess = ClientSessionToSession(sess); if (sess) timeout = sess->timeout; return timeout; } long wolfSSL_SSL_SESSION_set_timeout(WOLFSSL_SESSION* ses, long t) { word32 tmptime; ses = ClientSessionToSession(ses); if (ses == NULL || t < 0) { return BAD_FUNC_ARG; } tmptime = t & 0xFFFFFFFF; ses->timeout = tmptime; return WOLFSSL_SUCCESS; } long wolfSSL_SESSION_get_time(const WOLFSSL_SESSION* sess) { long bornOn = 0; WOLFSSL_ENTER("wolfSSL_SESSION_get_time"); sess = ClientSessionToSession(sess); if (sess) bornOn = sess->bornOn; return bornOn; } long wolfSSL_SESSION_set_time(WOLFSSL_SESSION *ses, long t) { ses = ClientSessionToSession(ses); if (ses == NULL || t < 0) { return 0; } ses->bornOn = (word32)t; return t; } #endif /* !NO_SESSION_CACHE && OPENSSL_EXTRA || HAVE_EXT_CACHE */ #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ defined(HAVE_EX_DATA) #if defined(HAVE_EX_DATA) && !defined(NO_SESSION_CACHE) static void SESSION_ex_data_cache_update(WOLFSSL_SESSION* session, int idx, void* data, byte get, void** getRet, int* setRet) { int row; int i; int error = 0; SessionRow* sessRow = NULL; const byte* id; byte foundCache = 0; if (getRet != NULL) *getRet = NULL; if (setRet != NULL) *setRet = WOLFSSL_FAILURE; id = session->sessionID; if (session->haveAltSessionID) id = session->altSessionID; row = (int)(HashObject(id, ID_LEN, &error) % SESSION_ROWS); if (error != 0) { WOLFSSL_MSG("Hash session failed"); return; } sessRow = &SessionCache[row]; if (get) error = SESSION_ROW_RD_LOCK(sessRow); else error = SESSION_ROW_WR_LOCK(sessRow); if (error != 0) { WOLFSSL_MSG("Session row lock failed"); return; } for (i = 0; i < SESSIONS_PER_ROW && i < sessRow->totalCount; i++) { WOLFSSL_SESSION* cacheSession; #ifdef SESSION_CACHE_DYNAMIC_MEM cacheSession = sessRow->Sessions[i]; #else cacheSession = &sessRow->Sessions[i]; #endif if (cacheSession && XMEMCMP(id, cacheSession->sessionID, ID_LEN) == 0 && session->side == cacheSession->side #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && (IsAtLeastTLSv1_3(session->version) == IsAtLeastTLSv1_3(cacheSession->version)) #endif ) { if (get) { if (getRet) { *getRet = wolfSSL_CRYPTO_get_ex_data( &cacheSession->ex_data, idx); } } else { if (setRet) { *setRet = wolfSSL_CRYPTO_set_ex_data( &cacheSession->ex_data, idx, data); } } foundCache = 1; break; } } SESSION_ROW_UNLOCK(sessRow); /* If we don't have a session in cache then clear the ex_data and * own it */ if (!foundCache) { XMEMSET(&session->ex_data, 0, sizeof(WOLFSSL_CRYPTO_EX_DATA)); session->ownExData = 1; if (!get) { *setRet = wolfSSL_CRYPTO_set_ex_data(&session->ex_data, idx, data); } } } #endif #endif #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \ || defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY) #ifndef NO_SESSION_CACHE int wolfSSL_SSL_CTX_remove_session(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *s) { #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) int rem_called = FALSE; #endif WOLFSSL_ENTER("wolfSSL_SSL_CTX_remove_session"); s = ClientSessionToSession(s); if (ctx == NULL || s == NULL) return BAD_FUNC_ARG; #ifdef HAVE_EXT_CACHE if (!ctx->internalCacheOff) #endif { const byte* id; WOLFSSL_SESSION *sess = NULL; word32 row = 0; int ret; id = s->sessionID; if (s->haveAltSessionID) id = s->altSessionID; ret = TlsSessionCacheGetAndWrLock(id, &sess, &row, ctx->method->side); if (ret == 0 && sess != NULL) { #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) if (sess->rem_sess_cb != NULL) { rem_called = TRUE; } #endif /* Call this before changing ownExData so that calls to ex_data * don't try to access the SessionCache again. */ EvictSessionFromCache(sess); #ifdef HAVE_EX_DATA if (sess->ownExData) { /* Most recent version of ex data is in cache. Copy it * over so the user can free it. */ XMEMCPY(&s->ex_data, &sess->ex_data, sizeof(WOLFSSL_CRYPTO_EX_DATA)); s->ownExData = 1; sess->ownExData = 0; } #endif #ifdef SESSION_CACHE_DYNAMIC_MEM { /* Find and clear entry. Row is locked so we are good to go. */ int idx; for (idx = 0; idx < SESSIONS_PER_ROW; idx++) { if (sess == SessionCache[row].Sessions[idx]) { XFREE(sess, sess->heap, DYNAMIC_TYPE_SESSION); SessionCache[row].Sessions[idx] = NULL; break; } } } #endif TlsSessionCacheUnlockRow(row); } } #if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA) if (ctx->rem_sess_cb != NULL && !rem_called) { ctx->rem_sess_cb(ctx, s); } #endif /* s cannot be resumed at this point */ s->timeout = 0; return 0; } WOLFSSL_SESSION *wolfSSL_SSL_get0_session(const WOLFSSL *ssl) { WOLFSSL_ENTER("wolfSSL_SSL_get0_session"); return ssl->session; } #endif /* NO_SESSION_CACHE */ #endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || OPENSSL_EXTRA || HAVE_LIGHTY */ #ifdef WOLFSSL_SESSION_EXPORT /* Used to import a serialized TLS session. * WARNING: buf contains sensitive information about the state and is best to be * encrypted before storing if stored. * * @param ssl WOLFSSL structure to import the session into * @param buf serialized session * @param sz size of buffer 'buf' * @return the number of bytes read from buffer 'buf' */ int wolfSSL_tls_import(WOLFSSL* ssl, const unsigned char* buf, unsigned int sz) { if (ssl == NULL || buf == NULL) { return BAD_FUNC_ARG; } return wolfSSL_session_import_internal(ssl, buf, sz, WOLFSSL_EXPORT_TLS); } /* Used to export a serialized TLS session. * WARNING: buf contains sensitive information about the state and is best to be * encrypted before storing if stored. * * @param ssl WOLFSSL structure to export the session from * @param buf output of serialized session * @param sz size in bytes set in 'buf' * @return the number of bytes written into buffer 'buf' */ int wolfSSL_tls_export(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz) { if (ssl == NULL || sz == NULL) { return BAD_FUNC_ARG; } return wolfSSL_session_export_internal(ssl, buf, sz, WOLFSSL_EXPORT_TLS); } #ifdef WOLFSSL_DTLS int wolfSSL_dtls_import(WOLFSSL* ssl, const unsigned char* buf, unsigned int sz) { WOLFSSL_ENTER("wolfSSL_session_import"); if (ssl == NULL || buf == NULL) { return BAD_FUNC_ARG; } /* sanity checks on buffer and protocol are done in internal function */ return wolfSSL_session_import_internal(ssl, buf, sz, WOLFSSL_EXPORT_DTLS); } /* Sets the function to call for serializing the session. This function is * called right after the handshake is completed. */ int wolfSSL_CTX_dtls_set_export(WOLFSSL_CTX* ctx, wc_dtls_export func) { WOLFSSL_ENTER("wolfSSL_CTX_dtls_set_export"); /* purposefully allow func to be NULL */ if (ctx == NULL) { return BAD_FUNC_ARG; } ctx->dtls_export = func; return WOLFSSL_SUCCESS; } /* Sets the function in WOLFSSL struct to call for serializing the session. This * function is called right after the handshake is completed. */ int wolfSSL_dtls_set_export(WOLFSSL* ssl, wc_dtls_export func) { WOLFSSL_ENTER("wolfSSL_dtls_set_export"); /* purposefully allow func to be NULL */ if (ssl == NULL) { return BAD_FUNC_ARG; } ssl->dtls_export = func; return WOLFSSL_SUCCESS; } /* This function allows for directly serializing a session rather than using * callbacks. It has less overhead by removing a temporary buffer and gives * control over when the session gets serialized. When using callbacks the * session is always serialized immediately after the handshake is finished. * * buf is the argument to contain the serialized session * sz is the size of the buffer passed in * ssl is the WOLFSSL struct to serialize * returns the size of serialized session on success, 0 on no action, and * negative value on error */ int wolfSSL_dtls_export(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz) { WOLFSSL_ENTER("wolfSSL_dtls_export"); if (ssl == NULL || sz == NULL) { return BAD_FUNC_ARG; } if (buf == NULL) { *sz = MAX_EXPORT_BUFFER; return 0; } /* if not DTLS do nothing */ if (!ssl->options.dtls) { WOLFSSL_MSG("Currently only DTLS export is supported"); return 0; } /* copy over keys, options, and dtls state struct */ return wolfSSL_session_export_internal(ssl, buf, sz, WOLFSSL_EXPORT_DTLS); } /* This function is similar to wolfSSL_dtls_export but only exports the portion * of the WOLFSSL structure related to the state of the connection, i.e. peer * sequence number, epoch, AEAD state etc. * * buf is the argument to contain the serialized state, if null then set "sz" to * buffer size required * sz is the size of the buffer passed in * ssl is the WOLFSSL struct to serialize * returns the size of serialized session on success, 0 on no action, and * negative value on error */ int wolfSSL_dtls_export_state_only(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz) { WOLFSSL_ENTER("wolfSSL_dtls_export_state_only"); if (ssl == NULL || sz == NULL) { return BAD_FUNC_ARG; } if (buf == NULL) { *sz = MAX_EXPORT_STATE_BUFFER; return 0; } /* if not DTLS do nothing */ if (!ssl->options.dtls) { WOLFSSL_MSG("Currently only DTLS export state is supported"); return 0; } /* copy over keys, options, and dtls state struct */ return wolfSSL_dtls_export_state_internal(ssl, buf, *sz); } /* returns 0 on success */ int wolfSSL_send_session(WOLFSSL* ssl) { int ret; byte* buf; word32 bufSz = MAX_EXPORT_BUFFER; WOLFSSL_ENTER("wolfSSL_send_session"); if (ssl == NULL) { return BAD_FUNC_ARG; } buf = (byte*)XMALLOC(bufSz, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (buf == NULL) { return MEMORY_E; } /* if not DTLS do nothing */ if (!ssl->options.dtls) { XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); WOLFSSL_MSG("Currently only DTLS export is supported"); return 0; } /* copy over keys, options, and dtls state struct */ ret = wolfSSL_session_export_internal(ssl, buf, &bufSz, WOLFSSL_EXPORT_DTLS); if (ret < 0) { XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } /* if no error ret has size of buffer */ ret = ssl->dtls_export(ssl, buf, ret, NULL); if (ret != WOLFSSL_SUCCESS) { XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } XFREE(buf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); return 0; } #endif /* WOLFSSL_DTLS */ #endif /* WOLFSSL_SESSION_EXPORT */ #ifdef OPENSSL_EXTRA /* Copies the master secret over to out buffer. If outSz is 0 returns the size * of master secret. * * ses : a session from completed TLS/SSL handshake * out : buffer to hold copy of master secret * outSz : size of out buffer * returns : number of bytes copied into out buffer on success * less then or equal to 0 is considered a failure case */ int wolfSSL_SESSION_get_master_key(const WOLFSSL_SESSION* ses, unsigned char* out, int outSz) { int size; ses = ClientSessionToSession(ses); if (outSz == 0) { return SECRET_LEN; } if (ses == NULL || out == NULL || outSz < 0) { return 0; } if (outSz > SECRET_LEN) { size = SECRET_LEN; } else { size = outSz; } XMEMCPY(out, ses->masterSecret, size); return size; } int wolfSSL_SESSION_get_master_key_length(const WOLFSSL_SESSION* ses) { (void)ses; return SECRET_LEN; } #ifdef WOLFSSL_EARLY_DATA unsigned int wolfSSL_SESSION_get_max_early_data(const WOLFSSL_SESSION *session) { return session->maxEarlyDataSz; } #endif /* WOLFSSL_EARLY_DATA */ #endif /* OPENSSL_EXTRA */ void SetupSession(WOLFSSL* ssl) { WOLFSSL_SESSION* session = ssl->session; WOLFSSL_ENTER("SetupSession"); if (!IsAtLeastTLSv1_3(ssl->version) && ssl->arrays != NULL) { /* Make sure the session ID is available when the user calls any * get_session API */ if (!session->haveAltSessionID) { XMEMCPY(session->sessionID, ssl->arrays->sessionID, ID_LEN); session->sessionIDSz = ssl->arrays->sessionIDSz; } else { XMEMCPY(session->sessionID, session->altSessionID, ID_LEN); session->sessionIDSz = ID_LEN; } } session->side = (byte)ssl->options.side; if (!IsAtLeastTLSv1_3(ssl->version) && ssl->arrays != NULL) XMEMCPY(session->masterSecret, ssl->arrays->masterSecret, SECRET_LEN); session->haveEMS = ssl->options.haveEMS; #ifdef WOLFSSL_SESSION_ID_CTX /* If using compatibility layer then check for and copy over session context * id. */ if (ssl->sessionCtxSz > 0 && ssl->sessionCtxSz < ID_LEN) { XMEMCPY(ssl->session->sessionCtx, ssl->sessionCtx, ssl->sessionCtxSz); session->sessionCtxSz = ssl->sessionCtxSz; } #endif session->timeout = ssl->timeout; #ifndef NO_ASN_TIME session->bornOn = LowResTimer(); #endif #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) session->version = ssl->version; #endif #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) session->cipherSuite0 = ssl->options.cipherSuite0; session->cipherSuite = ssl->options.cipherSuite; #endif #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) session->peerVerifyRet = (byte)ssl->peerVerifyRet; #endif session->isSetup = 1; } #ifdef WOLFSSL_SESSION_ID_CTX /* Storing app session context id, this value is inherited by WOLFSSL * objects created from WOLFSSL_CTX. Any session that is imported with a * different session context id will be rejected. * * ctx structure to set context in * sid_ctx value of context to set * sid_ctx_len length of sid_ctx buffer * * Returns WOLFSSL_SUCCESS in success case and WOLFSSL_FAILURE when failing */ int wolfSSL_CTX_set_session_id_context(WOLFSSL_CTX* ctx, const unsigned char* sid_ctx, unsigned int sid_ctx_len) { WOLFSSL_ENTER("wolfSSL_CTX_set_session_id_context"); /* No application specific context needed for wolfSSL */ if (sid_ctx_len > ID_LEN || ctx == NULL || sid_ctx == NULL) { return WOLFSSL_FAILURE; } XMEMCPY(ctx->sessionCtx, sid_ctx, sid_ctx_len); ctx->sessionCtxSz = (byte)sid_ctx_len; return WOLFSSL_SUCCESS; } /* Storing app session context id. Any session that is imported with a * different session context id will be rejected. * * ssl structure to set context in * id value of context to set * len length of sid_ctx buffer * * Returns WOLFSSL_SUCCESS in success case and WOLFSSL_FAILURE when failing */ int wolfSSL_set_session_id_context(WOLFSSL* ssl, const unsigned char* id, unsigned int len) { WOLFSSL_ENTER("wolfSSL_set_session_id_context"); if (len > ID_LEN || ssl == NULL || id == NULL) { return WOLFSSL_FAILURE; } XMEMCPY(ssl->sessionCtx, id, len); ssl->sessionCtxSz = (byte)len; return WOLFSSL_SUCCESS; } #endif /* return a new malloc'd session with default settings on success */ WOLFSSL_SESSION* wolfSSL_NewSession(void* heap) { WOLFSSL_SESSION* ret = NULL; WOLFSSL_ENTER("wolfSSL_NewSession"); ret = (WOLFSSL_SESSION*)XMALLOC(sizeof(WOLFSSL_SESSION), heap, DYNAMIC_TYPE_SESSION); if (ret != NULL) { int err; XMEMSET(ret, 0, sizeof(WOLFSSL_SESSION)); wolfSSL_RefInit(&ret->ref, &err); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (err != 0) { WOLFSSL_MSG("Error setting up session reference mutex"); XFREE(ret, ret->heap, DYNAMIC_TYPE_SESSION); return NULL; } #else (void)err; #endif #ifndef NO_SESSION_CACHE ret->cacheRow = INVALID_SESSION_ROW; /* not in cache */ #endif ret->type = WOLFSSL_SESSION_TYPE_HEAP; ret->heap = heap; #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("SESSION master secret", ret->masterSecret, SECRET_LEN); wc_MemZero_Add("SESSION id", ret->sessionID, ID_LEN); #endif #ifdef HAVE_SESSION_TICKET ret->ticket = ret->staticTicket; #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) ret->ticketNonce.data = ret->ticketNonce.dataStatic; #endif #endif #ifdef HAVE_EX_DATA ret->ownExData = 1; if (crypto_ex_cb_ctx_session != NULL) { crypto_ex_cb_setup_new_data(ret, crypto_ex_cb_ctx_session, &ret->ex_data); } #endif } return ret; } WOLFSSL_SESSION* wolfSSL_SESSION_new_ex(void* heap) { return wolfSSL_NewSession(heap); } WOLFSSL_SESSION* wolfSSL_SESSION_new(void) { return wolfSSL_SESSION_new_ex(NULL); } /* add one to session reference count * return WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on error */ int wolfSSL_SESSION_up_ref(WOLFSSL_SESSION* session) { int ret; session = ClientSessionToSession(session); if (session == NULL || session->type != WOLFSSL_SESSION_TYPE_HEAP) return WOLFSSL_FAILURE; wolfSSL_RefInc(&session->ref, &ret); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (ret != 0) { WOLFSSL_MSG("Failed to lock session mutex"); return WOLFSSL_FAILURE; } #else (void)ret; #endif return WOLFSSL_SUCCESS; } /** * Deep copy the contents from input to output. * @param input The source of the copy. * @param output The destination of the copy. * @param avoidSysCalls If true, then system calls will be avoided or an error * will be returned if it is not possible to proceed * without a system call. This is useful for fetching * sessions from cache. When a cache row is locked, we * don't want to block other threads with long running * system calls. * @param ticketNonceBuf If not null and @avoidSysCalls is true, the copy of the * ticketNonce will happen in this pre allocated buffer * @param ticketNonceLen @ticketNonceBuf len as input, used length on output * @param ticketNonceUsed if @ticketNonceBuf was used to copy the ticket noncet * @return WOLFSSL_SUCCESS on success * WOLFSSL_FAILURE on failure */ static int wolfSSL_DupSessionEx(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output, int avoidSysCalls, byte* ticketNonceBuf, byte* ticketNonceLen, byte* preallocUsed) { #ifdef HAVE_SESSION_TICKET int ticLenAlloc = 0; byte *ticBuff = NULL; #endif const size_t copyOffset = OFFSETOF(WOLFSSL_SESSION, heap) + sizeof(input->heap); int ret = WOLFSSL_SUCCESS; (void)avoidSysCalls; (void)ticketNonceBuf; (void)ticketNonceLen; (void)preallocUsed; input = ClientSessionToSession(input); output = ClientSessionToSession(output); if (input == NULL || output == NULL || input == output) { WOLFSSL_MSG("input or output are null or same"); return WOLFSSL_FAILURE; } #ifdef HAVE_SESSION_TICKET if (output->ticket != output->staticTicket) { ticBuff = output->ticket; ticLenAlloc = output->ticketLenAlloc; } #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) /* free the data, it would be better to reuse the buffer but this * maintain the code simpler. A smart allocator should reuse the free'd * buffer in the next malloc without much performance penalties. */ if (output->ticketNonce.data != output->ticketNonce.dataStatic) { /* Callers that avoid syscall should never calls this with * output->tickeNonce.data being a dynamic buffer.*/ if (avoidSysCalls) { WOLFSSL_MSG("can't avoid syscalls with dynamic TicketNonce buffer"); return WOLFSSL_FAILURE; } XFREE(output->ticketNonce.data, output->heap, DYNAMIC_TYPE_SESSION_TICK); output->ticketNonce.data = output->ticketNonce.dataStatic; output->ticketNonce.len = 0; } #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif /* HAVE_SESSION_TICKET */ #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (output->peer != NULL) { if (avoidSysCalls) { WOLFSSL_MSG("Can't free cert when avoiding syscalls"); return WOLFSSL_FAILURE; } wolfSSL_X509_free(output->peer); output->peer = NULL; } #endif XMEMCPY((byte*)output + copyOffset, (byte*)input + copyOffset, sizeof(WOLFSSL_SESSION) - copyOffset); #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) && \ defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) /* fix pointer to static after the copy */ output->ticketNonce.data = output->ticketNonce.dataStatic; #endif /* Set sane values for copy */ #ifndef NO_SESSION_CACHE if (output->type != WOLFSSL_SESSION_TYPE_CACHE) output->cacheRow = INVALID_SESSION_ROW; #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (input->peer != NULL && input->peer->dynamicMemory) { if (wolfSSL_X509_up_ref(input->peer) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Can't increase peer cert ref count"); output->peer = NULL; } } else if (!avoidSysCalls) output->peer = wolfSSL_X509_dup(input->peer); else /* output->peer is not that important to copy */ output->peer = NULL; #endif #ifdef HAVE_SESSION_TICKET if (input->ticketLen > SESSION_TICKET_LEN) { /* Need dynamic buffer */ if (ticBuff == NULL || ticLenAlloc < input->ticketLen) { /* allocate new one */ byte* tmp; if (avoidSysCalls) { WOLFSSL_MSG("Failed to allocate memory for ticket when avoiding" " syscalls"); output->ticket = ticBuff; output->ticketLenAlloc = (word16) ticLenAlloc; output->ticketLen = 0; ret = WOLFSSL_FAILURE; } else { #ifdef WOLFSSL_NO_REALLOC tmp = (byte*)XMALLOC(input->ticketLen, output->heap, DYNAMIC_TYPE_SESSION_TICK); XFREE(ticBuff, output->heap, DYNAMIC_TYPE_SESSION_TICK); ticBuff = NULL; #else tmp = (byte*)XREALLOC(ticBuff, input->ticketLen, output->heap, DYNAMIC_TYPE_SESSION_TICK); #endif /* WOLFSSL_NO_REALLOC */ if (tmp == NULL) { WOLFSSL_MSG("Failed to allocate memory for ticket"); #ifndef WOLFSSL_NO_REALLOC XFREE(ticBuff, output->heap, DYNAMIC_TYPE_SESSION_TICK); ticBuff = NULL; #endif /* WOLFSSL_NO_REALLOC */ output->ticket = NULL; output->ticketLen = 0; output->ticketLenAlloc = 0; ret = WOLFSSL_FAILURE; } else { ticBuff = tmp; ticLenAlloc = input->ticketLen; } } } if (ticBuff != NULL && ret == WOLFSSL_SUCCESS) { XMEMCPY(ticBuff, input->ticket, input->ticketLen); output->ticket = ticBuff; output->ticketLenAlloc = (word16) ticLenAlloc; } } else { /* Default ticket to non dynamic */ if (avoidSysCalls) { /* Try to use ticBuf if available. Caller can later move it to * the static buffer. */ if (ticBuff != NULL) { if (ticLenAlloc >= input->ticketLen) { output->ticket = ticBuff; output->ticketLenAlloc = ticLenAlloc; } else { WOLFSSL_MSG("ticket dynamic buffer too small but we are " "avoiding system calls"); ret = WOLFSSL_FAILURE; output->ticket = ticBuff; output->ticketLenAlloc = (word16) ticLenAlloc; output->ticketLen = 0; } } else { output->ticket = output->staticTicket; output->ticketLenAlloc = 0; } } else { if (ticBuff != NULL) XFREE(ticBuff, output->heap, DYNAMIC_TYPE_SESSION_TICK); output->ticket = output->staticTicket; output->ticketLenAlloc = 0; } if (input->ticketLenAlloc > 0 && ret == WOLFSSL_SUCCESS) { /* Shouldn't happen as session should have placed this in * the static buffer */ XMEMCPY(output->ticket, input->ticket, input->ticketLen); } } ticBuff = NULL; #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (preallocUsed != NULL) *preallocUsed = 0; if (input->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ && ret == WOLFSSL_SUCCESS) { /* TicketNonce does not fit in the static buffer */ if (!avoidSysCalls) { output->ticketNonce.data = (byte*)XMALLOC(input->ticketNonce.len, output->heap, DYNAMIC_TYPE_SESSION_TICK); if (output->ticketNonce.data == NULL) { WOLFSSL_MSG("Failed to allocate space for ticket nonce"); output->ticketNonce.data = output->ticketNonce.dataStatic; output->ticketNonce.len = 0; ret = WOLFSSL_FAILURE; } else { output->ticketNonce.len = input->ticketNonce.len; XMEMCPY(output->ticketNonce.data, input->ticketNonce.data, input->ticketNonce.len); ret = WOLFSSL_SUCCESS; } } /* we can't do syscalls. Use prealloc buffers if provided from the * caller. */ else if (ticketNonceBuf != NULL && *ticketNonceLen >= input->ticketNonce.len) { XMEMCPY(ticketNonceBuf, input->ticketNonce.data, input->ticketNonce.len); *ticketNonceLen = input->ticketNonce.len; if (preallocUsed != NULL) *preallocUsed = 1; ret = WOLFSSL_SUCCESS; } else { WOLFSSL_MSG("TicketNonce bigger than static buffer, and we can't " "do syscalls"); ret = WOLFSSL_FAILURE; } } #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif /* HAVE_SESSION_TICKET */ #ifdef HAVE_EX_DATA if (input->type != WOLFSSL_SESSION_TYPE_CACHE && output->type != WOLFSSL_SESSION_TYPE_CACHE) { /* Not called with cache as that passes ownership of ex_data */ ret = crypto_ex_cb_dup_data(&input->ex_data, &output->ex_data, crypto_ex_cb_ctx_session); } #endif return ret; } /** * Deep copy the contents from input to output. * @param input The source of the copy. * @param output The destination of the copy. * @param avoidSysCalls If true, then system calls will be avoided or an error * will be returned if it is not possible to proceed * without a system call. This is useful for fetching * sessions from cache. When a cache row is locked, we * don't want to block other threads with long running * system calls. * @return WOLFSSL_SUCCESS on success * WOLFSSL_FAILURE on failure */ int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output, int avoidSysCalls) { return wolfSSL_DupSessionEx(input, output, avoidSysCalls, NULL, NULL, NULL); } WOLFSSL_SESSION* wolfSSL_SESSION_dup(WOLFSSL_SESSION* session) { WOLFSSL_SESSION* copy; WOLFSSL_ENTER("wolfSSL_SESSION_dup"); session = ClientSessionToSession(session); if (session == NULL) return NULL; #ifdef HAVE_SESSION_TICKET if (session->ticketLenAlloc > 0 && !session->ticket) { WOLFSSL_MSG("Session dynamic flag is set but ticket pointer is null"); return NULL; } #endif copy = wolfSSL_NewSession(session->heap); if (copy != NULL && wolfSSL_DupSession(session, copy, 0) != WOLFSSL_SUCCESS) { wolfSSL_FreeSession(NULL, copy); copy = NULL; } return copy; } void wolfSSL_FreeSession(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session) { session = ClientSessionToSession(session); if (session == NULL) return; (void)ctx; WOLFSSL_ENTER("wolfSSL_FreeSession"); if (session->ref.count > 0) { int ret; int isZero; wolfSSL_RefDec(&session->ref, &isZero, &ret); (void)ret; if (!isZero) { return; } wolfSSL_RefFree(&session->ref); } WOLFSSL_MSG("wolfSSL_FreeSession full free"); #ifdef HAVE_EX_DATA if (session->ownExData) { crypto_ex_cb_free_data(session, crypto_ex_cb_ctx_session, &session->ex_data); } #endif #ifdef HAVE_EX_DATA_CLEANUP_HOOKS wolfSSL_CRYPTO_cleanup_ex_data(&session->ex_data); #endif #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) if (session->peer) { wolfSSL_X509_free(session->peer); session->peer = NULL; } #endif #ifdef HAVE_SESSION_TICKET if (session->ticketLenAlloc > 0) { XFREE(session->ticket, session->heap, DYNAMIC_TYPE_SESSION_TICK); session->ticket = session->staticTicket; session->ticketLen = 0; session->ticketLenAlloc = 0; } #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (session->ticketNonce.data != session->ticketNonce.dataStatic) { XFREE(session->ticketNonce.data, session->heap, DYNAMIC_TYPE_SESSION_TICK); session->ticketNonce.data = session->ticketNonce.dataStatic; session->ticketNonce.len = 0; } #endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/ #endif #ifdef HAVE_EX_DATA_CLEANUP_HOOKS wolfSSL_CRYPTO_cleanup_ex_data(&session->ex_data); #endif /* Make sure masterSecret is zeroed. */ ForceZero(session->masterSecret, SECRET_LEN); /* Session ID is sensitive information too. */ ForceZero(session->sessionID, ID_LEN); if (session->type == WOLFSSL_SESSION_TYPE_HEAP) { XFREE(session, session->heap, DYNAMIC_TYPE_SESSION); } } /* DO NOT use this API internally. Use wolfSSL_FreeSession directly instead * and pass in the ctx parameter if possible (like from ssl->ctx). */ void wolfSSL_SESSION_free(WOLFSSL_SESSION* session) { session = ClientSessionToSession(session); wolfSSL_FreeSession(NULL, session); } #if defined(OPENSSL_EXTRA) || defined(HAVE_EXT_CACHE) /** * set cipher to WOLFSSL_SESSION from WOLFSSL_CIPHER * @param session a pointer to WOLFSSL_SESSION structure * @param cipher a function pointer to WOLFSSL_CIPHER * @return WOLFSSL_SUCCESS on success, otherwise WOLFSSL_FAILURE */ int wolfSSL_SESSION_set_cipher(WOLFSSL_SESSION* session, const WOLFSSL_CIPHER* cipher) { WOLFSSL_ENTER("wolfSSL_SESSION_set_cipher"); session = ClientSessionToSession(session); /* sanity check */ if (session == NULL || cipher == NULL) { WOLFSSL_MSG("bad argument"); return WOLFSSL_FAILURE; } session->cipherSuite0 = cipher->cipherSuite0; session->cipherSuite = cipher->cipherSuite; WOLFSSL_LEAVE("wolfSSL_SESSION_set_cipher", WOLFSSL_SUCCESS); return WOLFSSL_SUCCESS; } #endif /* OPENSSL_EXTRA || HAVE_EXT_CACHE */ const char* wolfSSL_SESSION_CIPHER_get_name(const WOLFSSL_SESSION* session) { session = ClientSessionToSession(session); if (session == NULL) { return NULL; } #if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) #if !defined(WOLFSSL_CIPHER_INTERNALNAME) && !defined(NO_ERROR_STRINGS) return GetCipherNameIana(session->cipherSuite0, session->cipherSuite); #else return GetCipherNameInternal(session->cipherSuite0, session->cipherSuite); #endif #else return NULL; #endif } #if defined(OPENSSL_ALL) || defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_NGINX) const unsigned char *wolfSSL_SESSION_get0_id_context( const WOLFSSL_SESSION *sess, unsigned int *sid_ctx_length) { return wolfSSL_SESSION_get_id((WOLFSSL_SESSION *)sess, sid_ctx_length); } int wolfSSL_SESSION_set1_id(WOLFSSL_SESSION *s, const unsigned char *sid, unsigned int sid_len) { if (s == NULL) { return WOLFSSL_FAILURE; } if (sid_len > ID_LEN) { return WOLFSSL_FAILURE; } s->sessionIDSz = sid_len; if (sid != s->sessionID) { XMEMCPY(s->sessionID, sid, sid_len); } return WOLFSSL_SUCCESS; } int wolfSSL_SESSION_set1_id_context(WOLFSSL_SESSION *s, const unsigned char *sid_ctx, unsigned int sid_ctx_len) { if (s == NULL) { return WOLFSSL_FAILURE; } if (sid_ctx_len > ID_LEN) { return WOLFSSL_FAILURE; } s->sessionCtxSz = sid_ctx_len; if (sid_ctx != s->sessionCtx) { XMEMCPY(s->sessionCtx, sid_ctx, sid_ctx_len); } return WOLFSSL_SUCCESS; } #endif #ifdef OPENSSL_EXTRA /* Return the total number of sessions */ long wolfSSL_CTX_sess_number(WOLFSSL_CTX* ctx) { word32 total = 0; WOLFSSL_ENTER("wolfSSL_CTX_sess_number"); (void)ctx; #if defined(WOLFSSL_SESSION_STATS) && !defined(NO_SESSION_CACHE) if (wolfSSL_get_session_stats(NULL, &total, NULL, NULL) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Error getting session stats"); } #else WOLFSSL_MSG("Please use macro WOLFSSL_SESSION_STATS for session stats"); #endif return (long)total; } #endif #ifdef SESSION_CERTS /* get session ID */ WOLFSSL_ABI const byte* wolfSSL_get_sessionID(const WOLFSSL_SESSION* session) { WOLFSSL_ENTER("wolfSSL_get_sessionID"); session = ClientSessionToSession(session); if (session) return session->sessionID; return NULL; } #endif #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ defined(HAVE_EX_DATA) int wolfSSL_SESSION_set_ex_data(WOLFSSL_SESSION* session, int idx, void* data) { int ret = WOLFSSL_FAILURE; WOLFSSL_ENTER("wolfSSL_SESSION_set_ex_data"); #ifdef HAVE_EX_DATA session = ClientSessionToSession(session); if (session != NULL) { #ifndef NO_SESSION_CACHE if (!session->ownExData) { /* Need to update in cache */ SESSION_ex_data_cache_update(session, idx, data, 0, NULL, &ret); } else #endif { ret = wolfSSL_CRYPTO_set_ex_data(&session->ex_data, idx, data); } } #else (void)session; (void)idx; (void)data; #endif return ret; } #ifdef HAVE_EX_DATA_CLEANUP_HOOKS int wolfSSL_SESSION_set_ex_data_with_cleanup( WOLFSSL_SESSION* session, int idx, void* data, wolfSSL_ex_data_cleanup_routine_t cleanup_routine) { WOLFSSL_ENTER("wolfSSL_SESSION_set_ex_data_with_cleanup"); session = ClientSessionToSession(session); if(session != NULL) { return wolfSSL_CRYPTO_set_ex_data_with_cleanup(&session->ex_data, idx, data, cleanup_routine); } return WOLFSSL_FAILURE; } #endif /* HAVE_EX_DATA_CLEANUP_HOOKS */ void* wolfSSL_SESSION_get_ex_data(const WOLFSSL_SESSION* session, int idx) { void* ret = NULL; WOLFSSL_ENTER("wolfSSL_SESSION_get_ex_data"); #ifdef HAVE_EX_DATA session = ClientSessionToSession(session); if (session != NULL) { #ifndef NO_SESSION_CACHE if (!session->ownExData) { /* Need to retrieve the data from the session cache */ SESSION_ex_data_cache_update((WOLFSSL_SESSION*)session, idx, NULL, 1, &ret, NULL); } else #endif { ret = wolfSSL_CRYPTO_get_ex_data(&session->ex_data, idx); } } #else (void)session; (void)idx; #endif return ret; } #endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL || HAVE_EX_DATA */ #if defined(OPENSSL_ALL) || (defined(OPENSSL_EXTRA) && \ (defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \ defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) #ifdef HAVE_EX_DATA int wolfSSL_SESSION_get_ex_new_index(long ctx_l,void* ctx_ptr, WOLFSSL_CRYPTO_EX_new* new_func, WOLFSSL_CRYPTO_EX_dup* dup_func, WOLFSSL_CRYPTO_EX_free* free_func) { WOLFSSL_ENTER("wolfSSL_SESSION_get_ex_new_index"); return wolfssl_get_ex_new_index(WOLF_CRYPTO_EX_INDEX_SSL_SESSION, ctx_l, ctx_ptr, new_func, dup_func, free_func); } #endif #endif #if defined(OPENSSL_ALL) || \ defined(OPENSSL_EXTRA) || defined(HAVE_STUNNEL) || \ defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) const byte* wolfSSL_SESSION_get_id(const WOLFSSL_SESSION* sess, unsigned int* idLen) { WOLFSSL_ENTER("wolfSSL_SESSION_get_id"); sess = ClientSessionToSession(sess); if (sess == NULL || idLen == NULL) { WOLFSSL_MSG("Bad func args. Please provide idLen"); return NULL; } #ifdef HAVE_SESSION_TICKET if (sess->haveAltSessionID) { *idLen = ID_LEN; return sess->altSessionID; } #endif *idLen = sess->sessionIDSz; return sess->sessionID; } #if (defined(HAVE_SESSION_TICKET) || defined(SESSION_CERTS)) && \ !defined(NO_FILESYSTEM) #ifndef NO_BIO #if defined(SESSION_CERTS) || \ (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) static const char* wolfSSL_internal_get_version(const ProtocolVersion* version); /* returns a pointer to the protocol used by the session */ static const char* wolfSSL_SESSION_get_protocol(const WOLFSSL_SESSION* in) { in = ClientSessionToSession(in); return wolfSSL_internal_get_version((ProtocolVersion*)&in->version); } #endif /* returns true (non 0) if the session has EMS (extended master secret) */ static int wolfSSL_SESSION_haveEMS(const WOLFSSL_SESSION* in) { in = ClientSessionToSession(in); if (in == NULL) return 0; return in->haveEMS; } #if defined(HAVE_SESSION_TICKET) /* prints out the ticket to bio passed in * return WOLFSSL_SUCCESS on success */ static int wolfSSL_SESSION_print_ticket(WOLFSSL_BIO* bio, const WOLFSSL_SESSION* in, const char* tab) { unsigned short i, j, z, sz; short tag = 0; byte* pt; in = ClientSessionToSession(in); if (in == NULL || bio == NULL) { return BAD_FUNC_ARG; } sz = in->ticketLen; pt = in->ticket; if (wolfSSL_BIO_printf(bio, "%s\n", (sz == 0)? " NONE": "") <= 0) return WOLFSSL_FAILURE; for (i = 0; i < sz;) { char asc[16]; XMEMSET(asc, 0, sizeof(asc)); if (sz - i < 16) { if (wolfSSL_BIO_printf(bio, "%s%04X -", tab, tag + (sz - i)) <= 0) return WOLFSSL_FAILURE; } else { if (wolfSSL_BIO_printf(bio, "%s%04X -", tab, tag) <= 0) return WOLFSSL_FAILURE; } for (j = 0; i < sz && j < 8; j++,i++) { asc[j] = ((pt[i])&0x6f)>='A'?((pt[i])&0x6f):'.'; if (wolfSSL_BIO_printf(bio, " %02X", pt[i]) <= 0) return WOLFSSL_FAILURE; } if (i < sz) { asc[j] = ((pt[i])&0x6f)>='A'?((pt[i])&0x6f):'.'; if (wolfSSL_BIO_printf(bio, "-%02X", pt[i]) <= 0) return WOLFSSL_FAILURE; j++; i++; } for (; i < sz && j < 16; j++,i++) { asc[j] = ((pt[i])&0x6f)>='A'?((pt[i])&0x6f):'.'; if (wolfSSL_BIO_printf(bio, " %02X", pt[i]) <= 0) return WOLFSSL_FAILURE; } /* pad out spacing */ for (z = j; z < 17; z++) { if (wolfSSL_BIO_printf(bio, " ") <= 0) return WOLFSSL_FAILURE; } for (z = 0; z < j; z++) { if (wolfSSL_BIO_printf(bio, "%c", asc[z]) <= 0) return WOLFSSL_FAILURE; } if (wolfSSL_BIO_printf(bio, "\n") <= 0) return WOLFSSL_FAILURE; tag += 16; } return WOLFSSL_SUCCESS; } #endif /* HAVE_SESSION_TICKET */ /* prints out the session information in human readable form * return WOLFSSL_SUCCESS on success */ int wolfSSL_SESSION_print(WOLFSSL_BIO *bp, const WOLFSSL_SESSION *session) { const unsigned char* pt; unsigned char buf[SECRET_LEN]; unsigned int sz = 0, i; int ret; session = ClientSessionToSession(session); if (session == NULL) { return WOLFSSL_FAILURE; } if (wolfSSL_BIO_printf(bp, "%s\n", "SSL-Session:") <= 0) return WOLFSSL_FAILURE; #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \ defined(HAVE_SESSION_TICKET)) if (wolfSSL_BIO_printf(bp, " Protocol : %s\n", wolfSSL_SESSION_get_protocol(session)) <= 0) return WOLFSSL_FAILURE; #endif if (wolfSSL_BIO_printf(bp, " Cipher : %s\n", wolfSSL_SESSION_CIPHER_get_name(session)) <= 0) return WOLFSSL_FAILURE; pt = wolfSSL_SESSION_get_id(session, &sz); if (wolfSSL_BIO_printf(bp, " Session-ID: ") <= 0) return WOLFSSL_FAILURE; for (i = 0; i < sz; i++) { if (wolfSSL_BIO_printf(bp, "%02X", pt[i]) <= 0) return WOLFSSL_FAILURE; } if (wolfSSL_BIO_printf(bp, "\n") <= 0) return WOLFSSL_FAILURE; if (wolfSSL_BIO_printf(bp, " Session-ID-ctx: \n") <= 0) return WOLFSSL_FAILURE; ret = wolfSSL_SESSION_get_master_key(session, buf, sizeof(buf)); if (wolfSSL_BIO_printf(bp, " Master-Key: ") <= 0) return WOLFSSL_FAILURE; if (ret > 0) { sz = (unsigned int)ret; for (i = 0; i < sz; i++) { if (wolfSSL_BIO_printf(bp, "%02X", buf[i]) <= 0) return WOLFSSL_FAILURE; } } if (wolfSSL_BIO_printf(bp, "\n") <= 0) return WOLFSSL_FAILURE; /* @TODO PSK identity hint and SRP */ if (wolfSSL_BIO_printf(bp, " TLS session ticket:") <= 0) return WOLFSSL_FAILURE; #ifdef HAVE_SESSION_TICKET if (wolfSSL_SESSION_print_ticket(bp, session, " ") != WOLFSSL_SUCCESS) return WOLFSSL_FAILURE; #endif #if !defined(NO_SESSION_CACHE) && (defined(OPENSSL_EXTRA) || \ defined(HAVE_EXT_CACHE)) if (wolfSSL_BIO_printf(bp, " Start Time: %ld\n", wolfSSL_SESSION_get_time(session)) <= 0) return WOLFSSL_FAILURE; if (wolfSSL_BIO_printf(bp, " Timeout : %ld (sec)\n", wolfSSL_SESSION_get_timeout(session)) <= 0) return WOLFSSL_FAILURE; #endif /* !NO_SESSION_CACHE && OPENSSL_EXTRA || HAVE_EXT_CACHE */ /* @TODO verify return code print */ if (wolfSSL_BIO_printf(bp, " Extended master secret: %s\n", (wolfSSL_SESSION_haveEMS(session) == 0)? "no" : "yes") <= 0) return WOLFSSL_FAILURE; return WOLFSSL_SUCCESS; } #endif /* !NO_BIO */ #endif /* (HAVE_SESSION_TICKET || SESSION_CERTS) && !NO_FILESYSTEM */ #endif /* OPENSSL_ALL || OPENSSL_EXTRA || HAVE_STUNNEL || WOLFSSL_NGINX || * WOLFSSL_HAPROXY */ #ifdef OPENSSL_EXTRA /** * Determine whether a WOLFSSL_SESSION object can be used for resumption * @param s a pointer to WOLFSSL_SESSION structure * @return return 1 if session is resumable, otherwise 0. */ int wolfSSL_SESSION_is_resumable(const WOLFSSL_SESSION *s) { s = ClientSessionToSession(s); if (s == NULL) return 0; #ifdef HAVE_SESSION_TICKET if (s->ticketLen > 0) return 1; #endif if (s->sessionIDSz > 0) return 1; return 0; } #endif /* OPENSSL_EXTRA */ #endif /* !WOLFSSL_SSL_SESS_INCLUDED */