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