Skip to content

Commit 2c0e74e

Browse files
committed
Logout PKCS11 session after successful sign and dont leak session
IB-8561 Signed-off-by: Raul Metsma <[email protected]>
1 parent 9491bcc commit 2c0e74e

File tree

1 file changed

+96
-61
lines changed

1 file changed

+96
-61
lines changed

src/crypto/PKCS11Signer.cpp

Lines changed: 96 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
#include "crypto/Digest.h"
2626
#include "crypto/X509Cert.h"
2727
#include "crypto/X509Crypto.h"
28+
#include "util/algorithm.h"
2829
#include "util/log.h"
2930
#include "util/File.h"
3031

3132
#include <openssl/evp.h>
3233

33-
#include <algorithm>
3434
#ifdef _WIN32
3535
#include <Windows.h>
3636
#else
@@ -43,9 +43,6 @@ using namespace std;
4343
class PKCS11Signer::Private
4444
{
4545
public:
46-
vector<CK_BYTE> attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const;
47-
vector<CK_OBJECT_HANDLE> findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const vector<CK_BYTE> &id = {}) const;
48-
4946
#ifdef _WIN32
5047
bool load(const string &driver)
5148
{
@@ -80,25 +77,74 @@ class PKCS11Signer::Private
8077
struct SignSlot
8178
{
8279
X509Cert certificate;
83-
CK_SLOT_ID slot;
80+
CK_SLOT_ID slot{};
8481
vector<CK_BYTE> id;
85-
} sign { X509Cert(), 0, {} };
82+
} sign;
8683
string pin;
8784
};
8885

