2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2001 - 2012 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "dnsseckeeper.hh"
27 #include "dnssecinfra.hh"
28 #include "ueberbackend.hh"
33 #include <sys/types.h>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/format.hpp>
37 #include <boost/assign/std/vector.hpp> // for 'operator+=()'
38 #include <boost/assign/list_inserter.hpp>
40 #include "cachecleaner.hh"
41 #include "arguments.hh"
44 using namespace boost::assign
;
45 #include "namespaces.hh"
48 DNSSECKeeper::keycache_t
DNSSECKeeper::s_keycache
;
49 DNSSECKeeper::metacache_t
DNSSECKeeper::s_metacache
;
50 pthread_rwlock_t
DNSSECKeeper::s_metacachelock
= PTHREAD_RWLOCK_INITIALIZER
;
51 pthread_rwlock_t
DNSSECKeeper::s_keycachelock
= PTHREAD_RWLOCK_INITIALIZER
;
52 AtomicCounter
DNSSECKeeper::s_ops
;
53 time_t DNSSECKeeper::s_last_prune
;
55 bool DNSSECKeeper::isSecuredZone(const DNSName
& zone
)
60 keyset_t keys
= getKeys(zone
); // does the cache
62 for(keyset_t::value_type
& val
: keys
) {
63 if(val
.second
.active
) {
70 bool DNSSECKeeper::isPresigned(const DNSName
& name
)
73 getFromMeta(name
, "PRESIGNED", meta
);
77 bool DNSSECKeeper::addKey(const DNSName
& name
, bool setSEPBit
, int algorithm
, int bits
, bool active
)
81 throw runtime_error("Creating an algorithm " +std::to_string(algorithm
)+" ("+algorithm2name(algorithm
)+") key requires the size (in bits) to be passed");
83 if(algorithm
== 12 || algorithm
== 13 || algorithm
== 250) // GOST, ECDSAP256SHA256, ED25519SHA512
85 else if(algorithm
== 14) // ECDSAP384SHA384
88 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algorithm
));
92 DNSSECPrivateKey dspk
;
93 shared_ptr
<DNSCryptoKeyEngine
> dpk(DNSCryptoKeyEngine::make(algorithm
));
96 dspk
.d_algorithm
= algorithm
;
97 dspk
.d_flags
= setSEPBit
? 257 : 256;
98 return addKey(name
, dspk
, active
);
101 void DNSSECKeeper::clearAllCaches() {
103 WriteLock
l(&s_keycachelock
);
106 WriteLock
l(&s_metacachelock
);
110 void DNSSECKeeper::clearCaches(const DNSName
& name
)
113 WriteLock
l(&s_keycachelock
);
114 s_keycache
.erase(name
);
116 WriteLock
l(&s_metacachelock
);
117 pair
<metacache_t::iterator
, metacache_t::iterator
> range
= s_metacache
.equal_range(tie(name
));
118 while(range
.first
!= range
.second
)
119 s_metacache
.erase(range
.first
++);
123 bool DNSSECKeeper::addKey(const DNSName
& name
, const DNSSECPrivateKey
& dpk
, bool active
)
126 DNSBackend::KeyData kd
;
127 kd
.flags
= dpk
.d_flags
; // the dpk doesn't get stored, only they key part
129 kd
.content
= dpk
.getKey()->convertToISC();
131 return d_keymetadb
->addDomainKey(name
, kd
) >= 0; // >= 0 == s
135 static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type
& a
, const DNSSECKeeper::keyset_t::value_type
& b
)
137 return make_pair(!a
.second
.keyType
, a
.second
.id
) <
138 make_pair(!b
.second
.keyType
, b
.second
.id
);
141 DNSSECPrivateKey
DNSSECKeeper::getKeyById(const DNSName
& zname
, unsigned int id
)
143 vector
<DNSBackend::KeyData
> keys
;
144 d_keymetadb
->getDomainKeys(zname
, 0, keys
);
145 for(const DNSBackend::KeyData
& kd
: keys
) {
149 DNSSECPrivateKey dpk
;
150 DNSKEYRecordContent dkrc
;
151 dpk
.setKey(shared_ptr
<DNSCryptoKeyEngine
>(DNSCryptoKeyEngine::makeFromISCString(dkrc
, kd
.content
)));
152 dpk
.d_flags
= kd
.flags
;
153 dpk
.d_algorithm
= dkrc
.d_algorithm
;
155 if(dpk
.d_algorithm
== 5 && getNSEC3PARAM(zname
)) {
156 dpk
.d_algorithm
+= 2;
161 throw runtime_error("Can't find a key with id "+std::to_string(id
)+" for zone '"+zname
.toString()+"'");
165 bool DNSSECKeeper::removeKey(const DNSName
& zname
, unsigned int id
)
168 return d_keymetadb
->removeDomainKey(zname
, id
);
171 bool DNSSECKeeper::deactivateKey(const DNSName
& zname
, unsigned int id
)
174 return d_keymetadb
->deactivateDomainKey(zname
, id
);
177 bool DNSSECKeeper::activateKey(const DNSName
& zname
, unsigned int id
)
180 return d_keymetadb
->activateDomainKey(zname
, id
);
184 void DNSSECKeeper::getFromMeta(const DNSName
& zname
, const std::string
& key
, std::string
& value
)
186 static int ttl
= ::arg().asNum("domain-metadata-cache-ttl");
188 unsigned int now
= time(0);
190 if(!((++s_ops
) % 100000)) {
195 ReadLock
l(&s_metacachelock
);
197 metacache_t::const_iterator iter
= s_metacache
.find(tie(zname
, key
));
198 if(iter
!= s_metacache
.end() && iter
->d_ttd
> now
) {
199 value
= iter
->d_value
;
204 d_keymetadb
->getDomainMetadata(zname
, key
, meta
);
211 nce
.d_ttd
= now
+ ttl
;
215 WriteLock
l(&s_metacachelock
);
216 replacing_insert(s_metacache
, nce
);
221 void DNSSECKeeper::getSoaEdit(const DNSName
& zname
, std::string
& value
)
223 static const string
soaEdit(::arg()["default-soa-edit"]);
224 static const string
soaEditSigned(::arg()["default-soa-edit-signed"]);
226 getFromMeta(zname
, "SOA-EDIT", value
);
228 if ((!soaEdit
.empty() || !soaEditSigned
.empty()) && value
.empty() && !isPresigned(zname
)) {
229 if (!soaEditSigned
.empty() && isSecuredZone(zname
))
238 uint64_t DNSSECKeeper::dbdnssecCacheSizes(const std::string
& str
)
240 if(str
=="meta-cache-size") {
241 ReadLock
l(&s_metacachelock
);
242 return s_metacache
.size();
244 else if(str
=="key-cache-size") {
245 ReadLock
l(&s_keycachelock
);
246 return s_keycache
.size();
251 bool DNSSECKeeper::getNSEC3PARAM(const DNSName
& zname
, NSEC3PARAMRecordContent
* ns3p
, bool* narrow
)
254 getFromMeta(zname
, "NSEC3PARAM", value
);
255 if(value
.empty()) { // "no NSEC3"
259 static int maxNSEC3Iterations
=::arg().asNum("max-nsec3-iterations");
261 NSEC3PARAMRecordContent
* tmp
=dynamic_cast<NSEC3PARAMRecordContent
*>(DNSRecordContent::mastermake(QType::NSEC3PARAM
, 1, value
));
264 if (ns3p
->d_iterations
> maxNSEC3Iterations
) {
265 ns3p
->d_iterations
= maxNSEC3Iterations
;
266 L
<<Logger::Error
<<"Number of NSEC3 iterations for zone '"<<zname
<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations
<<endl
;
268 if (ns3p
->d_algorithm
!= 1) {
269 L
<<Logger::Error
<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p
->d_algorithm
)<<"', setting to 1 for zone '"<<zname
<<"'."<<endl
;
270 ns3p
->d_algorithm
= 1;
274 getFromMeta(zname
, "NSEC3NARROW", value
);
275 *narrow
= (value
=="1");
280 bool DNSSECKeeper::setNSEC3PARAM(const DNSName
& zname
, const NSEC3PARAMRecordContent
& ns3p
, const bool& narrow
)
282 static int maxNSEC3Iterations
=::arg().asNum("max-nsec3-iterations");
283 if (ns3p
.d_iterations
> maxNSEC3Iterations
)
284 throw runtime_error("Can't set NSEC3PARAM for zone '"+zname
.toString()+"': number of NSEC3 iterations is above 'max-nsec3-iterations'");
286 if (ns3p
.d_algorithm
!= 1)
287 throw runtime_error("Invalid hash algorithm for NSEC3: '"+std::to_string(ns3p
.d_algorithm
)+"' for zone '"+zname
.toString()+"'. The only valid value is '1'");
290 string descr
= ns3p
.getZoneRepresentation();
292 meta
.push_back(descr
);
293 if (d_keymetadb
->setDomainMetadata(zname
, "NSEC3PARAM", meta
)) {
299 return d_keymetadb
->setDomainMetadata(zname
, "NSEC3NARROW", meta
);
304 bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName
& zname
)
307 return (d_keymetadb
->setDomainMetadata(zname
, "NSEC3PARAM", vector
<string
>()) && d_keymetadb
->setDomainMetadata(zname
, "NSEC3NARROW", vector
<string
>()));
311 bool DNSSECKeeper::setPresigned(const DNSName
& zname
)
316 return d_keymetadb
->setDomainMetadata(zname
, "PRESIGNED", meta
);
319 bool DNSSECKeeper::unsetPresigned(const DNSName
& zname
)
322 return d_keymetadb
->setDomainMetadata(zname
, "PRESIGNED", vector
<string
>());
326 * Add domainmetadata to allow publishing CDS records for zone zname
328 * @param zname DNSName of the zone
329 * @param digestAlgos string with comma-separated numbers that describe the
330 * used digest algorithms. This is copied to the database
332 * @return true if the data was inserted, false otherwise
334 bool DNSSECKeeper::setPublishCDS(const DNSName
& zname
, const string
& digestAlgos
)
338 meta
.push_back(digestAlgos
);
339 return d_keymetadb
->setDomainMetadata(zname
, "PUBLISH-CDS", meta
);
343 * Remove domainmetadata to stop publishing CDS records for zone zname
345 * @param zname DNSName of the zone
346 * @return true if the operation was successful, false otherwise
348 bool DNSSECKeeper::unsetPublishCDS(const DNSName
& zname
)
351 return d_keymetadb
->setDomainMetadata(zname
, "PUBLISH-CDS", vector
<string
>());
355 * Add domainmetadata to allow publishing CDNSKEY records.for zone zname
357 * @param zname DNSName of the zone
358 * @return true if the data was inserted, false otherwise
360 bool DNSSECKeeper::setPublishCDNSKEY(const DNSName
& zname
)
365 return d_keymetadb
->setDomainMetadata(zname
, "PUBLISH-CDNSKEY", meta
);
369 * Remove domainmetadata to stop publishing CDNSKEY records for zone zname
371 * @param zname DNSName of the zone
372 * @return true if the operation was successful, false otherwise
374 bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName
& zname
)
377 return d_keymetadb
->setDomainMetadata(zname
, "PUBLISH-CDNSKEY", vector
<string
>());
381 * Returns all keys that are used to sign the DNSKEY RRSet in a zone
383 * @param zname DNSName of the zone
384 * @return a keyset_t with all keys that are used to sign the DNSKEY
385 * RRSet (these are the entrypoint(s) to the zone)
387 DNSSECKeeper::keyset_t
DNSSECKeeper::getEntryPoints(const DNSName
& zname
)
389 DNSSECKeeper::keyset_t ret
;
390 DNSSECKeeper::keyset_t keys
= getKeys(zname
);
392 for(auto const &keymeta
: keys
)
393 if(keymeta
.second
.active
&& (keymeta
.second
.keyType
== KSK
|| keymeta
.second
.keyType
== CSK
))
394 ret
.push_back(keymeta
);
398 DNSSECKeeper::keyset_t
DNSSECKeeper::getKeys(const DNSName
& zone
, bool useCache
)
400 static int ttl
= ::arg().asNum("dnssec-key-cache-ttl");
401 unsigned int now
= time(0);
403 if(!((++s_ops
) % 100000)) {
407 if (useCache
&& ttl
> 0) {
408 ReadLock
l(&s_keycachelock
);
409 keycache_t::const_iterator iter
= s_keycache
.find(zone
);
411 if(iter
!= s_keycache
.end() && iter
->d_ttd
> now
) {
413 for(const keyset_t::value_type
& value
: iter
->d_keys
)
414 ret
.push_back(value
);
420 vector
<DNSBackend::KeyData
> dbkeyset
;
422 d_keymetadb
->getDomainKeys(zone
, 0, dbkeyset
);
424 // Determine the algorithms that have a KSK/ZSK split
425 set
<uint8_t> algoSEP
, algoNoSEP
;
426 vector
<uint8_t> algoHasSeparateKSK
;
427 for(const DNSBackend::KeyData
&keydata
: dbkeyset
) {
428 DNSSECPrivateKey dpk
;
429 DNSKEYRecordContent dkrc
;
431 dpk
.setKey(shared_ptr
<DNSCryptoKeyEngine
>(DNSCryptoKeyEngine::makeFromISCString(dkrc
, keydata
.content
)));
434 if(keydata
.flags
== 257)
435 algoSEP
.insert(dkrc
.d_algorithm
);
437 algoNoSEP
.insert(dkrc
.d_algorithm
);
440 set_intersection(algoSEP
.begin(), algoSEP
.end(), algoNoSEP
.begin(), algoNoSEP
.end(), std::back_inserter(algoHasSeparateKSK
));
442 for(DNSBackend::KeyData
& kd
: dbkeyset
)
444 DNSSECPrivateKey dpk
;
445 DNSKEYRecordContent dkrc
;
447 dpk
.setKey(shared_ptr
<DNSCryptoKeyEngine
>(DNSCryptoKeyEngine::makeFromISCString(dkrc
, kd
.content
)));
449 dpk
.d_flags
= kd
.flags
;
450 dpk
.d_algorithm
= dkrc
.d_algorithm
;
451 if(dpk
.d_algorithm
== 5 && getNSEC3PARAM(zone
))
456 kmd
.active
= kd
.active
;
457 kmd
.hasSEPBit
= (kd
.flags
== 257);
460 if (find(algoHasSeparateKSK
.begin(), algoHasSeparateKSK
.end(), dpk
.d_algorithm
) == algoHasSeparateKSK
.end())
462 else if(kmd
.hasSEPBit
)
467 retkeyset
.push_back(make_pair(dpk
, kmd
));
469 sort(retkeyset
.begin(), retkeyset
.end(), keyCompareByKindAndID
);
474 kce
.d_keys
= retkeyset
;
475 kce
.d_ttd
= now
+ ttl
;
477 WriteLock
l(&s_keycachelock
);
478 replacing_insert(s_keycache
, kce
);
485 bool DNSSECKeeper::checkKeys(const DNSName
& zone
)
487 vector
<DNSBackend::KeyData
> dbkeyset
;
488 d_keymetadb
->getDomainKeys(zone
, 0, dbkeyset
);
490 for(const DNSBackend::KeyData
&keydata
: dbkeyset
) {
491 DNSKEYRecordContent dkrc
;
492 shared_ptr
<DNSCryptoKeyEngine
> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc
, keydata
.content
));
493 if (!dke
->checkKey()) {
501 bool DNSSECKeeper::getPreRRSIGs(UeberBackend
& db
, const DNSName
& signer
, const DNSName
& qname
,
502 const DNSName
& wildcardname
, const QType
& qtype
,
503 DNSResourceRecord::Place signPlace
, vector
<DNSResourceRecord
>& rrsigs
, uint32_t signTTL
)
505 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
507 if(!db
.getSOAUncached(signer
, sd
)) {
508 DLOG(L
<<"Could not get SOA for domain"<<endl
);
511 db
.lookup(QType(QType::RRSIG
), wildcardname
.countLabels() ? wildcardname
: qname
, NULL
, sd
.domain_id
);
512 DNSResourceRecord rr
;
514 // cerr<<"Considering for '"<<qtype.getName()<<"' RRSIG '"<<rr.content<<"'\n";
515 vector
<string
> parts
;
516 stringtok(parts
, rr
.content
);
517 if(parts
[0] == qtype
.getName() && DNSName(parts
[7])==signer
) {
518 // cerr<<"Got it"<<endl;
519 if (wildcardname
.countLabels())
521 rr
.d_place
= signPlace
;
523 rrsigs
.push_back(rr
);
525 // else cerr<<"Skipping!"<<endl;
530 bool DNSSECKeeper::TSIGGrantsAccess(const DNSName
& zone
, const DNSName
& keyname
)
532 vector
<string
> allowed
;
534 d_keymetadb
->getDomainMetadata(zone
, "TSIG-ALLOW-AXFR", allowed
);
536 for(const string
& dbkey
: allowed
) {
537 if(DNSName(dbkey
)==keyname
)
543 bool DNSSECKeeper::getTSIGForAccess(const DNSName
& zone
, const string
& master
, DNSName
* keyname
)
545 vector
<string
> keynames
;
546 d_keymetadb
->getDomainMetadata(zone
, "AXFR-MASTER-TSIG", keynames
);
547 keyname
->trimToLabels(0);
549 // XXX FIXME this should check for a specific master!
550 for(const string
& dbkey
: keynames
) {
551 *keyname
=DNSName(dbkey
);
557 void DNSSECKeeper::cleanup()
560 Utility::gettimeofday(&now
, 0);
562 if(now
.tv_sec
- s_last_prune
> (time_t)(30)) {
564 WriteLock
l(&s_metacachelock
);
565 pruneCollection(s_metacache
, ::arg().asNum("max-cache-entries"));
568 WriteLock
l(&s_keycachelock
);
569 pruneCollection(s_keycache
, ::arg().asNum("max-cache-entries"));
571 s_last_prune
=time(0);