diff --git a/src/dtls.c b/src/dtls.c index 326b63d17..65a9bcdfd 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -19,6 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +/* + * WOLFSSL_DTLS_NO_HVR_ON_RESUME + * If defined, a DTLS server will not do a cookie exchange on successful + * client resumption: the resumption will be faster (one RTT less) and + * will consume less bandwidth (one ClientHello and one HelloVerifyRequest + * less). On the other hand, if a valid SessionID is collected, forged + * clientHello messages will consume resources on the server. + */ + #ifdef HAVE_CONFIG_H #include #endif @@ -194,6 +203,114 @@ static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch) return 0; } +#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME +#ifdef HAVE_SESSION_TICKET +static int TlsxFindByType(WolfSSL_ConstVector* ret, word16 extType, + WolfSSL_ConstVector exts) +{ + word32 len, idx = 0; + word16 type; + WolfSSL_ConstVector ext; + + XMEMSET(ret, 0, sizeof(*ret)); + len = exts.size; + /* type + len */ + while (len >= OPAQUE16_LEN + OPAQUE16_LEN) { + ato16(exts.elements + idx, &type); + idx += OPAQUE16_LEN; + idx += ReadVector16(exts.elements + idx, &ext); + if (idx > exts.size) + return BUFFER_ERROR; + if (type == extType) { + XMEMCPY(ret, &ext, sizeof(ext)); + return 0; + } + len = exts.size - idx; + } + return 0; +} + +static int TlsTicketIsValid(WOLFSSL* ssl, WolfSSL_ConstVector exts, + byte* isValid) +{ + WolfSSL_ConstVector tlsxSessionTicket; + byte tempTicket[SESSION_TICKET_LEN]; + InternalTicket* it; + int ret; + + *isValid = 0; + ret = TlsxFindByType(&tlsxSessionTicket, TLSX_SESSION_TICKET, exts); + if (ret != 0) + return ret; + if (tlsxSessionTicket.size == 0) + return 0; + if (tlsxSessionTicket.size > SESSION_TICKET_LEN) + return 0; + XMEMCPY(tempTicket, tlsxSessionTicket.elements, tlsxSessionTicket.size); + ret = DoDecryptTicket(ssl, tempTicket, (word32)tlsxSessionTicket.size, &it); + if (ret != WOLFSSL_TICKET_RET_OK && ret != WOLFSSL_TICKET_RET_CREATE) + return 0; + ForceZero(it, sizeof(InternalTicket)); + *isValid = 1; + return 0; +} +#endif /* HAVE_SESSION_TICKET */ + +static int TlsSessionIdIsValid(WOLFSSL* ssl, WolfSSL_ConstVector sessionID, + byte* isValid) +{ + WOLFSSL_SESSION* sess; + word32 sessRow; + int ret; + + *isValid = 0; + if (ssl->options.sessionCacheOff) + return 0; + if (sessionID.size != ID_LEN) + return 0; +#ifdef HAVE_EXT_CACHE + { + + if (ssl->ctx->get_sess_cb != NULL) { + int unused; + sess = + ssl->ctx->get_sess_cb(ssl, sessionID.elements, ID_LEN, &unused); + if (sess != NULL) { + *isValid = 1; + wolfSSL_FreeSession(ssl->ctx, sess); + return 0; + } + } + if (ssl->ctx->internalCacheLookupOff) + return 0; + } +#endif + ret = TlsSessionCacheGetAndLock(sessionID.elements, &sess, &sessRow); + if (ret == 0 && sess != NULL) { + *isValid = 1; + TlsSessionCacheUnlockRow(sessRow); + } + + return 0; +} + +static int TlsResumptionIsValid(WOLFSSL* ssl, WolfSSL_CH* ch, byte* isValid) +{ + int ret; + + *isValid = 0; +#ifdef HAVE_SESSION_TICKET + ret = TlsTicketIsValid(ssl, ch->extension, isValid); + if (ret != 0) + return ret; + if (*isValid) + return 0; +#endif /* HAVE_SESSION_TICKET */ + ret = TlsSessionIdIsValid(ssl, ch->sessionId, isValid); + return ret; +} +#endif /* WOLFSSL_DTLS_NO_HVR_ON_RESUME */ + int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz, byte* process) { @@ -205,6 +322,18 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ret = ParseClientHello(input + *inOutIdx, helloSz, &ch); if (ret != 0) return ret; + +#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME + { + byte isValid = 0; + ret = TlsResumptionIsValid(ssl, &ch, &isValid); + if (ret != 0) + return ret; + if (isValid) + return 0; + } +#endif /* WOLFSSL_DTLS_NO_HVR_ON_RESUME */ + ret = CreateDtlsCookie(ssl, &ch, cookie); if (ret != 0) return ret; diff --git a/src/internal.c b/src/internal.c index a45afa0d9..44640fbad 100644 --- a/src/internal.c +++ b/src/internal.c @@ -34281,12 +34281,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } - - /* Parse ticket sent by client, returns callback return value */ - int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len) + int DoDecryptTicket(WOLFSSL* ssl, const byte* input, word32 len, + InternalTicket **it) { ExternalTicket* et; - InternalTicket* it; int ret; int outLen; word16 inLen; @@ -34346,8 +34344,22 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_ERROR_VERBOSE(BAD_TICKET_KEY_CB_SZ); return BAD_TICKET_KEY_CB_SZ; } + *it = (InternalTicket*)et->enc_ticket; + return 0; + } - it = (InternalTicket*)et->enc_ticket; + /* Parse ticket sent by client, returns callback return value */ + int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len) + { + InternalTicket* it; + int ret; + + WOLFSSL_START(WC_FUNC_TICKET_DO); + WOLFSSL_ENTER("DoClientTicket"); + + ret = DoDecryptTicket(ssl, input, len, &it); + if (ret != 0) + return ret; #ifdef WOLFSSL_CHECK_MEM_ZERO /* Internal ticket successfully decrypted. */ wc_MemZero_Add("Do Client Ticket internal", it, sizeof(InternalTicket)); diff --git a/src/ssl.c b/src/ssl.c index 07bac5c3e..4686758b4 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -13838,15 +13838,63 @@ 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); +} + +int TlsSessionCacheGetAndLock(const byte *id, WOLFSSL_SESSION **sess, + word32 *lockedRow) +{ + SessionRow *sessRow; + 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 (SESSION_ROW_LOCK(sessRow) != 0) + return FATAL_ERROR; + + /* start from most recently used */ + count = 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) { + s = &sessRow->Sessions[idx]; + if (XMEMCMP(s->sessionID, id, ID_LEN) == 0) { + *sess = s; + break; + } + idx = idx > 0 ? idx - 1 : SESSIONS_PER_ROW - 1; + } + if (*sess == NULL) { + SESSION_ROW_UNLOCK(sessRow); + } + else { + *lockedRow = row; + } + + return 0; +} + int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) { WOLFSSL_SESSION* sess = NULL; const byte* id = NULL; word32 row; - int idx; - int count; int error = 0; - SessionRow* sessRow; #ifdef HAVE_SESSION_TICKET #ifndef WOLFSSL_SMALL_STACK byte tmpTicket[PREALLOC_SESSION_TICKET_LEN]; @@ -13936,13 +13984,6 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) } #endif - row = HashObject(id, ID_LEN, &error) % SESSION_ROWS; - if (error != 0) { - WOLFSSL_MSG("Hash session failed"); - return WOLFSSL_FAILURE; - } - - #ifdef HAVE_SESSION_TICKET if (output->ticket == NULL || output->ticketLenAlloc < PREALLOC_SESSION_TICKET_LEN) { @@ -13994,61 +14035,48 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) } #endif /* WOLFSSL_TLS13 && HAVE_SESSION_TICKET*/ - /* lock row */ - sessRow = &SessionCache[row]; - if (SESSION_ROW_LOCK(sessRow) != 0) { - WOLFSSL_MSG("Session cache row lock failure"); + /* init to avoid clang static analyzer false positive */ + row = 0; + error = TlsSessionCacheGetAndLock(id, &sess, &row); + 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) + if (preallocNonce != NULL) { XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK); + preallocNonce = NULL; + } #endif /* WOLFSSL_TLS13 */ #ifdef WOLFSSL_SMALL_STACK - if (tmpTicket != NULL) + if (tmpTicket != NULL) { XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER); -#endif -#endif - return WOLFSSL_FAILURE; - } - - /* start from most recently used */ - count = 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) { - WOLFSSL_SESSION* current; - - current = &sessRow->Sessions[idx]; - if (XMEMCMP(current->sessionID, id, ID_LEN) == 0 - && current->side == ssl->options.side - #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) - && (IsAtLeastTLSv1_3(ssl->version) == - IsAtLeastTLSv1_3(current->version)) - #endif - ) { - WOLFSSL_MSG("Found a session match"); - if (LowResTimer() < (current->bornOn + current->timeout)) { - WOLFSSL_MSG("Session valid"); - sess = current; - } else { - WOLFSSL_MSG("Session timed out"); - } - break; /* no more sessionIDs whether valid or not that match */ - } else { - WOLFSSL_MSG("SessionID not a match at this idx"); + tmpTicket = NULL; } - - idx = idx > 0 ? idx - 1 : SESSIONS_PER_ROW - 1; +#endif +#endif + } + else { +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) + if (IsAtLeastTLSv1_3(ssl->version) != IsAtLeastTLSv1_3(sess->version)) { + WOLFSSL_MSG("Invalid session: different protocol version"); + TlsSessionCacheUnlockRow(row); + error = WOLFSSL_FAILURE; + } + else if (LowResTimer() >= (sess->bornOn + sess->timeout)) { + WOLFSSL_MSG("Invalid session: timed out"); + TlsSessionCacheUnlockRow(row); + error = WOLFSSL_FAILURE; + } +#endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 */ } - if (sess != NULL) { + if (error == WOLFSSL_SUCCESS) { #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) /* We don't want the peer member. We will free it at the end. */ if (sess->peer != NULL) { @@ -14061,17 +14089,12 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) preallocNonce, &preallocNonceLen, &preallocNonceUsed); #else error = wolfSSL_DupSession(sess, output, 1); -#endif /* WOLFSSL_TSL */ - +#endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 */ #ifdef HAVE_EX_DATA output->ownExData = 0; /* Session cache owns external data */ #endif + TlsSessionCacheUnlockRow(row); } - else { - error = WOLFSSL_FAILURE; - } - - SESSION_ROW_UNLOCK(sessRow); /* We want to restore the bogus ID for TLS compatibility */ if (ssl->session->haveAltSessionID && diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ce8ceac69..0f3abf1b0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3922,6 +3922,9 @@ WOLFSSL_LOCAL ClientSession* AddSessionToClientCache(int side, int row, int idx, #endif WOLFSSL_LOCAL WOLFSSL_SESSION* ClientSessionToSession(const WOLFSSL_SESSION* session); +WOLFSSL_LOCAL void TlsSessionCacheUnlockRow(word32 row); +WOLFSSL_LOCAL int TlsSessionCacheGetAndLock(const byte *id, + WOLFSSL_SESSION **sess, word32 *lockedRow); /* WOLFSSL_API to test it in tests/api.c */ WOLFSSL_API int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output); WOLFSSL_LOCAL int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session); @@ -5480,6 +5483,10 @@ extern const WOLF_EC_NIST_NAME kNistCurves[]; /* internal functions */ WOLFSSL_LOCAL int SendChangeCipher(WOLFSSL* ssl); WOLFSSL_LOCAL int SendTicket(WOLFSSL* ssl); +#ifdef HAVE_SESSION_TICKET +WOLFSSL_LOCAL int DoDecryptTicket(WOLFSSL* ssl, const byte* input, word32 len, + InternalTicket **it); +#endif /* HAVE_SESSION_TICKET */ WOLFSSL_LOCAL int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len); WOLFSSL_LOCAL int SendData(WOLFSSL* ssl, const void* data, int sz); #ifdef WOLFSSL_TLS13