89-
vector<CK_BYTE> PKCS11Signer::Private::attribute(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const
86+
template<typename> struct function_traits;
87+
template<typename R, typename C, typename... Ps>
88+
struct function_traits<R (*C::*)(Ps...)>
89+
{
90+
using return_type = R;
91+
using arg_types = std::tuple<Ps...>;
92+
static constexpr std::size_t nargs = sizeof...(Ps);
93+
};
94+
95+
template<auto Getter, typename... Args>
96+
static auto PKCS11List(CK_FUNCTION_LIST *f, Args&&... args)
97+
{
98+
using Traits = function_traits<decltype(Getter)>;
99+
using T = std::remove_pointer_t<std::tuple_element_t<Traits::nargs - 2, typename Traits::arg_types>>;
100+
std::vector<T> result;
101+
CK_ULONG count = 0;
102+
if((f->*Getter)(std::forward<Args>(args)..., static_cast<T*>(nullptr), &count) != CKR_OK || count == 0)
103+
return result;
104+
result.resize(size_t(count));
105+
if((f->*Getter)(std::forward<Args>(args)..., result.data(), &count) != CKR_OK)
106+
result.clear();
107+
return result;
108+
}
109+
110+
struct PKCS11Session
90111
{
112+
PKCS11Session(CK_FUNCTION_LIST *_f, CK_SLOT_ID slot)
113+
: f(_f)
114+
{
115+
f->C_OpenSession(slot, CKF_SERIAL_SESSION, nullptr, nullptr, &handle);
116+
}
117+
118+
DISABLE_COPY(PKCS11Session);
119+
120+
~PKCS11Session()
121+
{
122+
if(handle)
123+
f->C_CloseSession(handle);
124+
}
125+
126+
vector<CK_BYTE> attribute(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const;
127+
vector<CK_OBJECT_HANDLE> findObject(CK_OBJECT_CLASS cls, const vector<CK_BYTE> &id = {}) const;
128+
operator bool() const { return handle != CK_INVALID_HANDLE; }
129+
130+
CK_FUNCTION_LIST *f;
131+
CK_SESSION_HANDLE handle = CK_INVALID_HANDLE;
132+
};
133+
134+
vector<CK_BYTE> PKCS11Session::attribute(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type) const
135+
{
136+
vector<CK_BYTE> value;
91137
CK_ATTRIBUTE attr { type, nullptr, 0 };
92-
if(f->C_GetAttributeValue(session, obj, &attr, 1) != CKR_OK)
93-
return {};
94-
vector<CK_BYTE> value(size_t(attr.ulValueLen));
138+
if(f->C_GetAttributeValue(handle, obj, &attr, 1) != CKR_OK)
139+
return value;
140+
value.resize(size_t(attr.ulValueLen));
95141
attr.pValue = value.data();
96-
if(f->C_GetAttributeValue(session, obj, &attr, 1) != CKR_OK)
97-
return {};
142+
if(f->C_GetAttributeValue(handle, obj, &attr, 1) != CKR_OK)
143+
value.clear();
98144
return value;
99145
}
100146

101-
vector<CK_OBJECT_HANDLE> PKCS11Signer::Private::findObject(CK_SESSION_HANDLE session, CK_OBJECT_CLASS cls, const vector<CK_BYTE> &id) const
147+
vector<CK_OBJECT_HANDLE> PKCS11Session::findObject(CK_OBJECT_CLASS cls, const vector<CK_BYTE> &id) const
102148
{
103149
CK_BBOOL _true = CK_TRUE;
104150
vector<CK_ATTRIBUTE> attrs {
@@ -107,14 +153,14 @@ vector<CK_OBJECT_HANDLE> PKCS11Signer::Private::findObject(CK_SESSION_HANDLE ses
107153
};
108154
if(!id.empty())
109155
attrs.push_back({ CKA_ID, CK_VOID_PTR(id.data()), CK_ULONG(id.size()) });
110-
if(f->C_FindObjectsInit(session, attrs.data(), CK_ULONG(attrs.size())) != CKR_OK)
156+
if(f->C_FindObjectsInit(handle, attrs.data(), CK_ULONG(attrs.size())) != CKR_OK)
111157
return {};
112158

113159
CK_ULONG count = 32;
114160
vector<CK_OBJECT_HANDLE> result(count);
115-
CK_RV err = f->C_FindObjects(session, result.data(), CK_ULONG(result.size()), &count);
161+
CK_RV err = f->C_FindObjects(handle, result.data(), CK_ULONG(result.size()), &count);
116162
result.resize(err == CKR_OK ? count : 0);
117-
f->C_FindObjectsFinal(session);
163+
f->C_FindObjectsFinal(handle);
118164
return result;
119165
}
120166

@@ -195,38 +241,29 @@ X509Cert PKCS11Signer::cert() const
195241
return d->sign.certificate;
196242

197243
// Load all slots.
198-
CK_ULONG size = 0;
199-
if(d->f->C_GetSlotList(true, nullptr, &size) != CKR_OK)
200-
THROW("Could not find any ID-Cards in any readers");
201-
vector<CK_SLOT_ID> slots(size);
202-
if(size && d->f->C_GetSlotList(true, slots.data(), &size) != CKR_OK)
244+
auto slots = PKCS11List<&CK_FUNCTION_LIST::C_GetSlotList>(d->f, CK_TRUE);
245+
if(slots.empty())
203246
THROW("Could not find any ID-Cards in any readers");
204247

205248
// Iterate over all found slots, if the slot has a token, check if the token has any certificates.
206-
CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
207249
vector<X509Cert> certificates;
208250
vector<Private::SignSlot> certSlotMapping;
209251
for(const CK_SLOT_ID &slot: slots)
210252
{
211-
if(session)
212-
d->f->C_CloseSession(session);
213-
if(d->f->C_OpenSession(slot, CKF_SERIAL_SESSION, nullptr, nullptr, &session) != CKR_OK)
214-
continue;
215-
for(CK_OBJECT_HANDLE obj: d->findObject(session, CKO_CERTIFICATE))
253+
for(PKCS11Session session(d->f, slot);
254+
CK_OBJECT_HANDLE obj: session.findObject(CKO_CERTIFICATE))
216255
{
217-
X509Cert x509(d->attribute(session, obj, CKA_VALUE));
256+
X509Cert x509(session.attribute(obj, CKA_VALUE));
218257
vector<X509Cert::KeyUsage> usage = x509.keyUsage();
219-
if(!x509.isValid() || find(usage.cbegin(), usage.cend(), X509Cert::NonRepudiation) == usage.cend() || x509.isCA())
258+
if(!x509.isValid() || !contains(usage, X509Cert::NonRepudiation) || x509.isCA())
220259
continue;
221-
vector<CK_BYTE> id = d->attribute(session, obj, CKA_ID);
222-
if(d->findObject(session, CKO_PUBLIC_KEY, id).empty())
260+
vector<CK_BYTE> id = session.attribute(obj, CKA_ID);
261+
if(session.findObject(CKO_PUBLIC_KEY, id).empty())
223262
continue;
224263
certSlotMapping.push_back({x509, slot, std::move(id)});
225264
certificates.push_back(std::move(x509));
226265
}
227266
}
228-
if(session)
229-
d->f->C_CloseSession(session);
230267

231268
if(certificates.empty())
232269
THROW("No certificates found.");
@@ -237,10 +274,10 @@ X509Cert PKCS11Signer::cert() const
237274
THROW("No certificate selected.");
238275

239276
// Find the corresponding slot and PKCS11 certificate struct.
240-
for(const Private::SignSlot &slot: certSlotMapping)
277+
for(Private::SignSlot &slot: certSlotMapping)
241278
{
242279
if(slot.certificate == selectedCert)
243-
d->sign = slot;
280+
d->sign = std::move(slot);
244281
}
245282

246283
if(!d->sign.certificate)
@@ -255,13 +292,8 @@ string PKCS11Signer::method() const
255292
if(!d->sign.certificate || !X509Crypto(d->sign.certificate).isRSAKey() ||
256293
parent != CONF(signatureDigestUri))
257294
return parent;
258-
CK_ULONG count = 0;
259-
CK_RV rv = d->f->C_GetMechanismList(d->sign.slot, nullptr, &count);
260-
if(rv != CKR_OK)
261-
return parent;
262-
vector<CK_MECHANISM_TYPE> mech(count);
263-
rv = d->f->C_GetMechanismList(d->sign.slot, mech.data(), &count);
264-
if(find(mech.cbegin(), mech.cend(), CKM_RSA_PKCS_PSS) != mech.cend())
295+
if(auto mech = PKCS11List<&CK_FUNCTION_LIST::C_GetMechanismList>(d->f, d->sign.slot);
296+
contains(mech, CKM_RSA_PKCS_PSS))
265297
return Digest::toRsaPssUri(std::move(parent));
266298
return parent;
267299
}
@@ -323,26 +355,33 @@ vector<unsigned char> PKCS11Signer::sign(const string &method, const vector<unsi
323355
{
324356
DEBUG("sign(mehthod = %s, digest = length=%zu)", method.c_str(), digest.size());
325357

326-
// Check that sign slot and certificate are selected.
327358
if(!d->sign.certificate)
328-
THROW("Signing slot or certificate are not selected.");
359+
THROW("Signing certificate is not selected.");
329360

330-
// Login if required.
331361
CK_TOKEN_INFO token{};
332-
CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
333-
if(d->f->C_GetTokenInfo(d->sign.slot, &token) != CKR_OK ||
334-
d->f->C_OpenSession(d->sign.slot, CKF_SERIAL_SESSION, nullptr, nullptr, &session) != CKR_OK)
335-
THROW("Signing slot or certificate are not selected.");
362+
if(d->f->C_GetTokenInfo(d->sign.slot, &token) != CKR_OK)
363+
THROW("Failed to get token info.");
364+
365+
if(token.flags & CKF_USER_PIN_LOCKED)
366+
{
367+
Exception e(EXCEPTION_PARAMS("PIN Locked"));
368+
e.setCode(Exception::PINLocked);
369+
throw e;
370+
}
371+
372+
PKCS11Session session(d->f, d->sign.slot);
373+
if(!session)
374+
THROW("Failed to open session.");
336375

337376
CK_RV rv = CKR_OK;
338377
if(token.flags & CKF_LOGIN_REQUIRED)
339378
{
340379
if(token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
341-
rv = d->f->C_Login(session, CKU_USER, nullptr, 0);
380+
rv = d->f->C_Login(session.handle, CKU_USER, nullptr, 0);
342381
else
343382
{
344383
string _pin = pin(d->sign.certificate);
345-
rv = d->f->C_Login(session, CKU_USER, CK_BYTE_PTR(_pin.c_str()), CK_ULONG(_pin.size()));
384+
rv = d->f->C_Login(session.handle, CKU_USER, CK_BYTE_PTR(_pin.c_str()), CK_ULONG(_pin.size()));
346385
}
347386
switch(rv)
348387
{
@@ -374,14 +413,14 @@ vector<unsigned char> PKCS11Signer::sign(const string &method, const vector<unsi
374413
}
375414
}
376415

377-
vector<CK_OBJECT_HANDLE> key = d->findObject(session, CKO_PRIVATE_KEY, d->sign.id);
416+
vector<CK_OBJECT_HANDLE> key = session.findObject(CKO_PRIVATE_KEY, d->sign.id);
378417
if(key.size() != 1)
379418
THROW("Could not get key that matches selected certificate.");
380419

381420
// Sign the digest.
382421
CK_KEY_TYPE keyType = CKK_RSA;
383422
CK_ATTRIBUTE attribute { CKA_KEY_TYPE, &keyType, sizeof(keyType) };
384-
d->f->C_GetAttributeValue(session, key.front(), &attribute, 1);
423+
d->f->C_GetAttributeValue(session.handle, key.front(), &attribute, 1);
385424

386425
CK_RSA_PKCS_PSS_PARAMS pssParams { CKM_SHA_1, CKG_MGF1_SHA1, 0 };
387426
CK_MECHANISM mech { keyType == CKK_ECDSA ? CKM_ECDSA : CKM_RSA_PKCS, nullptr, 0 };
@@ -409,16 +448,12 @@ vector<unsigned char> PKCS11Signer::sign(const string &method, const vector<unsi
409448
}
410449
else if(keyType == CKK_RSA)
411450
data = Digest::addDigestInfo(std::move(data), method);
412-
if(d->f->C_SignInit(session, &mech, key.front()) != CKR_OK)
413-
THROW("Failed to sign digest");
414-
415-
CK_ULONG size = 0;
416-
if(d->f->C_Sign(session, data.data(), CK_ULONG(data.size()), nullptr, &size) != CKR_OK)
451+
if(d->f->C_SignInit(session.handle, &mech, key.front()) != CKR_OK)
417452
THROW("Failed to sign digest");
418453

419-
vector<unsigned char> signature(size);
420-
rv = d->f->C_Sign(session, data.data(), CK_ULONG(data.size()), signature.data(), CK_ULONG_PTR(&size));
421-
if(rv != CKR_OK)
454+
auto signature = PKCS11List<&CK_FUNCTION_LIST::C_Sign>(d->f, session.handle, data.data(), CK_ULONG(data.size()));
455+
d->f->C_Logout(session.handle);
456+
if(signature.empty())
422457
THROW("Failed to sign digest");
423458
return signature;
424459
}

0 commit comments

Comments
 (0)