]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pkcs11signers.cc
rec: Don't account chained queries more than once
[thirdparty/pdns.git] / pdns / pkcs11signers.cc
CommitLineData
870a0fe4
AT
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
8daea594
AT
4#include <boost/assign/std/vector.hpp> // for 'operator+=()'
5#include <boost/assign/list_of.hpp>
efaf4928 6#include <boost/make_shared.hpp>
fa8fd4d2 7
a1d465f3 8#include <boost/format.hpp>
8daea594
AT
9#include <p11-kit/p11-kit.h>
10
887b696b
RK
11#include "pdns/dnssecinfra.hh"
12#include "pdns/logger.hh"
13#include "pdns/pdnsexception.hh"
14#include "pdns/sha.hh"
efaf4928 15#include "pdns/lock.hh"
8daea594 16
efaf4928 17#include "pkcs11signers.hh"
8daea594
AT
18/* TODO
19
20 - list possible tokens and supported modes
21 - Engine: <name>, Slot: <slot>, PIN: <pin>
8daea594 22 - ECDSA support (how to test?)
8daea594
AT
23
24NB! If you do use this, here is a simple way to get softhsm working
25
26create /etc/pkcs11/modules/softhsm.module
27
efaf4928 28put
8daea594
AT
29
30module: /usr/lib/softhsm/libsofthsm.so
31managed: yes
32
33in it. you need to use softhsm tools to manage this all.
34
35*/
36
efaf4928
AT
37#ifdef HAVE_P11KIT1_V2
38static CK_FUNCTION_LIST** p11_modules;
39#endif
8daea594
AT
40
41// map for signing algorithms
42static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2smech = boost::assign::map_list_of
43(5, CKM_SHA1_RSA_PKCS)
44(7, CKM_SHA1_RSA_PKCS)
45(8, CKM_SHA256_RSA_PKCS)
46(10, CKM_SHA512_RSA_PKCS)
47(13, CKM_ECDSA)
48(14, CKM_ECDSA);
49
50// map for hashing algorithms
51static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2hmech = boost::assign::map_list_of
52(5, CKM_SHA_1)
53(7, CKM_SHA_1)
54(8, CKM_SHA256)
55(10, CKM_SHA512)
56(13, CKM_SHA256)
57(14, CKM_SHA384);
58
8daea594
AT
59typedef enum { Attribute_Byte, Attribute_Long, Attribute_String } CkaValueType;
60
61// Attribute handling
62class P11KitAttribute {
63private:
64 CK_ATTRIBUTE_TYPE type;
65 CK_BYTE ckByte;
66 CK_ULONG ckLong;
67 std::string ckString;
68 CkaValueType ckType;
69 unsigned char *buffer;
70 CK_ULONG buflen;
71protected:
72 void Init() {
73 buffer = NULL;
74 buflen = 0;
75 };
76public:
2010ac95 77 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, const std::string& value) {
8daea594 78 Init();
2010ac95 79 this->type = type_;
8daea594
AT
80 setString(value);
81 }
82
2010ac95 83 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, char value) {
8daea594 84 Init();
2010ac95 85 this->type = type_;
8daea594
AT
86 setByte(value);
87 }
88
2010ac95 89 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, unsigned char value) {
8daea594 90 Init();
2010ac95 91 this->type = type_;
8daea594
AT
92 setByte(value);
93 }
94
2010ac95 95 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, unsigned long value) {
8daea594 96 Init();
2010ac95 97 this->type = type_;
8daea594
AT
98 setLong(value);
99 }
100
101 CkaValueType valueType() const {
102 return ckType;
103 }
104
105 const std::string &str() const {
106 return ckString;
107 };
108
109 unsigned char byte() const {
110 return ckByte;
111 }
112
113 unsigned long ulong() const {
114 return ckLong;
115 }
116
117 void setString(const std::string& value) {
118 this->ckString = value;
119 this->ckType = Attribute_String;
120 }
121
122 void setByte(char value) {
123 this->ckByte = value;
124 this->ckType = Attribute_Byte;
125 }
126
127 void setByte(unsigned char value) {
128 this->ckByte = value;
129 this->ckType = Attribute_Byte;
130 }
131
132 void setLong(unsigned long value) {
133 this->ckLong = value;
134 this->ckType = Attribute_Long;
135 }
136
137// this bit is used for getting attribute from object
138// we provide a pointer for GetAttributeValue to write to
139 CK_BYTE_PTR allocate(CK_ULONG amount) {
140 buffer = new unsigned char[amount];
141 buflen = amount;
142 return buffer;
143 }
144
145// and here we copy the results back and delete buffer
146 void commit(CK_ULONG amount) {
147 if (buffer) {
148 this->ckString.assign((char*)buffer, amount);
149 delete [] buffer;
150 }
151 buffer = NULL;
152 buflen = 0;
153 }
154
155// this is *writable* attribute (you write into it)
156 void wattr(CK_ATTRIBUTE_PTR attr) {
157 attr->type = type;
158 switch(ckType) {
159 case Attribute_Byte: {
160 attr->pValue = (void*)&ckByte;
161 attr->ulValueLen = 1;
162 break;
163 }
164 case Attribute_Long: {
165 attr->pValue = (void*)&ckLong;
166 attr->ulValueLen = sizeof(CK_ULONG);
167 break;
168 }
169 case Attribute_String: {
170 attr->pValue = buffer;
171 attr->ulValueLen = buflen;
172 }
173 };
174 };
175
176// this is *readable* attribute (you read from it)
177 void rattr(CK_ATTRIBUTE_PTR attr) const {
178 attr->type = type;
179 switch(ckType) {
180 case Attribute_Byte: {
181 attr->pValue = (void*)&ckByte;
182 attr->ulValueLen = 1;
183 break;
184 }
185 case Attribute_Long: {
186 attr->pValue = (void*)&ckLong;
187 attr->ulValueLen = sizeof(CK_ULONG);
188 break;
189 }
190 case Attribute_String: {
191 attr->pValue = (void*)ckString.c_str();
192 attr->ulValueLen = ckString.size();
193 }
194 };
195 };
196};
197
8daea594 198
85e7248d 199class Pkcs11Slot {
efaf4928 200 private:
85e7248d
AT
201 bool d_logged_in;
202 CK_FUNCTION_LIST* d_functions; // module functions
efaf4928
AT
203 CK_SESSION_HANDLE d_session;
204 CK_SLOT_ID d_slot;
85e7248d
AT
205 CK_RV d_err;
206 pthread_mutex_t d_m;
8daea594 207
85e7248d
AT
208 void logError(const std::string& operation) const {
209 if (d_err) {
a1d465f3
AT
210 std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X)") % operation % p11_kit_strerror(d_err) % d_err );
211 L<<Logger::Error<< msg << endl;
85e7248d
AT
212 }
213 }
248d701f 214
85e7248d
AT
215 public:
216 Pkcs11Slot(CK_FUNCTION_LIST* functions, const CK_SLOT_ID& slot) {
217 CK_TOKEN_INFO tokenInfo;
218 d_slot = slot;
219 d_functions = functions;
220 d_err = 0;
221 d_logged_in = false;
222 pthread_mutex_init(&(this->d_m), NULL);
223 Lock l(&d_m);
224
225 if ((d_err = d_functions->C_OpenSession(this->d_slot, CKF_SERIAL_SESSION|CKF_RW_SESSION, 0, 0, &(this->d_session)))) {
226 logError("C_OpenSession");
227 throw PDNSException("Could not open session");
228 }
229 // check if we need to login
230 if ((d_err = d_functions->C_GetTokenInfo(d_slot, &tokenInfo)) == 0) {
ecb8dc68 231 d_logged_in = !((tokenInfo.flags & CKF_LOGIN_REQUIRED) == CKF_LOGIN_REQUIRED);
85e7248d
AT
232 } else {
233 logError("C_GetTokenInfo");
335da0ba 234 throw PDNSException("Cannot get token info for slot " + std::to_string(slot));
85e7248d
AT
235 }
236 }
8daea594 237
85e7248d
AT
238 bool Login(const std::string& pin) {
239 if (d_logged_in) return true;
8daea594 240
85e7248d
AT
241 unsigned char *uPin = new unsigned char[pin.size()];
242 memcpy(uPin, pin.c_str(), pin.size());
243 d_err = d_functions->C_Login(this->d_session, CKU_USER, uPin, pin.size());
244 memset(uPin, 0, pin.size());
245 delete [] uPin;
246 logError("C_Login");
8daea594 247
85e7248d
AT
248 if (d_err == 0) {
249 d_logged_in = true;
250 }
8daea594 251
85e7248d
AT
252 return d_logged_in;
253 }
8daea594 254
85e7248d 255 bool LoggedIn() const { return d_logged_in; }
8daea594 256
85e7248d 257 CK_SESSION_HANDLE& Session() { return d_session; }
8daea594 258
85e7248d
AT
259 CK_FUNCTION_LIST* f() { return d_functions; }
260
261 pthread_mutex_t *m() { return &d_m; }
24e0b305 262
248d701f
AT
263 static std::shared_ptr<Pkcs11Slot> GetSlot(const std::string& module, const string& tokenId);
264 static CK_RV HuntSlot(const string& tokenId, CK_SLOT_ID &slotId, _CK_SLOT_INFO* info, CK_FUNCTION_LIST* functions);
8daea594
AT
265};
266
85e7248d
AT
267class Pkcs11Token {
268 private:
4d68b3b1 269 std::shared_ptr<Pkcs11Slot> d_slot;
85e7248d 270
efaf4928
AT
271 CK_OBJECT_HANDLE d_public_key;
272 CK_OBJECT_HANDLE d_private_key;
273 CK_KEY_TYPE d_key_type;
274
275 CK_ULONG d_bits;
276 std::string d_exponent;
277 std::string d_modulus;
278 std::string d_ec_point;
279 std::string d_ecdsa_params;
280
281 std::string d_label;
24e0b305 282 bool d_loaded;
efaf4928 283 CK_RV d_err;
efaf4928
AT
284
285 void logError(const std::string& operation) const {
286 if (d_err) {
a1d465f3
AT
287 std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X)") % operation % p11_kit_strerror(d_err) % d_err );
288 L<<Logger::Error<< msg << endl;
efaf4928 289 }
a1d465f3 290 }
8daea594 291
8daea594 292 public:
4d68b3b1 293 Pkcs11Token(const std::shared_ptr<Pkcs11Slot>& slot, const std::string& label);
efaf4928
AT
294 ~Pkcs11Token();
295
85e7248d 296 bool Login(const std::string& pin) {
24e0b305 297 if (pin.empty()) return false; // no empty pin.
85e7248d 298 if (d_slot->Login(pin) == true) {
efaf4928
AT
299 LoadAttributes();
300 }
8daea594 301
85e7248d 302 return LoggedIn();
8daea594
AT
303 }
304
24e0b305
AT
305 bool LoggedIn() {
306 if (d_loaded == false && d_slot->LoggedIn() == true) {
24e0b305
AT
307 LoadAttributes();
308 }
309 return d_slot->LoggedIn();
310 }
8daea594 311
efaf4928 312 void LoadAttributes() {
80d33e0c 313 Lock l(d_slot->m());
efaf4928
AT
314 std::vector<P11KitAttribute> attr;
315 std::vector<CK_OBJECT_HANDLE> key;
efaf4928 316 attr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
b8360d14 317// attr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
efaf4928
AT
318 attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
319 FindObjects2(attr, key, 1);
320 if (key.size() == 0) {
321 L<<Logger::Warning<<"Cannot load PCKS#11 private key "<<d_label<<std::endl;;
322 return;
323 }
324 d_private_key = key[0];
325 attr.clear();
326 attr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
b8360d14 327// attr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
efaf4928
AT
328 attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
329 FindObjects2(attr, key, 1);
330 if (key.size() == 0) {
331 L<<Logger::Warning<<"Cannot load PCKS#11 public key "<<d_label<<std::endl;
332 return;
333 }
334 d_public_key = key[0];
335
336 attr.clear();
337 attr.push_back(P11KitAttribute(CKA_KEY_TYPE, 0UL));
338
339 if (GetAttributeValue2(d_public_key, attr)==0) {
340 d_key_type = attr[0].ulong();
341 if (d_key_type == CKK_RSA) {
342 attr.clear();
343 attr.push_back(P11KitAttribute(CKA_MODULUS, ""));
344 attr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, ""));
345 attr.push_back(P11KitAttribute(CKA_MODULUS_BITS, 0UL));
346
347 if (!GetAttributeValue2(d_public_key, attr)) {
348 d_modulus = attr[0].str();
349 d_exponent = attr[1].str();
350 d_bits = attr[2].ulong();
351 } else {
352 throw PDNSException("Cannot load attributes for PCKS#11 public key " + d_label);
353 }
354 } else if (d_key_type == CKK_EC || d_key_type == CKK_ECDSA) {
355 attr.clear();
356 attr.push_back(P11KitAttribute(CKA_ECDSA_PARAMS, ""));
357 attr.push_back(P11KitAttribute(CKA_EC_POINT, ""));
358 if (!GetAttributeValue2(d_public_key, attr)) {
359 d_ecdsa_params = attr[0].str();
360 if (d_ecdsa_params == "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07") d_bits = 256;
7ecb693d
AT
361 else if (d_ecdsa_params == "\x06\x05\x2b\x81\x04\x00\x22") d_bits = 384;
362 else throw PDNSException("Unsupported EC key");
363 if (attr[1].str().length() != (d_bits*2/8 + 3)) throw PDNSException("EC Point data invalid");
415c31ae 364 d_ec_point = attr[1].str().substr(3);
efaf4928
AT
365 } else {
366 throw PDNSException("Cannot load attributes for PCKS#11 public key " + d_label);
367 }
368 } else {
369 throw PDNSException("Cannot determine type for PCKS#11 public key " + d_label);
370 }
371 } else {
372 throw PDNSException("Cannot load attributes for PCKS#11 public key " + d_label);
373 }
24e0b305
AT
374
375 d_loaded = true;
8daea594
AT
376 }
377
efaf4928 378 int GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector<P11KitAttribute>& pubAttributes, std::vector<P11KitAttribute>& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey) {
80d33e0c 379 {
85e7248d 380 Lock l(d_slot->m());
8daea594 381
efaf4928
AT
382 size_t k;
383 CK_ATTRIBUTE_PTR pubAttr, privAttr;
384 pubAttr = new CK_ATTRIBUTE[pubAttributes.size()];
385 privAttr = new CK_ATTRIBUTE[privAttributes.size()];
8daea594 386
efaf4928 387 k = 0;
ef7cd021 388 for(P11KitAttribute& attribute : pubAttributes) {
efaf4928
AT
389 attribute.rattr(pubAttr+k);
390 k++;
8daea594 391 }
8daea594 392
efaf4928 393 k = 0;
ef7cd021 394 for(P11KitAttribute& attribute : privAttributes) {
efaf4928
AT
395 attribute.rattr(privAttr+k);
396 k++;
397 }
8daea594 398
85e7248d 399 d_err = this->d_slot->f()->C_GenerateKeyPair(d_slot->Session(), mechanism, pubAttr, pubAttributes.size(), privAttr, privAttributes.size(), pubKey, privKey);
efaf4928
AT
400 logError("C_GenerateKeyPair");
401 delete [] pubAttr;
402 delete [] privAttr;
80d33e0c 403 }
8daea594 404
efaf4928 405 if (d_err == 0) LoadAttributes();
8daea594 406
efaf4928 407 return d_err;
8daea594 408 }
8daea594 409
efaf4928 410 int Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
85e7248d 411 Lock l(d_slot->m());
8daea594 412
efaf4928
AT
413 CK_BYTE buffer[1024];
414 CK_ULONG buflen = sizeof buffer; // should be enough for most signatures.
8daea594 415
efaf4928 416 // perform signature
85e7248d
AT
417 if ((d_err = this->d_slot->f()->C_SignInit(d_slot->Session(), mechanism, d_private_key))) { logError("C_SignInit"); return d_err; }
418 d_err = this->d_slot->f()->C_Sign(d_slot->Session(), (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
8daea594 419
efaf4928
AT
420 if (!d_err) {
421 result.assign((char*)buffer, buflen);
8daea594 422 }
8daea594 423
efaf4928
AT
424 memset(buffer,0,sizeof buffer);
425 logError("C_Sign");
426 return d_err;
427 }
8daea594 428
efaf4928 429 int Verify(const std::string& data, const std::string& signature, CK_MECHANISM_PTR mechanism) {
85e7248d 430 Lock l(d_slot->m());
8daea594 431
85e7248d
AT
432 if ((d_err = this->d_slot->f()->C_VerifyInit(d_slot->Session(), mechanism, d_public_key))) { logError("C_VerifyInit"); return d_err; }
433 d_err = this->d_slot->f()->C_Verify(d_slot->Session(), (unsigned char*)data.c_str(), data.size(), (unsigned char*)signature.c_str(), signature.size());
efaf4928
AT
434 logError("C_Verify");
435 return d_err;
436 }
8daea594 437
efaf4928 438 int Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
85e7248d 439 Lock l(d_slot->m());
8daea594 440
efaf4928
AT
441 CK_BYTE buffer[1024];
442 CK_ULONG buflen = sizeof buffer; // should be enough for most digests
85e7248d
AT
443 if ((d_err = this->d_slot->f()->C_DigestInit(d_slot->Session(), mechanism))) { logError("C_DigestInit"); return d_err; }
444 d_err = this->d_slot->f()->C_Digest(d_slot->Session(), (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
efaf4928
AT
445 if (!d_err) {
446 result.assign((char*)buffer, buflen);
447 }
448 memset(buffer,0,sizeof buffer);
449 logError("C_Digest");
450 return d_err;
451 }
8daea594 452
efaf4928 453 int DigestInit(CK_MECHANISM_PTR mechanism) {
85e7248d 454 d_err = d_slot->f()->C_DigestInit(d_slot->Session(), mechanism);
efaf4928
AT
455 logError("C_DigestInit");
456 return d_err;
457 }
8daea594 458
efaf4928 459 int DigestUpdate(const std::string& data) {
85e7248d 460 d_err = d_slot->f()->C_DigestUpdate(d_slot->Session(), (unsigned char*)data.c_str(), data.size());
efaf4928
AT
461 logError("C_DigestUpdate");
462 return d_err;
463 }
8daea594 464
efaf4928 465 int DigestKey(std::string& result) {
85e7248d 466 Lock l(d_slot->m());
efaf4928
AT
467 CK_MECHANISM mech;
468 mech.mechanism = CKM_SHA_1;
8daea594 469
efaf4928 470 DigestInit(&mech);
8daea594 471
efaf4928
AT
472 if (d_key_type == CKK_RSA) {
473 DigestUpdate(d_modulus);
474 DigestUpdate(d_exponent);
475 } else if (d_key_type == CKK_EC || d_key_type == CKK_ECDSA) {
476 DigestUpdate(d_ec_point);
477 }
8daea594 478
efaf4928 479 DigestFinal(result);
8daea594 480
efaf4928
AT
481 return d_err;
482 }
8daea594 483
efaf4928
AT
484 int DigestFinal(std::string& result) {
485 CK_BYTE buffer[1024] = {0};
486 CK_ULONG buflen = sizeof buffer; // should be enough for most digests
85e7248d 487 d_err = d_slot->f()->C_DigestFinal(d_slot->Session(), buffer, &buflen);
efaf4928
AT
488 if (!d_err) {
489 result.assign((char*)buffer, buflen);
490 }
491 memset(buffer,0,sizeof buffer);
492 logError("C_DigestFinal");
493 return d_err;
494 }
8daea594 495
efaf4928 496 int FindObjects(const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, int maxobjects) {
85e7248d 497 Lock l(d_slot->m());
efaf4928
AT
498 return FindObjects2(attributes, objects, maxobjects);
499 }
8daea594 500
efaf4928
AT
501 int FindObjects2(const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, int maxobjects) {
502 CK_RV rv;
503 size_t k;
504 unsigned long count;
8daea594 505
efaf4928
AT
506 CK_ATTRIBUTE_PTR attr;
507 CK_OBJECT_HANDLE_PTR handles = new CK_OBJECT_HANDLE[maxobjects];
508 attr = new CK_ATTRIBUTE[attributes.size()];
8daea594 509
efaf4928 510 k = 0;
ef7cd021 511 for(const P11KitAttribute& attribute : attributes) {
efaf4928
AT
512 attribute.rattr(attr+k);
513 k++;
514 }
8daea594 515
efaf4928 516 // perform search
85e7248d 517 d_err = this->d_slot->f()->C_FindObjectsInit(d_slot->Session(), attr, k);
8daea594 518
efaf4928
AT
519 if (d_err) {
520 delete [] attr;
521 delete [] handles;
522 logError("C_FindObjectsInit");
523 return d_err;
524 }
8daea594 525
efaf4928 526 count = maxobjects;
85e7248d 527 rv = d_err = this->d_slot->f()->C_FindObjects(d_slot->Session(), handles, maxobjects, &count);
efaf4928 528 objects.clear();
8daea594 529
efaf4928
AT
530 if (!rv) {
531 for(k=0;k<count;k++) {
532 objects.push_back(handles[k]);
533 }
534 }
535
536 logError("C_FindObjects");
537
538 delete [] attr;
539 delete [] handles;
8daea594 540
85e7248d 541 d_err = this->d_slot->f()->C_FindObjectsFinal(d_slot->Session());
efaf4928 542 logError("C_FindObjectsFinal");
8daea594 543
efaf4928 544 return rv;
8daea594 545 }
8daea594 546
efaf4928
AT
547 int GetAttributeValue(const CK_OBJECT_HANDLE& object, std::vector<P11KitAttribute>& attributes)
548 {
85e7248d 549 Lock l(d_slot->m());
efaf4928
AT
550 return GetAttributeValue2(object, attributes);
551 }
8daea594 552
efaf4928
AT
553 int GetAttributeValue2(const CK_OBJECT_HANDLE& object, std::vector<P11KitAttribute>& attributes)
554 {
555 size_t k;
556 CK_ATTRIBUTE_PTR attr;
557 attr = new CK_ATTRIBUTE[attributes.size()];
8daea594 558
efaf4928 559 k = 0;
ef7cd021 560 for(P11KitAttribute &attribute : attributes) {
efaf4928
AT
561 attribute.wattr(attr+k);
562 k++;
563 }
8daea594 564
efaf4928 565 // round 1 - get attribute sizes
85e7248d 566 d_err = d_slot->f()->C_GetAttributeValue(d_slot->Session(), object, attr, attributes.size());
efaf4928
AT
567 logError("C_GetAttributeValue");
568 if (d_err) {
569 delete [] attr;
570 return d_err;
571 }
8daea594 572
efaf4928 573 // then allocate memory
2010ac95
RG
574 for(size_t idx=0; idx < attributes.size(); idx++) {
575 if (attributes[idx].valueType() == Attribute_String) {
576 attr[idx].pValue = attributes[idx].allocate(attr[idx].ulValueLen);
efaf4928
AT
577 }
578 }
8daea594 579
efaf4928 580 // round 2 - get actual values
85e7248d 581 d_err = d_slot->f()->C_GetAttributeValue(d_slot->Session(), object, attr, attributes.size());
efaf4928 582 logError("C_GetAttributeValue");
8daea594 583
efaf4928 584 // copy values to map and release allocated memory
2010ac95
RG
585 for(size_t idx=0; idx < attributes.size(); idx++) {
586 if (attributes[idx].valueType() == Attribute_String) {
587 attributes[idx].commit(attr[idx].ulValueLen);
efaf4928
AT
588 }
589 }
8daea594 590
efaf4928 591 delete [] attr;
8daea594 592
efaf4928
AT
593 return d_err;
594 };
8daea594 595
efaf4928
AT
596 const std::string& Modulus() {
597 return d_modulus;
8daea594 598 }
8daea594 599
efaf4928
AT
600 const std::string& Exponent() {
601 return d_exponent;
602 }
8daea594 603
efaf4928
AT
604 const std::string& ECPoint() {
605 return d_ec_point;
8daea594 606 }
8daea594 607
efaf4928
AT
608 const std::string& ECParameters() {
609 return d_ecdsa_params;
610 }
8daea594 611
efaf4928
AT
612 CK_KEY_TYPE KeyType() {
613 return d_key_type;
8daea594 614 }
8daea594 615
efaf4928
AT
616 CK_ULONG Bits() {
617 return d_bits;
618 }
8daea594 619
248d701f 620 static std::shared_ptr<Pkcs11Token> GetToken(const std::string& module, const string& tokenId, const std::string& label);
8daea594
AT
621};
622
4d68b3b1 623static std::map<std::string, std::shared_ptr<Pkcs11Slot> > pkcs11_slots;
624static std::map<std::string, std::shared_ptr<Pkcs11Token> > pkcs11_tokens;
8daea594 625
248d701f
AT
626CK_RV Pkcs11Slot::HuntSlot(const string& tokenId, CK_SLOT_ID &slotId, _CK_SLOT_INFO* info, CK_FUNCTION_LIST* functions)
627{
628 CK_RV err;
629 unsigned long slots;
630 _CK_TOKEN_INFO tinfo;
631
632 // go thru all slots
633 // this is required by certain tokens, otherwise C_GetSlotInfo will not return a token
634 err = functions->C_GetSlotList(CK_FALSE, NULL_PTR, &slots);
635 if (err) {
636 L<<Logger::Warning<<"C_GetSlotList(CK_FALSE, NULL_PTR, &slots) = " << err << std::endl;
637 return err;
638 }
639
640 // iterate all slots
641 for(slotId=0;slotId<slots;slotId++) {
642 if ((err = functions->C_GetSlotInfo(slotId, info))) {
643 L<<Logger::Warning<<"C_GetSlotList("<<slotId<<", info) = " << err << std::endl;
644 return err;
645 }
646 if ((err = functions->C_GetTokenInfo(slotId, &tinfo))) {
647 L<<Logger::Warning<<"C_GetSlotList("<<slotId<<", &tinfo) = " << err << std::endl;
648 return err;
649 }
650 std::string slotName;
651 slotName.assign(reinterpret_cast<char*>(tinfo.label), 32);
652 // trim it
653 boost::trim(slotName);
654 if (boost::iequals(slotName, tokenId)) {
655 return 0;
656 }
657 }
658
659 // see if we can find it with slotId
660 try {
335da0ba 661 slotId = std::stoi(tokenId);
248d701f
AT
662 if ((err = functions->C_GetSlotInfo(slotId, info))) {
663 L<<Logger::Warning<<"C_GetSlotList("<<slotId<<", info) = " << err << std::endl;
664 return err;
665 }
666 L<<Logger::Warning<<"Specifying PKCS#11 token by SLOT ID is deprecated and should not be used"<<std::endl;
667 return 0;
668 } catch (...) {
5a427f84 669 return CKR_SLOT_ID_INVALID;
248d701f 670 }
5a427f84 671 return CKR_SLOT_ID_INVALID;
248d701f
AT
672}
673
674std::shared_ptr<Pkcs11Slot> Pkcs11Slot::GetSlot(const std::string& module, const string& tokenId) {
efaf4928 675 // see if we can find module
24e0b305
AT
676 std::string sidx = module;
677 sidx.append("|");
248d701f 678 sidx.append(tokenId);
4d68b3b1 679 std::map<std::string, std::shared_ptr<Pkcs11Slot> >::iterator slotIter;
989c2e30 680 CK_RV err;
efaf4928 681 CK_FUNCTION_LIST* functions;
8daea594 682
85e7248d
AT
683 // see if we have slot
684 if ((slotIter = pkcs11_slots.find(sidx)) != pkcs11_slots.end()) {
24e0b305 685 return slotIter->second;
8daea594 686 }
8daea594 687
efaf4928
AT
688#ifdef HAVE_P11KIT1_V2
689 functions = p11_kit_module_for_name(p11_modules, module.c_str());
690#else
691 functions = p11_kit_registered_name_to_module(module.c_str());
692#endif
693 if (functions == NULL) throw PDNSException("Cannot find PKCS#11 module " + module);
694 functions->C_Initialize(NULL); // initialize the module in case it hasn't been done yet.
8daea594 695
efaf4928
AT
696 // try to locate a slot
697 _CK_SLOT_INFO info;
248d701f 698 CK_SLOT_ID slotId;
96ac321d 699
248d701f 700 if ((err = Pkcs11Slot::HuntSlot(tokenId, slotId, &info, functions))) {
5a427f84 701 throw PDNSException(std::string("Cannot find PKCS#11 token ") + tokenId + std::string(" on module ") + module + std::string(": ") + boost::str( boost::format("%s (0x%X)") % p11_kit_strerror(err) % err));
8daea594 702 }
8daea594 703
85e7248d 704 // store slot
41e6efd6 705 pkcs11_slots[sidx] = std::make_shared<Pkcs11Slot>(functions, slotId);
8daea594 706
24e0b305
AT
707 return pkcs11_slots[sidx];
708}
709
248d701f 710std::shared_ptr<Pkcs11Token> Pkcs11Token::GetToken(const std::string& module, const string& tokenId, const std::string& label) {
24e0b305
AT
711 // see if we can find module
712 std::string tidx = module;
713 tidx.append("|");
335da0ba 714 tidx.append(tokenId);
24e0b305
AT
715 tidx.append("|");
716 tidx.append(label);
717 std::map<std::string, std::shared_ptr<Pkcs11Token> >::iterator tokenIter;
718 if ((tokenIter = pkcs11_tokens.find(tidx)) != pkcs11_tokens.end()) return tokenIter->second;
8daea594 719
248d701f 720 std::shared_ptr<Pkcs11Slot> slot = Pkcs11Slot::GetSlot(module, tokenId);
24e0b305 721 pkcs11_tokens[tidx] = std::make_shared<Pkcs11Token>(slot, label);
85e7248d 722 return pkcs11_tokens[tidx];
8daea594
AT
723}
724
4d68b3b1 725Pkcs11Token::Pkcs11Token(const std::shared_ptr<Pkcs11Slot>& slot, const std::string& label) {
efaf4928 726 // open a session
efaf4928 727 this->d_bits = 0;
85e7248d 728 this->d_slot = slot;
efaf4928
AT
729 this->d_label = label;
730 this->d_err = 0;
24e0b305 731 this->d_loaded = false;
85e7248d 732 if (this->d_slot->LoggedIn()) LoadAttributes();
8daea594
AT
733}
734
efaf4928 735Pkcs11Token::~Pkcs11Token() {
8daea594
AT
736}
737
248d701f 738bool PKCS11ModuleSlotLogin(const std::string& module, const string& tokenId, const std::string& pin)
24e0b305
AT
739{
740 std::shared_ptr<Pkcs11Slot> slot;
248d701f 741 slot = Pkcs11Slot::GetSlot(module, tokenId);
24e0b305
AT
742 if (slot->LoggedIn()) return true; // no point failing
743 return slot->Login(pin);
744}
745
8daea594
AT
746PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(unsigned int algorithm): DNSCryptoKeyEngine(algorithm) {}
747PKCS11DNSCryptoKeyEngine::~PKCS11DNSCryptoKeyEngine() {}
748PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig) : DNSCryptoKeyEngine(orig.d_algorithm) {}
749
750void PKCS11DNSCryptoKeyEngine::create(unsigned int bits) {
751 std::vector<P11KitAttribute> pubAttr;
752 std::vector<P11KitAttribute> privAttr;
753 CK_MECHANISM mech;
754 CK_OBJECT_HANDLE pubKey, privKey;
755 CK_RV rv;
4d68b3b1 756 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
757 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
758 if (d_slot->LoggedIn() == false)
85e7248d 759 if (d_slot->Login(d_pin) == false)
efaf4928
AT
760 throw PDNSException("Not logged in to token");
761
8daea594 762 std::string pubExp("\000\001\000\001", 4); // 65537
efaf4928 763
054d1c9b
AT
764 pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
765 pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
766 pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
8daea594
AT
767 pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
768 pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
769 pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
770 pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits));
efaf4928 771 pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp));
8daea594
AT
772 pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
773
054d1c9b
AT
774 privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
775 privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
8daea594
AT
776 privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
777 privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
778// privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
779 privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
780 privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
781 privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
782 privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
783 privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
784 privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
785
786 mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
787 mech.pParameter = NULL;
788 mech.ulParameterLen = 0;
789
efaf4928
AT
790 if ((rv = d_slot->GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey))) {
791 throw PDNSException("Keypair generation failed");
8daea594
AT
792 }
793};
794
efaf4928 795std::string PKCS11DNSCryptoKeyEngine::sign(const std::string& msg) const {
8daea594 796 std::string result;
4d68b3b1 797 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
798 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
799 if (d_slot->LoggedIn() == false)
85e7248d 800 if (d_slot->Login(d_pin) == false)
efaf4928
AT
801 throw PDNSException("Not logged in to token");
802
8daea594
AT
803 CK_MECHANISM mech;
804 mech.mechanism = dnssec2smech[d_algorithm];
805 mech.pParameter = NULL;
806 mech.ulParameterLen = 0;
ba9a0057
AT
807
808 if (mech.mechanism == CKM_ECDSA) {
809 if (d_slot->Sign(this->hash(msg), result, &mech)) throw PDNSException("Could not sign data");
810 } else {
811 if (d_slot->Sign(msg, result, &mech)) throw PDNSException("Could not sign data");
812 }
8daea594
AT
813 return result;
814};
815
816std::string PKCS11DNSCryptoKeyEngine::hash(const std::string& msg) const {
817 std::string result;
818 CK_MECHANISM mech;
819 mech.mechanism = dnssec2hmech[d_algorithm];
820 mech.pParameter = NULL;
821 mech.ulParameterLen = 0;
4d68b3b1 822 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
823 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
824 if (d_slot->LoggedIn() == false)
85e7248d 825 if (d_slot->Login(d_pin) == false)
efaf4928 826 throw PDNSException("Not logged in to token");
8daea594 827
efaf4928 828 if (d_slot->Digest(msg, result, &mech)) {
edb00fd3 829 L<<Logger::Error<<"Could not digest using PKCS#11 token - using software workaround"<<endl;
8daea594
AT
830 // FINE! I'll do this myself, then, shall I?
831 switch(d_algorithm) {
832 case 5: {
dd133337 833 return pdns_sha1sum(msg);
8daea594
AT
834 }
835 case 8: {
dd133337 836 return pdns_sha256sum(msg);
8daea594
AT
837 }
838 case 10: {
dd133337 839 return pdns_sha512sum(msg);
8daea594
AT
840 }
841 case 13: {
dd133337 842 return pdns_sha256sum(msg);
8daea594
AT
843 }
844 case 14: {
dd133337 845 return pdns_sha384sum(msg);
8daea594
AT
846 }
847 };
848 };
849 return result;
850};
851
852bool PKCS11DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const {
4d68b3b1 853 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
854 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
855 if (d_slot->LoggedIn() == false)
85e7248d 856 if (d_slot->Login(d_pin) == false)
efaf4928
AT
857 throw PDNSException("Not logged in to token");
858
8daea594
AT
859 CK_MECHANISM mech;
860 mech.mechanism = dnssec2smech[d_algorithm];
861 mech.pParameter = NULL;
862 mech.ulParameterLen = 0;
ba9a0057
AT
863 if (mech.mechanism == CKM_ECDSA) {
864 return (d_slot->Verify(this->hash(msg), signature, &mech)==0);
865 } else {
866 return (d_slot->Verify(msg, signature, &mech) == 0);
867 }
8daea594
AT
868};
869
efaf4928 870std::string PKCS11DNSCryptoKeyEngine::getPubKeyHash() const {
8daea594 871 // find us a public key
4d68b3b1 872 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
873 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
874 if (d_slot->LoggedIn() == false)
85e7248d 875 if (d_slot->Login(d_pin) == false)
efaf4928
AT
876 throw PDNSException("Not logged in to token");
877
878 std::string result;
879 if (d_slot->DigestKey(result) == 0) return result;
9cd38ef1 880 throw PDNSException("Could not digest key (maybe it's missing?)");
8daea594
AT
881};
882
883std::string PKCS11DNSCryptoKeyEngine::getPublicKeyString() const {
884 std::string result("");
4d68b3b1 885 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
886 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
887 if (d_slot->LoggedIn() == false)
85e7248d 888 if (d_slot->Login(d_pin) == false)
efaf4928
AT
889 throw PDNSException("Not logged in to token");
890
891 if (d_slot->KeyType() == CKK_RSA) {
892 if (d_slot->Exponent().length() < 255) {
893 result.assign(1, (char) (unsigned int) d_slot->Exponent().length());
894 } else {
895 result.assign(1, 0);
896 uint16_t len=htons(d_slot->Exponent().length());
897 result.append((char*)&len, 2);
898 }
899 result.append(d_slot->Exponent());
900 result.append(d_slot->Modulus());
901 } else {
902 result.append(d_slot->ECPoint());
903 }
904 return result;
8daea594
AT
905};
906
907int PKCS11DNSCryptoKeyEngine::getBits() const {
4d68b3b1 908 std::shared_ptr<Pkcs11Token> d_slot;
efaf4928
AT
909 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
910 if (d_slot->LoggedIn() == false)
85e7248d 911 if (d_slot->Login(d_pin) == false)
efaf4928
AT
912 throw PDNSException("Not logged in to token");
913
914 return d_slot->Bits();
8daea594
AT
915};
916
efaf4928 917DNSCryptoKeyEngine::storvector_t PKCS11DNSCryptoKeyEngine::convertToISCVector() const {
8daea594
AT
918 storvector_t storvect;
919 typedef std::vector<std::pair<std::string, std::string> > outputs_t;
920 outputs_t outputs;
921
922 boost::assign::push_back(storvect)
335da0ba 923 (make_pair("Algorithm", std::to_string(d_algorithm)))
efaf4928 924 (make_pair("Engine", d_module))
dc6dd311 925 (make_pair("Slot", d_slot_id))
8daea594
AT
926 (make_pair("PIN", d_pin))
927 (make_pair("Label", d_label));
928 return storvect;
929};
930
3a3ecb9d 931void PKCS11DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap) {
335da0ba 932 drc.d_algorithm = pdns_stou(stormap["algorithm"]);
3a3ecb9d 933 d_module = stormap["engine"];
248d701f
AT
934 d_slot_id = stormap["slot"];
935 boost::trim(d_slot_id);
3a3ecb9d
AT
936 d_pin = stormap["pin"];
937 d_label = stormap["label"];
938 // validate parameters
939
4d68b3b1 940 std::shared_ptr<Pkcs11Token> d_slot;
3a3ecb9d
AT
941 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label);
942 if (d_pin != "" && d_slot->LoggedIn() == false)
943 if (d_slot->Login(d_pin) == false)
944 throw PDNSException("Could not log in to token (PIN wrong?)");
945};
946
e69c2dac 947std::shared_ptr<DNSCryptoKeyEngine> PKCS11DNSCryptoKeyEngine::maker(unsigned int algorithm)
8daea594 948{
e69c2dac 949 return std::make_shared<PKCS11DNSCryptoKeyEngine>(algorithm);
8daea594
AT
950}
951
952// this is called during program startup
953namespace {
efaf4928 954 static struct LoaderStruct
8daea594 955 {
efaf4928
AT
956 LoaderStruct()
957 {
fa8f416f 958#ifdef HAVE_P11KIT1_V2
efaf4928 959 p11_modules = p11_kit_modules_load_and_initialize(0);
fa8f416f 960#else
efaf4928 961 p11_kit_initialize_registered();
fa8f416f 962#endif
efaf4928
AT
963 };
964 ~LoaderStruct() {
fa8f416f 965#ifdef HAVE_P11KIT1_V2
efaf4928 966 p11_kit_modules_release(p11_modules);
fa8f416f 967#else
efaf4928 968 p11_kit_finalize_registered();
fa8f416f 969#endif
efaf4928
AT
970 };
971 } loaderPkcs11;
8daea594 972}