diff --git a/src/crypto.c b/src/crypto.c index c503db84..0a14f56a 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -3424,13 +3424,32 @@ CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, decDataLen = (word32)ulEncryptedDataLen; if (pData == NULL) { - *pulDataLen = decDataLen - 1; + /* decDataLen could be zero on an empty/invalid query; guard + * the underflow even though the decrypt itself would also + * reject it. */ + *pulDataLen = (decDataLen > 0) ? decDataLen - 1 : 0; return CKR_OK; } + /* Ciphertext must be at least one full block. */ + if (decDataLen < AES_BLOCK_SIZE) { + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + /* The actual plaintext size isn't known until padding is stripped, + * so pass the caller's real buffer capacity (*pulDataLen) as the + * output size. WP11_AesCbcPad_Decrypt validates each write against + * this capacity and returns BUFFER_E (mapped to + * CKR_BUFFER_TOO_SMALL below) rather than overflowing a buffer + * that is too small for the recovered plaintext. */ + decDataLen = (word32)*pulDataLen; ret = WP11_AesCbcPad_Decrypt(pEncryptedData, (int)ulEncryptedDataLen, pData, &decDataLen, session); + if (ret == BUFFER_E) { + /* Report required size; operation stays active for retry. */ + *pulDataLen = decDataLen; + return CKR_BUFFER_TOO_SMALL; + } if (ret < 0) break; *pulDataLen = decDataLen; @@ -3740,6 +3759,11 @@ CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, ret = WP11_AesCbcPad_DecryptUpdate(pEncryptedPart, (int)ulEncryptedPartLen, pPart, &decPartLen, session); + if (ret == BUFFER_E) { + /* Report required size; operation stays active for retry. */ + *pulPartLen = decPartLen; + return CKR_BUFFER_TOO_SMALL; + } if (ret < 0) { WP11_Session_SetOpInitialized(session, 0); return CKR_FUNCTION_FAILED; @@ -3912,6 +3936,11 @@ CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, return CKR_OK; ret = WP11_AesCbcPad_DecryptFinal(pLastPart, &decPartLen, session); + if (ret == BUFFER_E) { + /* Report required size; operation stays active for retry. */ + *pulLastPartLen = decPartLen; + return CKR_BUFFER_TOO_SMALL; + } if (ret < 0) { WP11_Session_SetOpInitialized(session, 0); return CKR_FUNCTION_FAILED; @@ -5237,6 +5266,10 @@ CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, #endif if (!WP11_Session_IsOpInitialized(session, WP11_INIT_TLS_MAC_SIGN)) return CKR_OPERATION_NOT_INITIALIZED; + if (!CK_ULONG_FITS_WORD32(ulPartLen)) { + WP11_Session_SetOpInitialized(session, 0); + return CKR_DATA_LEN_RANGE; + } ret = WP11_Session_UpdateData(session, pPart, (word32)ulPartLen); break; @@ -7041,6 +7074,19 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, derivedKeyLen = *(CK_ULONG*)lenAttr->pValue; if (derivedKeyLen == 0) return CKR_ATTRIBUTE_VALUE_INVALID; + /* WP11_PBKDF2 takes int lengths; reject caller-controlled CK_ULONG + * values that would silently truncate to word32 or become negative + * on cast to int (Fenrir F-4313 class). */ + if (!CK_ULONG_FITS_WORD32(pwLen) || + !CK_ULONG_FITS_WORD32(params->ulSaltSourceDataLen) || + !CK_ULONG_FITS_WORD32(params->iterations) || + !CK_ULONG_FITS_WORD32(derivedKeyLen) || + pwLen > (CK_ULONG)0x7FFFFFFF || + params->ulSaltSourceDataLen > (CK_ULONG)0x7FFFFFFF || + params->iterations > (CK_ULONG)0x7FFFFFFF || + derivedKeyLen > (CK_ULONG)0x7FFFFFFF) { + return CKR_MECHANISM_PARAM_INVALID; + } derivedKey = (CK_BYTE*)XMALLOC(derivedKeyLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -7147,6 +7193,17 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, iterationCount == 0) { return CKR_MECHANISM_PARAM_INVALID; } + /* wc_PKCS12_PBKDF takes int lengths; reject CK_ULONG values that + * would either silently truncate to word32 or become negative on + * cast to int. */ + if (!CK_ULONG_FITS_WORD32(passwordLen) || + !CK_ULONG_FITS_WORD32(saltLen) || + !CK_ULONG_FITS_WORD32(iterationCount) || + passwordLen > (CK_ULONG)0x7FFFFFFF || + saltLen > (CK_ULONG)0x7FFFFFFF || + iterationCount > (CK_ULONG)0x7FFFFFFF) { + return CKR_MECHANISM_PARAM_INVALID; + } derivedKey = (CK_BYTE*)XMALLOC(derivedKeyLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (derivedKey == NULL) @@ -9166,6 +9223,38 @@ CK_RV C_MessageVerifyFinal(CK_SESSION_HANDLE hSession) #if defined (WOLFPKCS11_PKCS11_V3_2) +#ifdef WOLFPKCS11_MLKEM +/* + * PKCS#11 v3.0 sec 5.1: an object whose effective CKA_PRIVATE is CK_TRUE must + * not be created on a session that is not logged in as the user. Empty-PIN + * tokens treat public sessions as logged in (mirrors the find-time gate in + * WP11_Object_Find). Returns CKR_USER_NOT_LOGGED_IN when the template asks for + * a private object on a public session, otherwise CKR_OK. + * + * Only used by C_EncapsulateKey / C_DecapsulateKey, which are themselves + * compiled out without WOLFPKCS11_MLKEM; guard the definition the same way to + * avoid an unused-function error under -Werror. + */ +static CK_RV CheckPrivateObjectLogin(WP11_Session* session, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount) +{ + CK_ATTRIBUTE* privAttr = NULL; + + FindAttributeType(pTemplate, ulAttributeCount, CKA_PRIVATE, &privAttr); + if (privAttr != NULL && privAttr->pValue != NULL && + privAttr->ulValueLen == sizeof(CK_BBOOL) && + *(CK_BBOOL*)privAttr->pValue == CK_TRUE) { + WP11_Slot* slot = WP11_Session_GetSlot(session); + if (!WP11_Slot_Has_Empty_Pin(slot) && !WP11_Slot_IsLoggedIn(slot)) { + return CKR_USER_NOT_LOGGED_IN; + } + } + + return CKR_OK; +} +#endif /* WOLFPKCS11_MLKEM */ + CK_RV C_EncapsulateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hPublicKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_BYTE_PTR pCiphertext, @@ -9243,6 +9332,9 @@ CK_RV C_EncapsulateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, if (rv == CKR_OK && pCiphertext == NULL) return CKR_OK; + if (rv == CKR_OK) + rv = CheckPrivateObjectLogin(session, pTemplate, ulAttributeCount); + if (rv == CKR_OK) { rv = CreateObject(session, pTemplate, ulAttributeCount, &secretObj); if (rv == CKR_OK) { @@ -9354,6 +9446,9 @@ CK_RV C_DecapsulateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, return CKR_MECHANISM_INVALID; } + if (rv == CKR_OK) + rv = CheckPrivateObjectLogin(session, pTemplate, ulAttributeCount); + if (rv == CKR_OK) { rv = CreateObject(session, pTemplate, ulAttributeCount, &secretObj); if (rv == CKR_OK) { diff --git a/src/internal.c b/src/internal.c index 3bc8ecfa..e8a5a9dd 100644 --- a/src/internal.c +++ b/src/internal.c @@ -614,6 +614,26 @@ static int libraryInitCount = 0; /* Lock for globals including global random. */ static WP11_Lock globalLock; +#if !defined(SINGLE_THREADED) && defined(WOLFSSL_MUTEX_INITIALIZER) && \ + defined(WOLFSSL_MUTEX_INITIALIZER_CLAUSE) +/* Permanently-live mutex that serializes WP11_Library_Init, + * WP11_Library_Final, and WP11_Library_IsInitialized. Needed because + * globalLock above is created inside Init and destroyed inside Final, so + * concurrent C_Initialize / C_Finalize / any-C_-call would otherwise race + * on globalLock's lifetime (Fenrir F-4798, F-4799). Static init avoids the + * chicken-and-egg of needing a lock to protect lock creation. + * + * WOLFSSL_MUTEX_INITIALIZER_CLAUSE expands to "= WOLFSSL_MUTEX_INITIALIZER(...)" + * on builds that support a static mutex initializer. Older wolfSSL (e.g. + * v5.6.6) exposes only the object-like WOLFSSL_MUTEX_INITIALIZER with no + * lockname argument and lacks the _CLAUSE wrapper; gating on _CLAUSE keeps + * those builds on the non-static fallback path below rather than failing to + * compile. */ +static wolfSSL_Mutex libraryInitLock + WOLFSSL_MUTEX_INITIALIZER_CLAUSE(libraryInitLock); +#define WP11_HAVE_LIBRARY_INIT_LOCK +#endif + #ifndef SINGLE_THREADED /** @@ -6590,6 +6610,15 @@ int WP11_Library_Init(void) int ret = 0; int i; +#ifdef WP11_HAVE_LIBRARY_INIT_LOCK + /* Serialize the entire init sequence: both the libraryInitCount==0 + * check and globalLock construction must happen under a permanently + * live mutex so two concurrent C_Initialize calls don't both enter + * the construction branch. */ + if (wc_LockMutex(&libraryInitLock) != 0) + return BAD_MUTEX_E; +#endif + if (libraryInitCount == 0) { ret = WP11_Lock_Init(&globalLock); if (ret == 0) { @@ -6629,6 +6658,10 @@ int WP11_Library_Init(void) WP11_Lock_UnlockRW(&globalLock); } +#ifdef WP11_HAVE_LIBRARY_INIT_LOCK + wc_UnLockMutex(&libraryInitLock); +#endif + return ret; } @@ -6641,6 +6674,14 @@ void WP11_Library_Final(void) int i; int cnt; +#ifdef WP11_HAVE_LIBRARY_INIT_LOCK + /* Hold the init lock across the count decrement and the conditional + * WP11_Lock_Free so a racing WP11_Library_IsInitialized can't observe + * count>0, then call WP11_Lock_LockRO on a freed globalLock. */ + if (wc_LockMutex(&libraryInitLock) != 0) + return; +#endif + WP11_Lock_LockRW(&globalLock); cnt = --libraryInitCount; WP11_Lock_UnlockRW(&globalLock); @@ -6669,6 +6710,10 @@ void WP11_Library_Final(void) WP11_Lock_Free(&globalLock); wolfCrypt_Cleanup(); } + +#ifdef WP11_HAVE_LIBRARY_INIT_LOCK + wc_UnLockMutex(&libraryInitLock); +#endif } /** @@ -6679,7 +6724,25 @@ void WP11_Library_Final(void) */ int WP11_Library_IsInitialized(void) { - int ret, locked = 0; + int ret; +#ifdef WP11_HAVE_LIBRARY_INIT_LOCK + /* Read libraryInitCount and lock globalLock under the init mutex so a + * racing WP11_Library_Final can't free globalLock between the count + * check and WP11_Lock_LockRO. */ + if (wc_LockMutex(&libraryInitLock) != 0) + return 0; + if (libraryInitCount > 0) { + WP11_Lock_LockRO(&globalLock); + ret = libraryInitCount > 0; + WP11_Lock_UnlockRO(&globalLock); + } + else { + ret = 0; + } + wc_UnLockMutex(&libraryInitLock); + return ret; +#else + int locked = 0; if (libraryInitCount > 0) { /* cannot used globalLock before init */ WP11_Lock_LockRO(&globalLock); @@ -6690,6 +6753,7 @@ int WP11_Library_IsInitialized(void) WP11_Lock_UnlockRO(&globalLock); } return ret; +#endif } /** @@ -6834,10 +6898,25 @@ int WP11_Slot_OpenSession(WP11_Slot* slot, unsigned long flags, void* app, void WP11_Slot_CloseSession(WP11_Slot* slot, WP11_Session* session) { int dynamic; + int stillLinked = 0; int noMore = 1; WP11_Session* curr; WP11_Lock_LockRW(&slot->lock); + /* Two threads holding the same session pointer (e.g. two C_CloseSession + * calls with the same handle) can both pass the lock-free WP11_Session_Get + * lookup. Re-validate inside the slot lock that the session is still in + * the list before doing anything with it. */ + for (curr = slot->session; curr != NULL; curr = curr->next) { + if (curr == session) { + stillLinked = 1; + break; + } + } + if (!stillLinked) { + WP11_Lock_UnlockRW(&slot->lock); + return; + } /* Only free the session object if it is on top and there is more than the * minimum number of sessions associated with the slot. */ @@ -6871,12 +6950,16 @@ void WP11_Slot_CloseSessions(WP11_Slot* slot) { WP11_Session* curr; + /* Hold the slot lock across the entire walk so a concurrent + * C_OpenSession / C_CloseSession can't mutate slot->session between + * the NULL test and the free, or unlink the head we're about to free. + */ + WP11_Lock_LockRW(&slot->lock); /* Free all sessions down to minimum. */ while (slot->session != NULL && SESS_HANDLE_SESS_ID(slot->session->handle) > WP11_SESSION_CNT_MIN) { wp11_Slot_FreeSession(slot, slot->session); } - WP11_Lock_LockRW(&slot->lock); /* Finalize the rest. */ for (curr = slot->session; curr != NULL; curr = curr->next) wp11_Session_Final(curr); @@ -7050,6 +7133,76 @@ int WP11_Slot_CheckSOPin(WP11_Slot* slot, char* pin, int pinLen) return ret; } +/** + * Check the SO PIN with the failed-login lockout applied, but without logging + * in. C_InitToken must verify the SO PIN before wiping the token, and the + * verification needs the same WP11_MAX_LOGIN_FAILS_SO brute-force lockout that + * WP11_Slot_SOLogin enforces (Fenrir F-4632). Unlike WP11_Slot_SOLogin this + * does NOT set loginState or reject open read-only sessions: C_InitToken is not + * a login and re-initializes the token immediately afterwards, so those side + * effects would be wrong here. + * + * When WOLFPKCS11_NO_TIME is defined there is no lockout state to maintain and + * this collapses to a plain WP11_Slot_CheckSOPin, matching the historical + * behaviour exactly. + * + * @param slot [in] Slot object. + * @param pin [in] PIN to check. + * @param pinLen [in] Length of PIN. + * @return PIN_NOT_SET_E when the token is not initialized. + * PIN_INVALID_E when the PIN is not correct or the SO is locked out. + * Other -ve value when hashing PIN fails. + * 0 when PIN is correct. + */ +int WP11_Slot_CheckSOPinLockout(WP11_Slot* slot, char* pin, int pinLen) +{ + int ret; +#ifndef WOLFPKCS11_NO_TIME + time_t now; + time_t allowed; + + if (wc_GetTime(&now, sizeof(now)) != 0) + return PIN_INVALID_E; + + /* Check for too many fails and whether the timeout has elapsed. */ + WP11_Lock_LockRW(&slot->lock); + if (slot->token.soFailedLogin == WP11_MAX_LOGIN_FAILS_SO) { + allowed = slot->token.soLastFailedLogin + + slot->token.soFailLoginTimeout; + if (allowed < now) + slot->token.soFailedLogin = 0; + else { + WP11_Lock_UnlockRW(&slot->lock); + return PIN_INVALID_E; + } + } + WP11_Lock_UnlockRW(&slot->lock); +#endif + + ret = WP11_Slot_CheckSOPin(slot, pin, pinLen); + +#ifndef WOLFPKCS11_NO_TIME + WP11_Lock_LockRW(&slot->lock); + /* PIN failed - update failure info. */ + if (ret == PIN_INVALID_E) { + slot->token.soFailedLogin++; + if (slot->token.soFailedLogin == WP11_MAX_LOGIN_FAILS_SO) { + slot->token.soLastFailedLogin = now; + slot->token.soFailLoginTimeout += WP11_SO_LOGIN_FAIL_TIMEOUT; + } + } + /* Worked - clear failure info. */ + else if (ret == 0) { + slot->token.soFailedLogin = 0; + slot->token.soLastFailedLogin = 0; + slot->token.soFailLoginTimeout = 0; + } + WP11_Lock_UnlockRW(&slot->lock); +#endif + + return ret; +} + /** * Check the PIN is correct for user. * @@ -7705,6 +7858,11 @@ int WP11_Session_UpdateData(WP11_Session *session, byte *data, word32 dataLen) int ret = 0; byte* tmp; + /* Guard the cumulative word32 sum against silent wrap that would lead to + * a tiny allocation followed by an oversized XMEMCPY past it. */ + if (dataLen > (word32)0xFFFFFFFFu - session->dataSz) + return MEMORY_E; + #ifdef XREALLOC tmp = (byte*)XREALLOC(session->data, session->dataSz + dataLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -9816,6 +9974,18 @@ int WP11_Object_Find(WP11_Session* session, CK_OBJECT_HANDLE objHandle, int onToken = OBJ_HANDLE_ON_TOKEN(objHandle); if (!onToken) { +#ifdef WOLFPKCS11_NSS + /* The NSS cross-session walk below needs the slot lock so a concurrent + * remove-session can't reclaim a session node. Acquire it before the + * token lock to match the global lock order (slot then token) used by + * WP11_Session_RemoveObject / WP11_Slot_CloseSessions; acquiring them + * in the opposite order would risk an AB-BA deadlock. */ + WP11_Lock_LockRO(&session->slot->lock); +#endif + /* Hold the token lock across the session->object walk so a concurrent + * WP11_Session_RemoveObject (which holds the same lock around the + * unlink) can't free a node we're stepping through. */ + WP11_Lock_LockRO(&session->slot->token.lock); obj = session->object; while (obj != NULL) { if (obj->handle == objHandle) { @@ -9826,10 +9996,6 @@ int WP11_Object_Find(WP11_Session* session, CK_OBJECT_HANDLE objHandle, } #ifdef WOLFPKCS11_NSS if (ret == BAD_FUNC_ARG) { - /* Token lock is needed in case remove object is run, slot lock is - * needed in case remove session is run */ - WP11_Lock_LockRO(&session->slot->lock); - WP11_Lock_LockRO(&session->slot->token.lock); for (scan = session->slot->session; scan != NULL && ret != 0; scan = scan->next) { if (scan == session) @@ -9841,9 +10007,11 @@ int WP11_Object_Find(WP11_Session* session, CK_OBJECT_HANDLE objHandle, } } } - WP11_Lock_UnlockRO(&session->slot->token.lock); - WP11_Lock_UnlockRO(&session->slot->lock); } +#endif + WP11_Lock_UnlockRO(&session->slot->token.lock); +#ifdef WOLFPKCS11_NSS + WP11_Lock_UnlockRO(&session->slot->lock); #endif } else { @@ -10294,8 +10462,11 @@ static int GetEcPoint(ecc_key* key, byte* data, CK_ULONG* len) if (data == NULL) *len = dataLen + 2 + longLen; - else if (*len < (CK_ULONG)dataLen) + else if (*len < (CK_ULONG)(dataLen + 2 + longLen)) { + /* Report the required size so the caller can resize and retry. */ + *len = dataLen + 2 + longLen; ret = BUFFER_E; + } else { *len = dataLen + 2 + longLen; i = 0; @@ -13759,8 +13930,16 @@ int WP11_Tls12_Master_Key_Derive(CK_SSL3_RANDOM_DATA* random, return BAD_FUNC_ARG; } + /* Reject CK_ULONG additions that wrap or that wouldn't fit word32 when + * later passed to wc_PRF_TLS. TLS 1.2 random length is fixed at 32 bytes + * per RFC 5246, but the PKCS#11 API accepts arbitrary CK_ULONG lengths + * from the caller, so guard explicitly. */ + if (random->ulClientRandomLen > + (CK_ULONG)0xFFFFFFFF - random->ulServerRandomLen) { + return CKR_MECHANISM_PARAM_INVALID; + } ulSeedLen = random->ulClientRandomLen + random->ulServerRandomLen; - if (ulSeedLen == 0) { + if (ulSeedLen == 0 || ulSeedLen > (CK_ULONG)0xFFFFFFFF) { return CKR_MECHANISM_PARAM_INVALID; } pSeed = (byte*)XMALLOC(ulSeedLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -14147,10 +14326,17 @@ int WP11_AesCbcPad_Decrypt(unsigned char* enc, word32 encSz, unsigned char* dec, if (ret == 0) { finalSz = *decSz - sz; ret = WP11_AesCbcPad_DecryptFinal(dec + sz, &finalSz, session); - if (ret == 0) { + if (ret == 0 || ret == BUFFER_E) { + /* On success this is the plaintext length; on BUFFER_E it is the + * total required size (data already produced plus the final + * block's need) for the caller to resize and retry. */ *decSz = sz + finalSz; } } + else if (ret == BUFFER_E) { + /* Update set sz to the size it needs. */ + *decSz = sz; + } return ret; } @@ -14174,6 +14360,7 @@ int WP11_AesCbcPad_DecryptUpdate(unsigned char* enc, word32 encSz, { int ret = 0; WP11_CbcParams* cbc = &session->params.cbc; + word32 bufSz = *decSz; int sz = 0; int outSz = 0; @@ -14186,6 +14373,12 @@ int WP11_AesCbcPad_DecryptUpdate(unsigned char* enc, word32 encSz, enc += sz; encSz -= sz; if (cbc->partialSz == AES_BLOCK_SIZE && encSz > 0) { + /* Refuse to overflow caller's buffer; report the size needed so + * far and leave the operation active (CKR_BUFFER_TOO_SMALL). */ + if ((word32)(outSz + AES_BLOCK_SIZE) > bufSz) { + *decSz = (word32)outSz + AES_BLOCK_SIZE; + return BUFFER_E; + } ret = wc_AesCbcDecrypt(&cbc->aes, dec, cbc->partial, AES_BLOCK_SIZE); dec += AES_BLOCK_SIZE; @@ -14197,6 +14390,10 @@ int WP11_AesCbcPad_DecryptUpdate(unsigned char* enc, word32 encSz, sz = encSz - (encSz & (AES_BLOCK_SIZE - 1)); if (sz == (int)encSz) sz -= AES_BLOCK_SIZE; + if ((word32)(outSz + sz) > bufSz) { + *decSz = (word32)(outSz + sz); + return BUFFER_E; + } ret = wc_AesCbcDecrypt(&cbc->aes, dec, enc, sz); outSz += sz; enc += sz; @@ -14258,6 +14455,16 @@ int WP11_AesCbcPad_DecryptFinal(unsigned char* dec, word32* decSz, } if (ret == 0) { outSz = AES_BLOCK_SIZE - (padCnt & (0 - (padCnt <= AES_BLOCK_SIZE))); + /* Refuse to overflow caller's buffer. Output size is 0..15 bytes; + * caller passes the remaining capacity in *decSz. On a too-small + * buffer report the required size and leave the operation active, per + * the PKCS#11 CKR_BUFFER_TOO_SMALL contract; the AES context is + * released when the operation is reinitialised or the session closes. + * A caller that first queried the output size never reaches this. */ + if ((word32)outSz > *decSz) { + *decSz = outSz; + return BUFFER_E; + } for (i = 0; i < AES_BLOCK_SIZE; i++) { mask = (size_t)0 - (i != outSz); p = (unsigned char*)((size_t)p & mask); diff --git a/src/slot.c b/src/slot.c index 634d5aef..6efc8dc9 100644 --- a/src/slot.c +++ b/src/slot.c @@ -1290,7 +1290,12 @@ CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, return rv; } if (WP11_Slot_SOPin_IsSet(slot)) { - ret = WP11_Slot_CheckSOPin(slot, (char*)pPin, (int)ulPinLen); + /* Verify the SO PIN with the failed-login lockout applied, so this + * path cannot be used to brute-force the SO PIN (Fenrir F-4632). + * Use the lockout check rather than WP11_Slot_SOLogin: C_InitToken + * must not log in or set loginState, and it rejects open sessions + * earlier so the SOLogin read-only-session check is not wanted. */ + ret = WP11_Slot_CheckSOPinLockout(slot, (char*)pPin, (int)ulPinLen); if (ret != 0) { rv = CKR_PIN_INCORRECT; WOLFPKCS11_LEAVE("C_InitToken", rv); diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 299f6aed..30ba77a7 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -366,6 +366,8 @@ WP11_LOCAL void WP11_Slot_CloseSession(WP11_Slot* slot, WP11_Session* session); WP11_LOCAL void WP11_Slot_CloseSessions(WP11_Slot* slot); WP11_LOCAL int WP11_Slot_HasSession(WP11_Slot* slot); WP11_LOCAL int WP11_Slot_CheckSOPin(WP11_Slot* slot, char* pin, int pinLen); +WP11_LOCAL int WP11_Slot_CheckSOPinLockout(WP11_Slot* slot, char* pin, + int pinLen); WP11_LOCAL int WP11_Slot_CheckUserPin(WP11_Slot* slot, char* pin, int pinLen); WP11_LOCAL int WP11_Slot_Has_Empty_Pin(WP11_Slot* slot); WP11_LOCAL int WP11_Slot_SOPin_IsSet(WP11_Slot* slot);