From 1a499d6b1da1a405e2793bfb8b0c33b47c54d0f4 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Thu, 14 Aug 2025 09:49:25 +0300 Subject: [PATCH] Logout PKCS11 session after successful sign and dont leak session IB-8561 Signed-off-by: Raul Metsma --- src/crypto/PKCS11Signer.cpp | 157 ++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 61 deletions(-) diff --git a/src/crypto/PKCS11Signer.cpp b/src/crypto/PKCS11Signer.cpp index 6ea693b7f..8c0ed56e1 100644 --- a/src/crypto/PKCS11Signer.cpp +++ b/src/crypto/PKCS11Signer.cpp @@ -25,12 +25,12 @@ #include "crypto/Digest.h" #include "crypto/X509Cert.h" #include "crypto/X509Crypto.h" +#include "util/algorithm.h" #include "util/log.h" #include "util/File.h" #include -#include #ifdef _WIN32 #include #else @@ -43,9 +43,6 @@ using namespace std; class PKCS11Signer::Private { public: - vector attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const; - vector findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const vector &id = {}) const; - #ifdef _WIN32 bool load(const string &driver) { @@ -80,25 +77,74 @@ class PKCS11Signer::Private struct SignSlot { X509Cert certificate; - CK_SLOT_ID slot; + CK_SLOT_ID slot{}; vector id; - } sign { X509Cert(), 0, {} }; + } sign; string pin; }; -vector PKCS11Signer::Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const +template struct function_traits; +template +struct function_traits +{ + using return_type = R; + using arg_types = std::tuple; + static constexpr std::size_t nargs = sizeof...(Ps); +}; + +template +static auto PKCS11List(CK_FUNCTION_LIST *f, Args&&... args) +{ + using Traits = function_traits; + using T = std::remove_pointer_t>; + std::vector result; + CK_ULONG count = 0; + if((f->*Getter)(std::forward(args)..., static_cast(nullptr), &count) != CKR_OK || count == 0) + return result; + result.resize(size_t(count)); + if((f->*Getter)(std::forward(args)..., result.data(), &count) != CKR_OK) + result.clear(); + return result; +} + +struct PKCS11Session { + PKCS11Session(CK_FUNCTION_LIST *_f, CK_SLOT_ID slot) + : f(_f) + { + f->C_OpenSession(slot, CKF_SERIAL_SESSION, nullptr, nullptr, &handle); + } + + DISABLE_COPY(PKCS11Session); + + ~PKCS11Session() + { + if(handle) + f->C_CloseSession(handle); + } + + vector attribute(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const; + vector findObject(CK_OBJECT_CLASS cls, const vector &id = {}) const; + operator bool() const { return handle != CK_INVALID_HANDLE; } + + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE handle = CK_INVALID_HANDLE; +}; + +vector PKCS11Session::attribute(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const +{ + vector value; CK_ATTRIBUTE attr { type, nullptr, 0 }; - if(f->C_GetAttributeValue(session, obj, &attr, 1) != CKR_OK) - return {}; - vector value(size_t(attr.ulValueLen)); + if(f->C_GetAttributeValue(handle, obj, &attr, 1) != CKR_OK) + return value; + value.resize(size_t(attr.ulValueLen)); attr.pValue = value.data(); - if(f->C_GetAttributeValue(session, obj, &attr, 1) != CKR_OK) - return {}; + if(f->C_GetAttributeValue(handle, obj, &attr, 1) != CKR_OK) + value.clear(); return value; } -vector PKCS11Signer::Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const vector &id) const +vector PKCS11Session::findObject(CK_OBJECT_CLASS cls, const vector &id) const { CK_BBOOL _true = CK_TRUE; vector attrs { @@ -107,14 +153,14 @@ vector PKCS11Signer::Private::findObject(CK_SESSION_HANDLE ses }; if(!id.empty()) attrs.push_back({ CKA_ID, CK_VOID_PTR(id.data()), CK_ULONG(id.size()) }); - if(f->C_FindObjectsInit(session, attrs.data(), CK_ULONG(attrs.size())) != CKR_OK) + if(f->C_FindObjectsInit(handle, attrs.data(), CK_ULONG(attrs.size())) != CKR_OK) return {}; CK_ULONG count = 32; vector result(count); - CK_RV err = f->C_FindObjects(session, result.data(), CK_ULONG(result.size()), &count); + CK_RV err = f->C_FindObjects(handle, result.data(), CK_ULONG(result.size()), &count); result.resize(err == CKR_OK ? count : 0); - f->C_FindObjectsFinal(session); + f->C_FindObjectsFinal(handle); return result; } @@ -195,38 +241,29 @@ X509Cert PKCS11Signer::cert() const return d->sign.certificate; // Load all slots. - CK_ULONG size = 0; - if(d->f->C_GetSlotList(true, nullptr, &size) != CKR_OK) - THROW("Could not find any ID-Cards in any readers"); - vector slots(size); - if(size && d->f->C_GetSlotList(true, slots.data(), &size) != CKR_OK) + auto slots = PKCS11List<&CK_FUNCTION_LIST::C_GetSlotList>(d->f, CK_TRUE); + if(slots.empty()) THROW("Could not find any ID-Cards in any readers"); // Iterate over all found slots, if the slot has a token, check if the token has any certificates. - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; vector certificates; vector certSlotMapping; for(const CK_SLOT_ID &slot: slots) { - if(session) - d->f->C_CloseSession(session); - if(d->f->C_OpenSession(slot, CKF_SERIAL_SESSION, nullptr, nullptr, &session) != CKR_OK) - continue; - for(CK_OBJECT_HANDLE obj: d->findObject(session, CKO_CERTIFICATE)) + for(PKCS11Session session(d->f, slot); + CK_OBJECT_HANDLE obj: session.findObject(CKO_CERTIFICATE)) { - X509Cert x509(d->attribute(session, obj, CKA_VALUE)); + X509Cert x509(session.attribute(obj, CKA_VALUE)); vector usage = x509.keyUsage(); - if(!x509.isValid() || find(usage.cbegin(), usage.cend(), X509Cert::NonRepudiation) == usage.cend() || x509.isCA()) + if(!x509.isValid() || !contains(usage, X509Cert::NonRepudiation) || x509.isCA()) continue; - vector id = d->attribute(session, obj, CKA_ID); - if(d->findObject(session, CKO_PUBLIC_KEY, id).empty()) + vector id = session.attribute(obj, CKA_ID); + if(session.findObject(CKO_PUBLIC_KEY, id).empty()) continue; certSlotMapping.push_back({x509, slot, std::move(id)}); certificates.push_back(std::move(x509)); } } - if(session) - d->f->C_CloseSession(session); if(certificates.empty()) THROW("No certificates found."); @@ -237,10 +274,10 @@ X509Cert PKCS11Signer::cert() const THROW("No certificate selected."); // Find the corresponding slot and PKCS11 certificate struct. - for(const Private::SignSlot &slot: certSlotMapping) + for(Private::SignSlot &slot: certSlotMapping) { if(slot.certificate == selectedCert) - d->sign = slot; + d->sign = std::move(slot); } if(!d->sign.certificate) @@ -255,13 +292,8 @@ string PKCS11Signer::method() const if(!d->sign.certificate || !X509Crypto(d->sign.certificate).isRSAKey() || parent != CONF(signatureDigestUri)) return parent; - CK_ULONG count = 0; - CK_RV rv = d->f->C_GetMechanismList(d->sign.slot, nullptr, &count); - if(rv != CKR_OK) - return parent; - vector mech(count); - rv = d->f->C_GetMechanismList(d->sign.slot, mech.data(), &count); - if(find(mech.cbegin(), mech.cend(), CKM_RSA_PKCS_PSS) != mech.cend()) + if(auto mech = PKCS11List<&CK_FUNCTION_LIST::C_GetMechanismList>(d->f, d->sign.slot); + contains(mech, CKM_RSA_PKCS_PSS)) return Digest::toRsaPssUri(std::move(parent)); return parent; } @@ -323,26 +355,33 @@ vector PKCS11Signer::sign(const string &method, const vectorsign.certificate) - THROW("Signing slot or certificate are not selected."); + THROW("Signing certificate is not selected."); - // Login if required. CK_TOKEN_INFO token{}; - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; - if(d->f->C_GetTokenInfo(d->sign.slot, &token) != CKR_OK || - d->f->C_OpenSession(d->sign.slot, CKF_SERIAL_SESSION, nullptr, nullptr, &session) != CKR_OK) - THROW("Signing slot or certificate are not selected."); + if(d->f->C_GetTokenInfo(d->sign.slot, &token) != CKR_OK) + THROW("Failed to get token info."); + + if(token.flags & CKF_USER_PIN_LOCKED) + { + Exception e(EXCEPTION_PARAMS("PIN Locked")); + e.setCode(Exception::PINLocked); + throw e; + } + + PKCS11Session session(d->f, d->sign.slot); + if(!session) + THROW("Failed to open session."); CK_RV rv = CKR_OK; if(token.flags & CKF_LOGIN_REQUIRED) { if(token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) - rv = d->f->C_Login(session, CKU_USER, nullptr, 0); + rv = d->f->C_Login(session.handle, CKU_USER, nullptr, 0); else { string _pin = pin(d->sign.certificate); - rv = d->f->C_Login(session, CKU_USER, CK_BYTE_PTR(_pin.c_str()), CK_ULONG(_pin.size())); + rv = d->f->C_Login(session.handle, CKU_USER, CK_BYTE_PTR(_pin.c_str()), CK_ULONG(_pin.size())); } switch(rv) { @@ -374,14 +413,14 @@ vector PKCS11Signer::sign(const string &method, const vector key = d->findObject(session, CKO_PRIVATE_KEY, d->sign.id); + vector key = session.findObject(CKO_PRIVATE_KEY, d->sign.id); if(key.size() != 1) THROW("Could not get key that matches selected certificate."); // Sign the digest. CK_KEY_TYPE keyType = CKK_RSA; CK_ATTRIBUTE attribute { CKA_KEY_TYPE, &keyType, sizeof(keyType) }; - d->f->C_GetAttributeValue(session, key.front(), &attribute, 1); + d->f->C_GetAttributeValue(session.handle, key.front(), &attribute, 1); CK_RSA_PKCS_PSS_PARAMS pssParams { CKM_SHA_1, CKG_MGF1_SHA1, 0 }; CK_MECHANISM mech { keyType == CKK_ECDSA ? CKM_ECDSA : CKM_RSA_PKCS, nullptr, 0 }; @@ -409,16 +448,12 @@ vector PKCS11Signer::sign(const string &method, const vectorf->C_SignInit(session, &mech, key.front()) != CKR_OK) - THROW("Failed to sign digest"); - - CK_ULONG size = 0; - if(d->f->C_Sign(session, data.data(), CK_ULONG(data.size()), nullptr, &size) != CKR_OK) + if(d->f->C_SignInit(session.handle, &mech, key.front()) != CKR_OK) THROW("Failed to sign digest"); - vector signature(size); - rv = d->f->C_Sign(session, data.data(), CK_ULONG(data.size()), signature.data(), CK_ULONG_PTR(&size)); - if(rv != CKR_OK) + auto signature = PKCS11List<&CK_FUNCTION_LIST::C_Sign>(d->f, session.handle, data.data(), CK_ULONG(data.size())); + d->f->C_Logout(session.handle); + if(signature.empty()) THROW("Failed to sign digest"); return signature; }