]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dbdnsseckeeper.cc
c7217c7f941389032929ea34b3a731ab66a3b5ae
[thirdparty/pdns.git] / pdns / dbdnsseckeeper.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2001 - 2012 PowerDNS.COM BV
4
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
8
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.
12
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.
17
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
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "dnsseckeeper.hh"
27 #include "dnssecinfra.hh"
28 #include "ueberbackend.hh"
29 #include "statbag.hh"
30 #include <iostream>
31
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <fstream>
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>
39 #include "base64.hh"
40 #include "cachecleaner.hh"
41 #include "arguments.hh"
42
43
44 using namespace boost::assign;
45 #include "namespaces.hh"
46
47
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;
54
55 bool DNSSECKeeper::isSecuredZone(const DNSName& zone)
56 {
57 if(isPresigned(zone))
58 return true;
59
60 keyset_t keys = getKeys(zone); // does the cache
61
62 for(keyset_t::value_type& val : keys) {
63 if(val.second.active) {
64 return true;
65 }
66 }
67 return false;
68 }
69
70 bool DNSSECKeeper::isPresigned(const DNSName& name)
71 {
72 string meta;
73 getFromMeta(name, "PRESIGNED", meta);
74 return meta=="1";
75 }
76
77 bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int bits, bool active)
78 {
79 if(!bits) {
80 if(algorithm <= 10)
81 throw runtime_error("Creating an algorithm " +std::to_string(algorithm)+" ("+algorithm2name(algorithm)+") key requires the size (in bits) to be passed");
82 else {
83 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // GOST, ECDSAP256SHA256, ED25519SHA512
84 bits = 256;
85 else if(algorithm == 14) // ECDSAP384SHA384
86 bits = 384;
87 else {
88 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algorithm));
89 }
90 }
91 }
92 DNSSECPrivateKey dspk;
93 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm));
94 dpk->create(bits);
95 dspk.setKey(dpk);
96 dspk.d_algorithm = algorithm;
97 dspk.d_flags = setSEPBit ? 257 : 256;
98 return addKey(name, dspk, active);
99 }
100
101 void DNSSECKeeper::clearAllCaches() {
102 {
103 WriteLock l(&s_keycachelock);
104 s_keycache.clear();
105 }
106 WriteLock l(&s_metacachelock);
107 s_metacache.clear();
108 }
109
110 void DNSSECKeeper::clearCaches(const DNSName& name)
111 {
112 {
113 WriteLock l(&s_keycachelock);
114 s_keycache.erase(name);
115 }
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++);
120 }
121
122
123 bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, bool active)
124 {
125 clearCaches(name);
126 DNSBackend::KeyData kd;
127 kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
128 kd.active = active;
129 kd.content = dpk.getKey()->convertToISC();
130 // now store it
131 return d_keymetadb->addDomainKey(name, kd) >= 0; // >= 0 == s
132 }
133
134
135 static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
136 {
137 return make_pair(!a.second.keyType, a.second.id) <
138 make_pair(!b.second.keyType, b.second.id);
139 }
140
141 DNSSECPrivateKey DNSSECKeeper::getKeyById(const DNSName& zname, unsigned int id)
142 {
143 vector<DNSBackend::KeyData> keys;
144 d_keymetadb->getDomainKeys(zname, 0, keys);
145 for(const DNSBackend::KeyData& kd : keys) {
146 if(kd.id != id)
147 continue;
148
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;
154
155 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zname)) {
156 dpk.d_algorithm += 2;
157 }
158
159 return dpk;
160 }
161 throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toString()+"'");
162 }
163
164
165 bool DNSSECKeeper::removeKey(const DNSName& zname, unsigned int id)
166 {
167 clearCaches(zname);
168 return d_keymetadb->removeDomainKey(zname, id);
169 }
170
171 bool DNSSECKeeper::deactivateKey(const DNSName& zname, unsigned int id)
172 {
173 clearCaches(zname);
174 return d_keymetadb->deactivateDomainKey(zname, id);
175 }
176
177 bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
178 {
179 clearCaches(zname);
180 return d_keymetadb->activateDomainKey(zname, id);
181 }
182
183
184 void DNSSECKeeper::getFromMeta(const DNSName& zname, const std::string& key, std::string& value)
185 {
186 static int ttl = ::arg().asNum("domain-metadata-cache-ttl");
187 value.clear();
188 unsigned int now = time(0);
189
190 if(!((++s_ops) % 100000)) {
191 cleanup();
192 }
193
194 if (ttl > 0) {
195 ReadLock l(&s_metacachelock);
196
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;
200 return;
201 }
202 }
203 vector<string> meta;
204 d_keymetadb->getDomainMetadata(zname, key, meta);
205 if(!meta.empty())
206 value=*meta.begin();
207
208 if (ttl > 0) {
209 METACacheEntry nce;
210 nce.d_domain=zname;
211 nce.d_ttd = now + ttl;
212 nce.d_key= key;
213 nce.d_value = value;
214 {
215 WriteLock l(&s_metacachelock);
216 replacing_insert(s_metacache, nce);
217 }
218 }
219 }
220
221 void DNSSECKeeper::getSoaEdit(const DNSName& zname, std::string& value)
222 {
223 static const string soaEdit(::arg()["default-soa-edit"]);
224 static const string soaEditSigned(::arg()["default-soa-edit-signed"]);
225
226 getFromMeta(zname, "SOA-EDIT", value);
227
228 if ((!soaEdit.empty() || !soaEditSigned.empty()) && value.empty() && !isPresigned(zname)) {
229 if (!soaEditSigned.empty() && isSecuredZone(zname))
230 value=soaEditSigned;
231 if (value.empty())
232 value=soaEdit;
233 }
234
235 return;
236 }
237
238 uint64_t DNSSECKeeper::dbdnssecCacheSizes(const std::string& str)
239 {
240 if(str=="meta-cache-size") {
241 ReadLock l(&s_metacachelock);
242 return s_metacache.size();
243 }
244 else if(str=="key-cache-size") {
245 ReadLock l(&s_keycachelock);
246 return s_keycache.size();
247 }
248 return (uint64_t)-1;
249 }
250
251 bool DNSSECKeeper::getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
252 {
253 string value;
254 getFromMeta(zname, "NSEC3PARAM", value);
255 if(value.empty()) { // "no NSEC3"
256 return false;
257 }
258
259 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
260 if(ns3p) {
261 NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
262 *ns3p = *tmp;
263 delete tmp;
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;
267 }
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;
271 }
272 }
273 if(narrow) {
274 getFromMeta(zname, "NSEC3NARROW", value);
275 *narrow = (value=="1");
276 }
277 return true;
278 }
279
280 bool DNSSECKeeper::setNSEC3PARAM(const DNSName& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
281 {
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'");
285
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'");
288
289 clearCaches(zname);
290 string descr = ns3p.getZoneRepresentation();
291 vector<string> meta;
292 meta.push_back(descr);
293 if (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", meta)) {
294 meta.clear();
295
296 if(narrow)
297 meta.push_back("1");
298
299 return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta);
300 }
301 return false;
302 }
303
304 bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName& zname)
305 {
306 clearCaches(zname);
307 return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>()));
308 }
309
310
311 bool DNSSECKeeper::setPresigned(const DNSName& zname)
312 {
313 clearCaches(zname);
314 vector<string> meta;
315 meta.push_back("1");
316 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta);
317 }
318
319 bool DNSSECKeeper::unsetPresigned(const DNSName& zname)
320 {
321 clearCaches(zname);
322 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>());
323 }
324
325 /**
326 * Add domainmetadata to allow publishing CDS records for zone zname
327 *
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
331 * verbatim
332 * @return true if the data was inserted, false otherwise
333 */
334 bool DNSSECKeeper::setPublishCDS(const DNSName& zname, const string& digestAlgos)
335 {
336 clearCaches(zname);
337 vector<string> meta;
338 meta.push_back(digestAlgos);
339 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta);
340 }
341
342 /**
343 * Remove domainmetadata to stop publishing CDS records for zone zname
344 *
345 * @param zname DNSName of the zone
346 * @return true if the operation was successful, false otherwise
347 */
348 bool DNSSECKeeper::unsetPublishCDS(const DNSName& zname)
349 {
350 clearCaches(zname);
351 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>());
352 }
353
354 /**
355 * Add domainmetadata to allow publishing CDNSKEY records.for zone zname
356 *
357 * @param zname DNSName of the zone
358 * @return true if the data was inserted, false otherwise
359 */
360 bool DNSSECKeeper::setPublishCDNSKEY(const DNSName& zname)
361 {
362 clearCaches(zname);
363 vector<string> meta;
364 meta.push_back("1");
365 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta);
366 }
367
368 /**
369 * Remove domainmetadata to stop publishing CDNSKEY records for zone zname
370 *
371 * @param zname DNSName of the zone
372 * @return true if the operation was successful, false otherwise
373 */
374 bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname)
375 {
376 clearCaches(zname);
377 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>());
378 }
379
380 /**
381 * Returns all keys that are used to sign the DNSKEY RRSet in a zone
382 *
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)
386 */
387 DNSSECKeeper::keyset_t DNSSECKeeper::getEntryPoints(const DNSName& zname)
388 {
389 DNSSECKeeper::keyset_t ret;
390 DNSSECKeeper::keyset_t keys = getKeys(zname);
391
392 for(auto const &keymeta : keys)
393 if(keymeta.second.active && (keymeta.second.keyType == KSK || keymeta.second.keyType == CSK))
394 ret.push_back(keymeta);
395 return ret;
396 }
397
398 DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, bool useCache)
399 {
400 static int ttl = ::arg().asNum("dnssec-key-cache-ttl");
401 unsigned int now = time(0);
402
403 if(!((++s_ops) % 100000)) {
404 cleanup();
405 }
406
407 if (useCache && ttl > 0) {
408 ReadLock l(&s_keycachelock);
409 keycache_t::const_iterator iter = s_keycache.find(zone);
410
411 if(iter != s_keycache.end() && iter->d_ttd > now) {
412 keyset_t ret;
413 for(const keyset_t::value_type& value : iter->d_keys)
414 ret.push_back(value);
415 return ret;
416 }
417 }
418
419 keyset_t retkeyset;
420 vector<DNSBackend::KeyData> dbkeyset;
421
422 d_keymetadb->getDomainKeys(zone, 0, dbkeyset);
423
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;
430
431 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content)));
432
433 if(keydata.active) {
434 if(keydata.flags == 257)
435 algoSEP.insert(dkrc.d_algorithm);
436 else
437 algoNoSEP.insert(dkrc.d_algorithm);
438 }
439 }
440 set_intersection(algoSEP.begin(), algoSEP.end(), algoNoSEP.begin(), algoNoSEP.end(), std::back_inserter(algoHasSeparateKSK));
441
442 for(DNSBackend::KeyData& kd : dbkeyset)
443 {
444 DNSSECPrivateKey dpk;
445 DNSKEYRecordContent dkrc;
446
447 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
448
449 dpk.d_flags = kd.flags;
450 dpk.d_algorithm = dkrc.d_algorithm;
451 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zone))
452 dpk.d_algorithm+=2;
453
454 KeyMetaData kmd;
455
456 kmd.active = kd.active;
457 kmd.hasSEPBit = (kd.flags == 257);
458 kmd.id = kd.id;
459
460 if (find(algoHasSeparateKSK.begin(), algoHasSeparateKSK.end(), dpk.d_algorithm) == algoHasSeparateKSK.end())
461 kmd.keyType = CSK;
462 else if(kmd.hasSEPBit)
463 kmd.keyType = KSK;
464 else
465 kmd.keyType = ZSK;
466
467 retkeyset.push_back(make_pair(dpk, kmd));
468 }
469 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
470
471 if (ttl > 0) {
472 KeyCacheEntry kce;
473 kce.d_domain=zone;
474 kce.d_keys = retkeyset;
475 kce.d_ttd = now + ttl;
476 {
477 WriteLock l(&s_keycachelock);
478 replacing_insert(s_keycache, kce);
479 }
480 }
481
482 return retkeyset;
483 }
484
485 bool DNSSECKeeper::checkKeys(const DNSName& zone)
486 {
487 vector<DNSBackend::KeyData> dbkeyset;
488 d_keymetadb->getDomainKeys(zone, 0, dbkeyset);
489
490 for(const DNSBackend::KeyData &keydata : dbkeyset) {
491 DNSKEYRecordContent dkrc;
492 shared_ptr<DNSCryptoKeyEngine> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content));
493 if (!dke->checkKey()) {
494 return false;
495 }
496 }
497
498 return true;
499 }
500
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)
504 {
505 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
506 SOAData sd;
507 if(!db.getSOAUncached(signer, sd)) {
508 DLOG(L<<"Could not get SOA for domain"<<endl);
509 return false;
510 }
511 db.lookup(QType(QType::RRSIG), wildcardname.countLabels() ? wildcardname : qname, NULL, sd.domain_id);
512 DNSResourceRecord rr;
513 while(db.get(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())
520 rr.qname = qname;
521 rr.d_place = signPlace;
522 rr.ttl = signTTL;
523 rrsigs.push_back(rr);
524 }
525 // else cerr<<"Skipping!"<<endl;
526 }
527 return true;
528 }
529
530 bool DNSSECKeeper::TSIGGrantsAccess(const DNSName& zone, const DNSName& keyname)
531 {
532 vector<string> allowed;
533
534 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
535
536 for(const string& dbkey : allowed) {
537 if(DNSName(dbkey)==keyname)
538 return true;
539 }
540 return false;
541 }
542
543 bool DNSSECKeeper::getTSIGForAccess(const DNSName& zone, const string& master, DNSName* keyname)
544 {
545 vector<string> keynames;
546 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
547 keyname->trimToLabels(0);
548
549 // XXX FIXME this should check for a specific master!
550 for(const string& dbkey : keynames) {
551 *keyname=DNSName(dbkey);
552 return true;
553 }
554 return false;
555 }
556
557 void DNSSECKeeper::cleanup()
558 {
559 struct timeval now;
560 Utility::gettimeofday(&now, 0);
561
562 if(now.tv_sec - s_last_prune > (time_t)(30)) {
563 {
564 WriteLock l(&s_metacachelock);
565 pruneCollection(s_metacache, ::arg().asNum("max-cache-entries"));
566 }
567 {
568 WriteLock l(&s_keycachelock);
569 pruneCollection(s_keycache, ::arg().asNum("max-cache-entries"));
570 }
571 s_last_prune=time(0);
572 }
573 }