From: Aki Tuomi Date: Mon, 24 Feb 2014 10:33:02 +0000 (+0200) Subject: Add p11-kit-1 support X-Git-Tag: rec-3.6.0-rc1~29^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8daea59423ee2d912c88b71d71bc0637292de97b;p=thirdparty%2Fpdns.git Add p11-kit-1 support Skeleton for PKCS#11 signers Add exceptions for key format Rudimentary version, still needs cleanup Some small tweaks for pkcs11 Remove dnssecinfra.cc from zone2* Use pkcs11 when such file is detected Code cleanup Added pkcs11signers to test suite builds Added few comments Support key creation Add p11-kit-1 support Skeleton for PKCS#11 signers Add exceptions for key format Rudimentary version, still needs cleanup Some small tweaks for pkcs11 Code cleanup Added few comments Support key creation Avoid base64 for PKCS11 attributes hsm assign and hsm create-key methods --- diff --git a/configure.ac b/configure.ac index 3433fd9c13..819cf63a8f 100644 --- a/configure.ac +++ b/configure.ac @@ -158,6 +158,7 @@ AC_SUBST(DYNLINKFLAGS) PDNS_ENABLE_VERBOSE_LOGGING PDNS_WITH_SYSTEM_POLARSSL PDNS_ENABLE_BOTAN +PDNS_ENABLE_PKCS11 PDNS_WITH_CRYPTOPP PDNS_ENABLE_REMOTEBACKEND_HTTP PDNS_ENABLE_REMOTEBACKEND_ZEROMQ diff --git a/m4/pdns_enable_p11kit.m4 b/m4/pdns_enable_p11kit.m4 new file mode 100644 index 0000000000..cce64ebce2 --- /dev/null +++ b/m4/pdns_enable_p11kit.m4 @@ -0,0 +1,18 @@ +AC_DEFUN([PDNS_ENABLE_PKCS11],[ + AC_MSG_CHECKING([whether to enable PKCS11 support]) + AC_ARG_ENABLE([experimental-pkcs11], + [AS_HELP_STRING([--enable-experimental-pkcs11],[enable experimental PKCS11 support @<:@default=no@:>@])], + [enable_pkcs11=yes], + [enable_pkcs11=no] + ) + AC_MSG_RESULT([$enable_pkcs11]) + AM_CONDITIONAL([PKCS11], [test "x$enable_pkcs11" = "xyes"]) + + AS_IF([test "x$enable_pkcs11" = "xyes"], [ + PKG_CHECK_MODULES([P11KIT1], [p11-kit-1], + [AC_DEFINE([HAVE_P11KIT1],[1],[Define to 1 if you have p11-kit-1])], + [AC_MSG_ERROR([Could not find p11-kit-1])] + )] + ) + +]) diff --git a/modules/remotebackend/Makefile.am b/modules/remotebackend/Makefile.am index 82920da2a6..f6b74cc466 100644 --- a/modules/remotebackend/Makefile.am +++ b/modules/remotebackend/Makefile.am @@ -35,8 +35,13 @@ libtestremotebackend_la_SOURCES=../../pdns/dnsbackend.hh ../../pdns/dnsbackend.c ../../pdns/dns.hh ../../pdns/dns.cc ../../pdns/json.hh ../../pdns/json.cc \ remotebackend.hh remotebackend.cc unixconnector.cc httpconnector.cc pipeconnector.cc zmqconnector.cc -libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) -g -O0 -I../../pdns -libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) -g -O0 -I../../pdns +if PKCS11 +libtestremotebackend_la_SOURCES += ../../pdns/pkcs11signers.hh ../../pdns/pkcs11signers.cc +libtestremotebackend_la_LIBADD=$(P11KIT1_LIBS) +endif + +libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) $(P11KIT1_CFLAGS) -g -O0 -I../../pdns +libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) $(P11KIT1_CFLAGS) -g -O0 -I../../pdns test_remotebackend_pipe_SOURCES=test-remotebackend.cc test-remotebackend-pipe.cc test-remotebackend-keys.hh test_remotebackend_unix_SOURCES=test-remotebackend.cc test-remotebackend-unix.cc test-remotebackend-keys.hh diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 0bf2fd040c..9c5ac51f48 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -1,4 +1,3 @@ - AM_CXXFLAGS=-DSYSCONFDIR=\"@sysconfdir@\" -DLIBDIR=\"@libdir@\" -DLOCALSTATEDIR=\"@socketdir@\" @THREADFLAGS@ $(LUA_CFLAGS) $(SQLITE3_CFLAGS) $(POLARSSL_CFLAGS) -Iext/rapidjson/include -Iext/yahttp YAHTTP_LIBS = -Lext/yahttp/yahttp -lyahttp @@ -15,6 +14,10 @@ if BOTAN18 AM_CPPFLAGS += $(BOTAN18_CFLAGS) endif +if PKCS11 +AM_CPPFLAGS += $(P11KIT1_CFLAGS) +endif + EXTRA_DIST = dnslabeltext.rl dnslabeltext.cc mtasker.cc inflighter.cc docs/pdns_control.8 \ docs/pdns_server.8 docs/zone2sql.8 docs/zone2ldap.8 docs/pdnssec.8 \ docs/dnsreplay.8 docs/dnsscope.8 docs/dnswasher.8 docs/pdnssec.8 docs/zone2ldap.8 \ @@ -92,6 +95,11 @@ if ORACLE pdns_server_LDADD += $(ORACLE_LIBS) endif +if PKCS11 +pdns_server_SOURCES += pkcs11signers.cc pkcs11signers.hh +pdns_server_LDADD += $(P11KIT1_LIBS) +endif + pdnssec_SOURCES=pdnssec.cc dbdnsseckeeper.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc dnswriter.cc dnswriter.hh \ misc.cc misc.hh rcpgenerator.cc rcpgenerator.hh base64.cc base64.hh unix_utility.cc \ logger.cc statbag.cc qtype.cc sillyrecords.cc nsecrecords.cc dnssecinfra.cc dnssecinfra.hh \ @@ -105,14 +113,14 @@ pdnssec_SOURCES=pdnssec.cc dbdnsseckeeper.cc sstuff.hh dnsparser.cc dnsparser.hh serialtweaker.cc randomhelper.cc pdnssec_LDFLAGS=@moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@ $(BOOST_PROGRAM_OPTIONS_LDFLAGS) $(BOOST_SERIALIZATION_LDFLAGS) -pdnssec_LDADD= $(POLARSSL_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_SERIALIZATION_LIBS) $(SQLITE3_LIBS) +pdnssec_LDADD= $(POLARSSL_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_SERIALIZATION_LIBS) $(SQLITE3_LIBS) zone2sql_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \ arguments.cc logger.cc zone2sql.cc statbag.cc misc.cc \ unix_utility.cc qtype.cc dns.cc \ zoneparser-tng.cc dnsrecords.cc sillyrecords.cc \ dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc \ - nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh \ + nsecrecords.cc base32.cc bindparserclasses.hh \ dns_random.cc json.cc json.hh zone2sql_LDFLAGS=@THREADFLAGS@ @@ -122,7 +130,7 @@ zone2json_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \ arguments.cc logger.cc zone2json.cc statbag.cc misc.cc \ unix_utility.cc qtype.cc zoneparser-tng.cc dnsrecords.cc \ dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc sillyrecords.cc \ - nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh + nsecrecords.cc base32.cc bindparserclasses.hh zone2json_LDFLAGS=@THREADFLAGS@ zone2json_LDADD= $(POLARSSL_LIBS) @@ -131,7 +139,7 @@ zone2ldap_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \ arguments.cc logger.cc zone2ldap.cc statbag.cc misc.cc \ unix_utility.cc qtype.cc zoneparser-tng.cc dnsrecords.cc \ dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc sillyrecords.cc \ - nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh \ + nsecrecords.cc base32.cc bindparserclasses.hh \ dns_random.cc zone2ldap_LDFLAGS=@THREADFLAGS@ @@ -160,6 +168,11 @@ if ORACLE pdnssec_LDADD += $(ORACLE_LIBS) endif +if PKCS11 +pdnssec_SOURCES += pkcs11signers.cc pkcs11signers.hh +pdnssec_LDADD += $(P11KIT1_LIBS) +endif + sdig_SOURCES=sdig.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc dnswriter.cc dnslabeltext.cc dnswriter.hh \ misc.cc misc.hh rcpgenerator.cc rcpgenerator.hh base64.cc base64.hh unix_utility.cc \ logger.cc statbag.cc qtype.cc sillyrecords.cc nsecrecords.cc base32.cc @@ -283,6 +296,11 @@ testrunner_SOURCES=testrunner.cc test-misc_hh.cc test-nameserver_cc.cc test-dnsr testrunner_LDFLAGS= @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) testrunner_LDADD= $(POLARSSL_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) +if PKCS11 +testrunner_SOURCES += pkcs11signers.cc pkcs11signers.hh +testrunner_LDADD += $(P11KIT1_LIBS) +endif + pdns_recursor_SOURCES=syncres.cc resolver.hh misc.cc unix_utility.cc qtype.cc \ logger.cc statbag.cc arguments.cc lwres.cc pdns_recursor.cc reczones.cc lwres.hh \ mtasker.hh syncres.hh recursor_cache.cc recursor_cache.hh dnsparser.cc \ diff --git a/pdns/dnssecinfra.cc b/pdns/dnssecinfra.cc index a8a77fd835..94d98a0fed 100644 --- a/pdns/dnssecinfra.cc +++ b/pdns/dnssecinfra.cc @@ -16,6 +16,10 @@ #include "base64.hh" #include "sha.hh" #include "namespaces.hh" +#ifdef HAVE_P11KIT1 +#include "pkcs11signers.hh" +#endif + using namespace boost::assign; DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname) @@ -35,6 +39,7 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content) { + bool pkcs11=false; int algorithm = 0; string sline, key, value, raw; std::istringstream str(content); @@ -47,6 +52,20 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& d algorithm = atoi(value.c_str()); stormap["algorithm"]=lexical_cast(algorithm); continue; + } else if (pdns_iequals(key,"pin")) { + stormap["pin"]=value; + continue; + } else if (pdns_iequals(key,"engine")) { + stormap["engine"]=value; + pkcs11=true; + continue; + } else if (pdns_iequals(key,"slot")) { + int slot = atoi(value.c_str()); + stormap["slot"]=lexical_cast(slot); + continue; + } else if (pdns_iequals(key,"label")) { + stormap["label"]=value; + continue; } else if(pdns_iequals(key, "Private-key-format")) continue; @@ -54,7 +73,17 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& d B64Decode(value, raw); stormap[toLower(key)]=raw; } - DNSCryptoKeyEngine* dpk=make(algorithm); + DNSCryptoKeyEngine* dpk; + + if (pkcs11) { +#ifdef HAVE_P11KIT1 + dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm); +#else + throw new PDNSException("Cannot load PKCS#11 key without support for it"); +#endif + } else { + dpk=make(algorithm); + } dpk->fromISCMap(drc, stormap); return dpk; } @@ -66,7 +95,9 @@ std::string DNSCryptoKeyEngine::convertToISC() const ostringstream ret; ret<<"Private-key-format: v1.2\n"; BOOST_FOREACH(const stormap_t::value_type& value, stormap) { - if(value.first != "Algorithm") + if(value.first != "Algorithm" && value.first != "PIN" && + value.first != "Slot" && value.first != "Engine" && + value.first != "Label") ret< DNSCryptoKeyEngine::testMakers(unsigned int alg algorithm = atoi(value.c_str()); stormap["algorithm"]=lexical_cast(algorithm); continue; + } else if (pdns_iequals(key,"pin")) { + stormap["pin"]=value; + continue; + } else if (pdns_iequals(key,"engine")) { + stormap["engine"]=value; + continue; + } else if (pdns_iequals(key,"slot")) { + int slot = atoi(value.c_str()); + stormap["slot"]=lexical_cast(slot); + continue; + } else if (pdns_iequals(key,"label")) { + stormap["label"]=value; + continue; } else if(pdns_iequals(key, "Private-key-format")) continue; @@ -262,7 +306,6 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon string toHash; toHash.assign(toLower(simpleCompress(qname))); toHash.append(const_cast(drc).serialize("", true, true)); - DSRecordContent dsrc; if(digest==1) { @@ -287,6 +330,7 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon dsrc.d_algorithm= drc.d_algorithm; dsrc.d_digesttype=digest; dsrc.d_tag=const_cast(drc).getTag(); + return dsrc; } @@ -294,12 +338,13 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const DNSCryptoKeyEngine* pk, uint8_t algorithm, uint16_t flags) { DNSKEYRecordContent drc; - + drc.d_protocol=3; drc.d_algorithm = algorithm; drc.d_flags=flags; drc.d_key = pk->getPublicKeyString(); + return drc; } @@ -594,3 +639,4 @@ void addTSIG(DNSPacketWriter& pw, TSIGRecordContent* trc, const string& tsigkeyn trc->toPacket(pw); pw.commit(); } + diff --git a/pdns/dnssecinfra.hh b/pdns/dnssecinfra.hh index 96362a44cb..d81e7bb4d9 100644 --- a/pdns/dnssecinfra.hh +++ b/pdns/dnssecinfra.hh @@ -1,5 +1,6 @@ #ifndef PDNS_DNSSECINFRA_HH #define PDNS_DNSSECINFRA_HH + #include "dnsrecords.hh" #include #include @@ -28,7 +29,7 @@ class DNSCryptoKeyEngine virtual std::string getPubKeyHash()const =0; virtual std::string getPublicKeyString()const =0; virtual int getBits() const =0; - + virtual void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap)=0; virtual void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) { diff --git a/pdns/pdnssec.cc b/pdns/pdnssec.cc index 2f4affd3b5..19cd43394a 100644 --- a/pdns/pdnssec.cc +++ b/pdns/pdnssec.cc @@ -5,6 +5,7 @@ #include "base64.hh" #include #include +#include #include #include "dnsbackend.hh" #include "ueberbackend.hh" @@ -1116,6 +1117,8 @@ try cerr<<" Generate a ZSK or KSK to stdout with specified algo&bits"<(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str()))); + + if (!dk.addKey(zone, dpk)) { + cerr << "Unable to assign module slot to zone" << std::endl; + return 1; + } + + cerr << "Module " << module << " slot " << slot << " assigned to " << zone << endl; + return 0; + } else if (cmds[1] == "create-key") { + DomainInfo di; + string zone = cmds[2]; + unsigned int id; + + // verify zone + if (!B.getDomainInfo(zone, di)) { + cerr << "Unable to create key for unknown zone '" << zone << "'" << std::endl; + return 1; + } + + id = boost::lexical_cast(cmds[3]); + std::vector keys; + if (!B.getDomainKeys(zone, 0, keys)) { + cerr << "No keys found for zone " << zone << std::endl; + return 1; + } + + DNSCryptoKeyEngine *dke = NULL; + // lookup correct key + BOOST_FOREACH(DNSBackend::KeyData &kd, keys) { + if (kd.id == id) { + // found our key. + DNSKEYRecordContent dkrc; + dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content); + } + } + + if (!dke) { + cerr << "Could not find key with ID " << id << endl; + return 1; + } + + dke->create(2048); + + cerr << "Created key i think" << std::endl; + return 0; + } } else { cerr<<"Unknown command '"< +#include +#include +#include +#include +#include // for 'operator+=()' +#include +#include +#include + +#include "dnssecinfra.hh" +#include "pkcs11signers.hh" +#include "pdnsexception.hh" + +/* TODO + + - list possible tokens and supported modes + - Engine: , Slot: , PIN: + - fallback if not supported to polarssl (for hashing) + - ECDSA support (how to test?) + - Physical token testing? + - module-slot locking (as they do not support parallel) + +NB! If you do use this, here is a simple way to get softhsm working + +create /etc/pkcs11/modules/softhsm.module + +put + +module: /usr/lib/softhsm/libsofthsm.so +managed: yes + +in it. you need to use softhsm tools to manage this all. + +*/ + + + +// map for signing algorithms +static std::map dnssec2smech = boost::assign::map_list_of +(5, CKM_SHA1_RSA_PKCS) +(7, CKM_SHA1_RSA_PKCS) +(8, CKM_SHA256_RSA_PKCS) +(10, CKM_SHA512_RSA_PKCS) +(13, CKM_ECDSA) +(14, CKM_ECDSA); + +// map for hashing algorithms +static std::map dnssec2hmech = boost::assign::map_list_of +(5, CKM_SHA_1) +(7, CKM_SHA_1) +(8, CKM_SHA256) +(10, CKM_SHA512) +(13, CKM_SHA256) +(14, CKM_SHA384); + +// p11 handler for modules + +class P11KitModule; +class P11KitSlot; + +typedef enum { Attribute_Byte, Attribute_Long, Attribute_String } CkaValueType; + +// Attribute handling +class P11KitAttribute { +private: + CK_ATTRIBUTE_TYPE type; + CK_BYTE ckByte; + CK_ULONG ckLong; + std::string ckString; + CkaValueType ckType; + unsigned char *buffer; + CK_ULONG buflen; +protected: + void Init() { + buffer = NULL; + buflen = 0; + }; +public: + P11KitAttribute(CK_ATTRIBUTE_TYPE type, const std::string& value) { + Init(); + this->type = type; + setString(value); + } + + P11KitAttribute(CK_ATTRIBUTE_TYPE type, char value) { + Init(); + this->type = type; + setByte(value); + } + + P11KitAttribute(CK_ATTRIBUTE_TYPE type, unsigned char value) { + Init(); + this->type = type; + setByte(value); + } + + P11KitAttribute(CK_ATTRIBUTE_TYPE type, unsigned long value) { + Init(); + this->type = type; + setLong(value); + } + + CkaValueType valueType() const { + return ckType; + } + + const std::string &str() const { + return ckString; + }; + + unsigned char byte() const { + return ckByte; + } + + unsigned long ulong() const { + return ckLong; + } + + void setString(const std::string& value) { + this->ckString = value; + this->ckType = Attribute_String; + } + + void setByte(char value) { + this->ckByte = value; + this->ckType = Attribute_Byte; + } + + void setByte(unsigned char value) { + this->ckByte = value; + this->ckType = Attribute_Byte; + } + + void setLong(unsigned long value) { + this->ckLong = value; + this->ckType = Attribute_Long; + } + +// this bit is used for getting attribute from object +// we provide a pointer for GetAttributeValue to write to + CK_BYTE_PTR allocate(CK_ULONG amount) { + buffer = new unsigned char[amount]; + buflen = amount; + return buffer; + } + +// and here we copy the results back and delete buffer + void commit(CK_ULONG amount) { + if (buffer) { + this->ckString.assign((char*)buffer, amount); + delete [] buffer; + } + buffer = NULL; + buflen = 0; + } + +// this is *writable* attribute (you write into it) + void wattr(CK_ATTRIBUTE_PTR attr) { + attr->type = type; + switch(ckType) { + case Attribute_Byte: { + attr->pValue = (void*)&ckByte; + attr->ulValueLen = 1; + break; + } + case Attribute_Long: { + attr->pValue = (void*)&ckLong; + attr->ulValueLen = sizeof(CK_ULONG); + break; + } + case Attribute_String: { + attr->pValue = buffer; + attr->ulValueLen = buflen; + } + }; + }; + +// this is *readable* attribute (you read from it) + void rattr(CK_ATTRIBUTE_PTR attr) const { + attr->type = type; + switch(ckType) { + case Attribute_Byte: { + attr->pValue = (void*)&ckByte; + attr->ulValueLen = 1; + break; + } + case Attribute_Long: { + attr->pValue = (void*)&ckLong; + attr->ulValueLen = sizeof(CK_ULONG); + break; + } + case Attribute_String: { + attr->pValue = (void*)ckString.c_str(); + attr->ulValueLen = ckString.size(); + } + }; + }; +}; + +// representation of slot +class P11KitSlot { +private: + CK_SESSION_HANDLE d_session; + CK_SLOT_ID d_slot; + P11KitModule *d_module; + +public: + P11KitSlot(); + P11KitSlot(CK_SLOT_ID slot, P11KitModule *module); + P11KitSlot(const P11KitSlot& rhs); + ~P11KitSlot(); + + void SetSlot(CK_SLOT_ID slot); + void SetModule(P11KitModule *module); + + CK_RV OpenSession(CK_FLAGS flags); + CK_RV CloseSession(); + + CK_RV GetInfo(CK_SLOT_INFO_PTR info) const; + CK_RV GetTokenInfo(CK_TOKEN_INFO_PTR info) const; + + CK_RV Login(const std::string& pin, CK_USER_TYPE user); + CK_RV Logout(); + + CK_RV GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector& pubAttributes, std::vector& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey); + CK_RV FindObjects(const std::vector& attributes, std::vector& objects, CK_ULONG maxobjects) const; + CK_RV SetAttributeValue(CK_OBJECT_HANDLE object, const std::vector& attributes); + CK_RV GetAttributeValue(CK_OBJECT_HANDLE object, std::vector& attributes) const; + + CK_RV Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const; + CK_RV Verify(const std::string& data, const std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const; + + CK_RV Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) const; + + CK_RV DigestInit(CK_MECHANISM_PTR mechanism) const; + CK_RV DigestUpdate(const std::string& data) const; + CK_RV DigestUpdate(CK_OBJECT_HANDLE key) const; + CK_RV DigestFinal(std::string& result) const; +}; + +// representation of module (or Engine) +class P11KitModule +{ + public: + std::string d_module; + CK_FUNCTION_LIST_PTR functions; + + P11KitModule() {} + + P11KitModule(const std::string& module) { + this->d_module = module; + } + + P11KitModule(const P11KitModule& rhs) { + functions = rhs.functions; + d_module = rhs.d_module; + } + + void setModule(const std::string& module) { + this->d_module = module; + }; + +// basically get the function list + bool initialize() { + functions = p11_kit_registered_name_to_module(d_module.c_str()); + if (functions == NULL) return false; + return true; + }; + +// convenience method + checking that slot exists + bool GetSlot(CK_SLOT_ID slotId, P11KitSlot &slot) { + _CK_SLOT_INFO info; + if (this->functions->C_GetSlotInfo(slotId, &info)) { + return false; + } + slot.SetSlot(slotId); + slot.SetModule(this); + return true; + }; +}; + +P11KitSlot::P11KitSlot() { d_module = NULL; }; + +P11KitSlot::P11KitSlot(CK_SLOT_ID slot, P11KitModule *module) +{ + this->d_slot = slot; + this->d_module = module; +} + +P11KitSlot::P11KitSlot(const P11KitSlot &rhs) +{ + this->d_slot = rhs.d_slot; + this->d_module = rhs.d_module; + this->d_session = rhs.d_session; +} + +P11KitSlot::~P11KitSlot() +{ + if (this->d_module) + this->d_module->functions->C_CloseAllSessions(this->d_slot); +} + +// DO NOT CALL THIS ON YOUR OWN +void P11KitSlot::SetSlot(CK_SLOT_ID slot) { + this->d_slot = slot; +} + +// DO NOT CALL THIS ON YOUR OWN +void P11KitSlot::SetModule(P11KitModule *module) { + this->d_module = module; +} + +// Create new session, mostly uses CKF_SERIAL_SESSION (which is mandatory flag) +// Another flag you can pass is CFK_RW_SESSION +CK_RV P11KitSlot::OpenSession(CK_FLAGS flags) +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_OpenSession(this->d_slot, flags, 0, 0, &(this->d_session)); +} + +CK_RV P11KitSlot::CloseSession() +{ + if (!this->d_module) return 0xff; + CK_RV rv = this->d_module->functions->C_CloseSession(this->d_session); + this->d_session = 0; + return rv; +} + +CK_RV P11KitSlot::GetInfo(CK_SLOT_INFO_PTR info) const +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_GetSlotInfo(this->d_slot, info); +} + +CK_RV P11KitSlot::GetTokenInfo(CK_TOKEN_INFO_PTR info) const +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_GetTokenInfo(this->d_slot, info); +} + +CK_RV P11KitSlot::Login(const std::string& pin, CK_USER_TYPE user) +{ + if (!this->d_module) return 0xff; + CK_RV rv; + unsigned char *pPin; + pPin = new unsigned char[pin.size()]; + pin.copy(reinterpret_cast(pPin), pin.size()); + rv = this->d_module->functions->C_Login(this->d_session, user, pPin, pin.size()); + delete [] pPin; + return rv; +} + +CK_RV P11KitSlot::Logout() +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_Logout(this->d_slot); +} + +CK_RV P11KitSlot::GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector& pubAttributes, std::vector& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey) { + if (!this->d_module) return 0xff; + + CK_RV rv; + size_t k; + CK_ATTRIBUTE_PTR pubAttr, privAttr; + pubAttr = new CK_ATTRIBUTE[pubAttributes.size()]; + privAttr = new CK_ATTRIBUTE[privAttributes.size()]; + + k = 0; + BOOST_FOREACH(P11KitAttribute& attribute, pubAttributes) { + attribute.rattr(pubAttr+k); + k++; + } + + k = 0; + BOOST_FOREACH(P11KitAttribute& attribute, privAttributes) { + attribute.rattr(privAttr+k); + k++; + } + + rv = this->d_module->functions->C_GenerateKeyPair(d_session, mechanism, pubAttr, pubAttributes.size(), privAttr, privAttributes.size(), pubKey, privKey); + + delete [] pubAttr; + delete [] privAttr; + + return rv; +} + +// Finds object(s) that match exactly to attributes +CK_RV P11KitSlot::FindObjects(const std::vector& attributes, std::vector& objects, unsigned long maxobjects) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + size_t k; + unsigned long count; + CK_ATTRIBUTE_PTR attr; + CK_OBJECT_HANDLE_PTR handles = new CK_OBJECT_HANDLE[maxobjects]; + attr = new CK_ATTRIBUTE[attributes.size()]; + + k = 0; + BOOST_FOREACH(const P11KitAttribute& attribute, attributes) { + attribute.rattr(attr+k); + k++; + } + + // perform search + rv = this->d_module->functions->C_FindObjectsInit(d_session, attr, k); + + if (rv) { + delete [] attr; + delete [] handles; + return rv; + } + + count = maxobjects; + rv = this->d_module->functions->C_FindObjects(d_session, handles, maxobjects, &count); + + if (!rv) { + objects.clear(); + for(k=0;kd_module->functions->C_FindObjectsFinal(d_session); + + return rv; +}; + +// TODO: Untested codepath +CK_RV P11KitSlot::SetAttributeValue(CK_OBJECT_HANDLE object, const std::vector& attributes) +{ + if (!this->d_module) return 0xff; + CK_RV rv; + size_t k; + CK_ATTRIBUTE_PTR attr; + attr = new CK_ATTRIBUTE[attributes.size()]; + + k = 0; + BOOST_FOREACH(const P11KitAttribute &attribute, attributes) { + attribute.rattr(attr+k); + k++; + } + + rv = this->d_module->functions->C_SetAttributeValue(d_session, object, attr, attributes.size()); + + delete [] attr; + return rv; +} + +CK_RV P11KitSlot::GetAttributeValue(CK_OBJECT_HANDLE object, std::vector& attributes) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + size_t k; + CK_ATTRIBUTE_PTR attr; + attr = new CK_ATTRIBUTE[attributes.size()]; + + k = 0; + BOOST_FOREACH(P11KitAttribute &attribute, attributes) { + attribute.wattr(attr+k); + k++; + } + + // round 1 - get attribute sizes + rv = this->d_module->functions->C_GetAttributeValue(d_session, object, attr, attributes.size()); + + if (rv) { + delete [] attr; + return rv; + } + + // then allocate memory + for(size_t k=0; k < attributes.size(); k++) { + if (attributes[k].valueType() == Attribute_String) { + attr[k].pValue = attributes[k].allocate(attr[k].ulValueLen); + } + } + + // round 2 - get actual values + rv = this->d_module->functions->C_GetAttributeValue(d_session, object, attr, attributes.size()); + + // copy values to map and release allocated memory + for(size_t k=0; k < attributes.size(); k++) { + if (attributes[k].valueType() == Attribute_String) { + attributes[k].commit(attr[k].ulValueLen); + } + } + + delete [] attr; + + return rv; +}; + +CK_RV P11KitSlot::Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + CK_BYTE buffer[1024]; + CK_ULONG buflen = sizeof buffer; // should be enough for most signatures. + + // perform signature + if ((rv = this->d_module->functions->C_SignInit(d_session, mechanism, key))) return rv; // well that failed. + rv = this->d_module->functions->C_Sign(d_session, (unsigned char*)data.c_str(), data.size(), buffer, &buflen); + if (!rv) { + result.assign((char*)buffer, buflen); + } + memset(buffer,0,sizeof buffer); + + return rv; +}; + +CK_RV P11KitSlot::Verify(const std::string& data, const std::string& signature, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + if ((rv = this->d_module->functions->C_VerifyInit(d_session, mechanism, key))) return rv; + rv = this->d_module->functions->C_Verify(d_session, (unsigned char*)data.c_str(), data.size(), (unsigned char*)signature.c_str(), signature.size()); + return rv; +}; + +CK_RV P11KitSlot::Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + CK_BYTE buffer[1024]; + CK_ULONG buflen = sizeof buffer; // should be enough for most digests + if ((rv = this->d_module->functions->C_DigestInit(d_session, mechanism))) return rv; + rv = this->d_module->functions->C_Digest(d_session, (unsigned char*)data.c_str(), data.size(), buffer, &buflen); + if (!rv) { + result.assign((char*)buffer, buflen); + } + memset(buffer,0,sizeof buffer); + + return rv; +}; + +CK_RV P11KitSlot::DigestInit(CK_MECHANISM_PTR mechanism) const +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_DigestInit(d_session, mechanism); +} + +CK_RV P11KitSlot::DigestUpdate(const std::string& data) const +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_DigestUpdate(d_session, (unsigned char*)data.c_str(), data.size()); +} + +CK_RV P11KitSlot::DigestUpdate(CK_OBJECT_HANDLE key) const +{ + if (!this->d_module) return 0xff; + return this->d_module->functions->C_DigestKey(d_session, key); +} + +CK_RV P11KitSlot::DigestFinal(std::string& result) const +{ + if (!this->d_module) return 0xff; + CK_RV rv; + CK_BYTE buffer[1024]; + CK_ULONG buflen = sizeof buffer; // should be enough for most digests + rv = this->d_module->functions->C_DigestFinal(d_session, buffer, &buflen); + if (!rv) { + result.assign((char*)buffer, buflen); + } + memset(buffer,0,sizeof buffer); + return rv; +} + +// map between engine names and engines +static std::map pkcs11_engines; +// map between engine names and slots (not used now) +//static std::map pkcs11_slots; + +static bool pkcs11_GetSlot(const std::string& engine, CK_SLOT_ID slotId, const std::string& pin, CK_FLAGS flags, P11KitSlot& slot) +{ + CK_RV rv; + if (engine.empty()) return false; + + // open module if necessary + if (pkcs11_engines.find(engine) == pkcs11_engines.end()) { + P11KitModule module(engine); + if (module.initialize() == false) { + throw PDNSException("Cannot initialize or unknown PKCS#11 engine " + engine); + } + pkcs11_engines[engine] = module; + } + pkcs11_engines[engine].GetSlot(slotId, slot); + rv = slot.OpenSession(flags); + if (rv) { + return false; + }; + rv = slot.Login(pin, CKU_USER); +// if (rv) { +// std::cerr << "Login gave " << rv << std::endl; +// }; + return rv == 0; +} + +PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(unsigned int algorithm): DNSCryptoKeyEngine(algorithm) {} +PKCS11DNSCryptoKeyEngine::~PKCS11DNSCryptoKeyEngine() {} +PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig) : DNSCryptoKeyEngine(orig.d_algorithm) {} + +void PKCS11DNSCryptoKeyEngine::create(unsigned int bits) { + std::vector pubAttr; + std::vector privAttr; + CK_MECHANISM mech; + CK_OBJECT_HANDLE pubKey, privKey; + CK_RV rv; + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION|CKF_RW_SESSION, d_slot); + std::string pubExp("\000\001\000\001", 4); // 65537 + + pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE)); + pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE)); + pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE)); + pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits)); + pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp)); + pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + + privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE)); + privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE)); +// privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen")); + privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything + privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE)); + privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE)); + privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE)); + privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE)); + privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + + mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + if ((rv = d_slot.GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey))) { + std::ostringstream error; + error << "Keypair generation failed with " << rv; + throw new PDNSException(error.str()); + } +}; + +std::string PKCS11DNSCryptoKeyEngine::sign(const std::string& msg) const { + std::string result; + std::vector key; + std::vector attr; + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + // find key that can be used for signing + attr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE)); + attr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + d_slot.FindObjects(attr, key, 1); + // hopefully we have a key + if (key.size() == 0) return ""; + // choose mech + CK_MECHANISM mech; + mech.mechanism = dnssec2smech[d_algorithm]; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + d_slot.Sign(msg, result, &mech, key[0]); + return result; +}; + +std::string PKCS11DNSCryptoKeyEngine::hash(const std::string& msg) const { + std::string result; + CK_MECHANISM mech; + mech.mechanism = dnssec2hmech[d_algorithm]; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + + if (d_slot.Digest(msg, result, &mech)) { + // FINE! I'll do this myself, then, shall I? + switch(d_algorithm) { + case 5: { + SHA1Summer sha; + sha.feed(msg); + return sha.get(); + } + case 8: { + SHA256Summer sha; + sha.feed(msg); + return sha.get(); + } + case 10: { + SHA512Summer sha; + sha.feed(msg); + return sha.get(); + } + case 13: { + SHA256Summer sha; + sha.feed(msg); + return sha.get(); + } + case 14: { + SHA384Summer sha; + sha.feed(msg); + return sha.get(); + } + }; + }; + return result; +}; + +bool PKCS11DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const { + bool result; + std::vector key; + std::vector attr; + // find a key that can be used to verify signatures + attr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE)); + attr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + + d_slot.FindObjects(attr, key, 1); + // hopefully we have a key + if (key.size() == 0) return ""; + // choose mech + CK_MECHANISM mech; + mech.mechanism = dnssec2smech[d_algorithm]; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + result = d_slot.Verify(msg, signature, &mech, key[0]); + return result; +}; + +std::string PKCS11DNSCryptoKeyEngine::getPubKeyHash() const { + std::string result = ""; + std::vector key; + std::vector attr; + // find us a public key + //attr.push_back(P11KitAttribute(CKA_CLASS, CKO_PUBLIC_KEY)); + attr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + + d_slot.FindObjects(attr, key, 1); + if (key.size() > 0) { + attr.clear(); + attr.push_back(P11KitAttribute(CKA_MODULUS, "")); + attr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, "")); + if (d_slot.GetAttributeValue(key[0], attr) == 0) { + CK_MECHANISM mech; + mech.mechanism = CKM_SHA_1; + d_slot.DigestInit(&mech); + d_slot.DigestUpdate(attr[0].str()); + d_slot.DigestUpdate(attr[1].str()); + d_slot.DigestFinal(result); + } + } + return result; +}; + +std::string PKCS11DNSCryptoKeyEngine::getPublicKeyString() const { + std::string result(""); + std::vector key; + std::vector attr; + attr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + + d_slot.FindObjects(attr, key, 1); + if (key.size() > 0) { + attr.clear(); + attr.push_back(P11KitAttribute(CKA_MODULUS, "")); + attr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, "")); + if (d_slot.GetAttributeValue(key[0], attr) == 0) { + if(attr[1].str().length() < 255) + result.assign(1, (char) (unsigned int) attr[1].str().length()); + else { + result.assign(1, 0); + uint16_t len=htons(attr[1].str().length()); + result.append((char*)&len, 2); + } + result.append(attr[1].str()); + result.append(attr[0].str()); + } +// } else { +// std::cerr << "Could not find key" << std::endl; + } + return result; +}; + +int PKCS11DNSCryptoKeyEngine::getBits() const { + int bits = -1; + std::vector key; + std::vector attr; + //attr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE)); + attr.push_back(P11KitAttribute(CKA_LABEL, d_label)); + P11KitSlot d_slot; + pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot); + +// std::cerr << "Looking for " << d_label << " from " << d_slot_id << std::endl; + + d_slot.FindObjects(attr, key, 1); + if (key.size() > 0) { + attr.clear(); + attr.push_back(P11KitAttribute(CKA_MODULUS_BITS, 0UL)); + if (d_slot.GetAttributeValue(key[0], attr) == 0) { + bits = static_cast(attr[0].ulong()); + } +// } else { +// std::cerr << "Could not find key" << std::endl; + } + return bits; +}; + +DNSCryptoKeyEngine::storvector_t PKCS11DNSCryptoKeyEngine::convertToISCVector() const { + storvector_t storvect; + typedef std::vector > outputs_t; + outputs_t outputs; + + boost::assign::push_back(storvect) + (make_pair("Algorithm", boost::lexical_cast(d_algorithm))) + (make_pair("Engine", d_engine)) + (make_pair("Slot", boost::lexical_cast(d_slot_id))) + (make_pair("PIN", d_pin)) + (make_pair("Label", d_label)); + return storvect; +}; + +DNSCryptoKeyEngine* PKCS11DNSCryptoKeyEngine::maker(unsigned int algorithm) +{ + return new PKCS11DNSCryptoKeyEngine(algorithm); +} + +// this is called during program startup +namespace { +struct LoaderStruct +{ + LoaderStruct() + { + p11_kit_initialize_registered(); + }; + ~LoaderStruct() { + p11_kit_finalize_registered(); + }; +} loaderPkcs11; +} diff --git a/pdns/pkcs11signers.hh b/pdns/pkcs11signers.hh new file mode 100644 index 0000000000..37850ffeac --- /dev/null +++ b/pdns/pkcs11signers.hh @@ -0,0 +1,50 @@ +class PKCS11DNSCryptoKeyEngine : public DNSCryptoKeyEngine +{ + protected: + std::string d_engine; + unsigned long d_slot_id; + std::string d_pin; + std::string d_label; + + public: + PKCS11DNSCryptoKeyEngine(unsigned int algorithm); + ~PKCS11DNSCryptoKeyEngine(); + + bool operator<(const PKCS11DNSCryptoKeyEngine& rhs) const + { + return false; + } + PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig); + + string getName() const { return "P11 Kit PKCS#11"; }; + + void create(unsigned int bits); + + storvector_t convertToISCVector() const; + + std::string sign(const std::string& msg) const; + + std::string hash(const std::string& msg) const; + + bool verify(const std::string& msg, const std::string& signature) const; + + std::string getPubKeyHash() const; + + std::string getPublicKeyString() const; + + int getBits() const; + + void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap) { + drc.d_algorithm = atoi(stormap["algorithm"].c_str()); + d_engine = stormap["engine"]; + d_slot_id = atoi(stormap["slot"].c_str()); + d_pin = stormap["pin"]; + d_label = stormap["label"]; + }; + + void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) { throw "Unimplemented"; }; + void fromPublicKeyString(const std::string& content) { throw "Unimplemented"; }; + + static DNSCryptoKeyEngine* maker(unsigned int algorithm); +}; +