]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dbdnsseckeeper.cc
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / dbdnsseckeeper.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "dnsseckeeper.hh"
26 #include "dnssecinfra.hh"
27 #include "ueberbackend.hh"
28 #include "statbag.hh"
29 #include <iostream>
30
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <fstream>
34 #include <unordered_map>
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 "base32.hh"
40 #include "base64.hh"
41 #include "cachecleaner.hh"
42 #include "arguments.hh"
43
44
45 using namespace boost::assign;
46 #include "namespaces.hh"
47
48
49 SharedLockGuarded<DNSSECKeeper::keycache_t> DNSSECKeeper::s_keycache;
50 SharedLockGuarded<DNSSECKeeper::metacache_t> DNSSECKeeper::s_metacache;
51 int64_t DNSSECKeeper::s_metaCacheCleanActions = 0;
52 AtomicCounter DNSSECKeeper::s_ops;
53 time_t DNSSECKeeper::s_last_prune;
54 size_t DNSSECKeeper::s_maxEntries = 0;
55
56 bool DNSSECKeeper::doesDNSSEC()
57 {
58 return d_keymetadb->doesDNSSEC();
59 }
60
61 bool DNSSECKeeper::isSecuredZone(const DNSName& zone, bool useCache)
62 {
63 if(isPresigned(zone, useCache))
64 return true;
65
66 keyset_t keys = getKeys(zone); // does the cache
67
68 for(keyset_t::value_type& val : keys) {
69 if(val.second.active) {
70 return true;
71 }
72 }
73 return false;
74 }
75
76 bool DNSSECKeeper::isPresigned(const DNSName& name, bool useCache)
77 {
78 string meta;
79 if (useCache) {
80 getFromMeta(name, "PRESIGNED", meta);
81 }
82 else {
83 getFromMetaNoCache(name, "PRESIGNED", meta);
84 }
85 return meta=="1";
86 }
87
88
89 bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active, bool published)
90 {
91 if(!bits) {
92 if(algorithm <= 10)
93 throw runtime_error("Creating an algorithm " +std::to_string(algorithm)+" ("+algorithm2name(algorithm)+") key requires the size (in bits) to be passed.");
94 else {
95 if(algorithm == DNSSECKeeper::ECCGOST || algorithm == DNSSECKeeper::ECDSA256 || algorithm == DNSSECKeeper::ED25519)
96 bits = 256;
97 else if(algorithm == DNSSECKeeper::ECDSA384)
98 bits = 384;
99 else if(algorithm == DNSSECKeeper::ED448)
100 bits = 456;
101 else {
102 throw runtime_error("Can not guess key size for algorithm "+std::to_string(algorithm));
103 }
104 }
105 }
106 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm));
107 try{
108 dpk->create(bits);
109 } catch (const std::runtime_error& error){
110 throw runtime_error("The algorithm does not support the given bit size.");
111 }
112 DNSSECPrivateKey dspk;
113 dspk.setKey(dpk, setSEPBit ? 257 : 256, algorithm);
114 return addKey(name, dspk, id, active, published) && clearKeyCache(name);
115 }
116
117 void DNSSECKeeper::clearAllCaches() {
118 s_keycache.write_lock()->clear();
119 s_metacache.write_lock()->clear();
120 }
121
122 /* This function never fails, the return value is to simplify call chains
123 elsewhere so we can do mutate<cache> && clear<cache> */
124 bool DNSSECKeeper::clearKeyCache(const DNSName& name)
125 {
126 s_keycache.write_lock()->erase(name);
127 return true;
128 }
129
130 bool DNSSECKeeper::clearMetaCache(const DNSName& name)
131 {
132 s_metacache.write_lock()->erase(name);
133 ++s_metaCacheCleanActions;
134 return true;
135 }
136
137 void DNSSECKeeper::clearCaches(const DNSName& name)
138 {
139 (void)clearKeyCache(name);
140 (void)clearMetaCache(name);
141 }
142
143 bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active, bool published)
144 {
145 DNSBackend::KeyData kd;
146 kd.flags = dpk.getFlags(); // the dpk doesn't get stored, only they key part
147 kd.active = active;
148 kd.published = published;
149 kd.content = dpk.getKey()->convertToISC();
150 // now store it
151 return d_keymetadb->addDomainKey(name, kd, id) && clearKeyCache(name);
152 }
153
154
155 static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
156 {
157 return pair(!a.second.keyType, a.second.id) <
158 pair(!b.second.keyType, b.second.id);
159 }
160
161 DNSSECPrivateKey DNSSECKeeper::getKeyById(const DNSName& zname, unsigned int id)
162 {
163 vector<DNSBackend::KeyData> keys;
164 d_keymetadb->getDomainKeys(zname, keys);
165 for(const DNSBackend::KeyData& kd : keys) {
166 if(kd.id != id)
167 continue;
168
169 DNSKEYRecordContent dkrc;
170 auto key = shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content));
171 DNSSECPrivateKey dpk;
172 dpk.setKey(key, kd.flags, dkrc.d_algorithm);
173
174 return dpk;
175 }
176 throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toLogString()+"'");
177 }
178
179
180 bool DNSSECKeeper::removeKey(const DNSName& zname, unsigned int id)
181 {
182 return d_keymetadb->removeDomainKey(zname, id) && clearKeyCache(zname);
183 }
184
185 bool DNSSECKeeper::deactivateKey(const DNSName& zname, unsigned int id)
186 {
187 return d_keymetadb->deactivateDomainKey(zname, id) && clearKeyCache(zname);
188 }
189
190 bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
191 {
192 return d_keymetadb->activateDomainKey(zname, id) && clearKeyCache(zname);
193 }
194
195 bool DNSSECKeeper::unpublishKey(const DNSName& zname, unsigned int id)
196 {
197 return d_keymetadb->unpublishDomainKey(zname, id) && clearKeyCache(zname);
198 }
199
200 bool DNSSECKeeper::publishKey(const DNSName& zname, unsigned int id)
201 {
202 return d_keymetadb->publishDomainKey(zname, id) && clearKeyCache(zname);
203 }
204
205 void DNSSECKeeper::getFromMetaOrDefault(const DNSName& zname, const std::string& key, std::string& value, const std::string& defaultvalue)
206 {
207 if (getFromMeta(zname, key, value))
208 return;
209 else
210 value = defaultvalue;
211 }
212
213 bool DNSSECKeeper::getFromMeta(const DNSName& zname, const std::string& key, std::string& value)
214 {
215 if (d_metaUpdate) {
216 if (d_keymetadb->inTransaction()) {
217 throw runtime_error("DNSSECKeeper::getFromMeta() called after an update from within a transaction.");
218 }
219 d_metaUpdate=false;
220 }
221
222 static int ttl = ::arg().asNum("zone-metadata-cache-ttl");
223
224 if(!((++s_ops) % 100000)) {
225 cleanup();
226 }
227
228 value.clear();
229 time_t now = time(nullptr);
230
231 bool ret = false;
232 bool fromCache = false;
233 METAValues meta;
234
235 if (ttl) {
236 auto metacache = s_metacache.read_lock();
237 auto iter = metacache->find(zname);
238 if(iter != metacache->end() && iter->d_ttd > now) {
239 meta = iter->d_value;
240 fromCache = true;
241 }
242 else {
243 d_metaCacheCleanAction = s_metaCacheCleanActions;
244 }
245 }
246
247 if (!fromCache) {
248 d_keymetadb->getAllDomainMetadata(zname, meta);
249 }
250
251 auto iter = meta.find(key);
252 if (iter != meta.end()) {
253 if (!iter->second.empty()) {
254 value = *iter->second.begin();
255 }
256 ret = true;
257 }
258
259 if (ttl && !fromCache) {
260 METACacheEntry nce;
261 nce.d_domain=zname;
262 nce.d_ttd = now + ttl;
263 nce.d_value = std::move(meta);
264 {
265 auto metacache = s_metacache.write_lock();
266 if(d_metaCacheCleanAction != s_metaCacheCleanActions) {
267 return false;
268 }
269 lruReplacingInsert<SequencedTag>(*metacache, nce);
270 }
271 }
272
273 return ret;
274 }
275
276 bool DNSSECKeeper::getFromMetaNoCache(const DNSName& name, const std::string& kind, std::string& value)
277 {
278 std::vector<std::string> meta;
279 if (d_keymetadb->getDomainMetadata(name, kind, meta)) {
280 if(!meta.empty()) {
281 value = *meta.begin();
282 return true;
283 }
284 }
285 return false;
286 }
287
288 void DNSSECKeeper::getSoaEdit(const DNSName& zname, std::string& value, bool useCache)
289 {
290 static const string soaEdit(::arg()["default-soa-edit"]);
291 static const string soaEditSigned(::arg()["default-soa-edit-signed"]);
292
293 if (isPresigned(zname, useCache)) {
294 // SOA editing on a presigned zone never makes sense
295 return;
296 }
297
298 getFromMeta(zname, "SOA-EDIT", value);
299
300 if ((!soaEdit.empty() || !soaEditSigned.empty()) && value.empty()) {
301 if (!soaEditSigned.empty() && isSecuredZone(zname, useCache))
302 value=soaEditSigned;
303 if (value.empty())
304 value=soaEdit;
305 }
306
307 return;
308 }
309
310 uint64_t DNSSECKeeper::dbdnssecCacheSizes(const std::string& str)
311 {
312 if(str=="meta-cache-size") {
313 return s_metacache.read_lock()->size();
314 }
315 else if(str=="key-cache-size") {
316 return s_keycache.read_lock()->size();
317 }
318 return (uint64_t)-1;
319 }
320
321 bool DNSSECKeeper::getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow, bool useCache)
322 {
323 string value;
324 if(useCache) {
325 getFromMeta(zname, "NSEC3PARAM", value);
326 }
327 else {
328 getFromMetaNoCache(zname, "NSEC3PARAM", value);
329 }
330 if(value.empty()) { // "no NSEC3"
331 return false;
332 }
333
334 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
335 if(ns3p != nullptr) {
336 *ns3p = NSEC3PARAMRecordContent(value);
337 if (ns3p->d_iterations > maxNSEC3Iterations && !isPresigned(zname, useCache)) {
338 ns3p->d_iterations = maxNSEC3Iterations;
339 g_log<<Logger::Error<<"Number of NSEC3 iterations for zone '"<<zname<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations<<endl;
340 }
341 if (ns3p->d_algorithm != 1) {
342 g_log<<Logger::Error<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p->d_algorithm)<<"', setting to 1 for zone '"<<zname<<"'."<<endl;
343 ns3p->d_algorithm = 1;
344 }
345 }
346 if(narrow != nullptr) {
347 if(useCache) {
348 getFromMeta(zname, "NSEC3NARROW", value);
349 }
350 else {
351 getFromMetaNoCache(zname, "NSEC3NARROW", value);
352 }
353 *narrow = (value=="1");
354 }
355 return true;
356 }
357
358 /*
359 * Check is the provided NSEC3PARAM record is something we can work with
360 *
361 * \param ns3p NSEC3PARAMRecordContent to check
362 * \param msg string to fill with an error message
363 * \return true on valid, false otherwise
364 */
365 bool DNSSECKeeper::checkNSEC3PARAM(const NSEC3PARAMRecordContent& ns3p, string& msg)
366 {
367 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
368 bool ret = true;
369 if (ns3p.d_iterations > maxNSEC3Iterations) {
370 msg += "Number of NSEC3 iterations is above 'max-nsec3-iterations'.";
371 ret = false;
372 }
373
374 if (ns3p.d_algorithm != 1) {
375 if (!ret)
376 msg += ' ';
377 msg += "Invalid hash algorithm for NSEC3: '"+std::to_string(ns3p.d_algorithm)+"', the only valid value is '1'.";
378 ret = false;
379 }
380
381 return ret;
382 }
383
384 bool DNSSECKeeper::setNSEC3PARAM(const DNSName& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
385 {
386 if (d_keymetadb->inTransaction()) {
387 d_metaUpdate = true;
388 }
389
390 string error_msg = "";
391 if (!checkNSEC3PARAM(ns3p, error_msg))
392 throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toLogString()+"' are invalid: " + error_msg);
393
394 string descr = ns3p.getZoneRepresentation();
395 vector<string> meta;
396 meta.push_back(descr);
397 if (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", meta)) {
398 meta.clear();
399
400 if(narrow)
401 meta.push_back("1");
402
403 return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta) && clearMetaCache(zname);
404 }
405 return false;
406 }
407
408 bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName& zname)
409 {
410 if (d_keymetadb->inTransaction()) {
411 d_metaUpdate = true;
412 }
413
414 return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>())) && clearMetaCache(zname);
415 }
416
417
418 bool DNSSECKeeper::setPresigned(const DNSName& zname)
419 {
420 if (d_keymetadb->inTransaction()) {
421 d_metaUpdate = true;
422 }
423
424 vector<string> meta;
425 meta.push_back("1");
426 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta) && clearMetaCache(zname);
427 }
428
429 bool DNSSECKeeper::unsetPresigned(const DNSName& zname)
430 {
431 if (d_keymetadb->inTransaction()) {
432 d_metaUpdate = true;
433 }
434
435 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>()) && clearMetaCache(zname);
436 }
437
438 /**
439 * Add domainmetadata to allow publishing CDS records for zone zname
440 *
441 * @param zname DNSName of the zone
442 * @param digestAlgos string with comma-separated numbers that describe the
443 * used digest algorithms. This is copied to the database
444 * verbatim
445 * @return true if the data was inserted, false otherwise
446 */
447 bool DNSSECKeeper::setPublishCDS(const DNSName& zname, const string& digestAlgos)
448 {
449 if (d_keymetadb->inTransaction()) {
450 d_metaUpdate = true;
451 }
452
453 vector<string> meta;
454 meta.push_back(digestAlgos);
455 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta) && clearMetaCache(zname);
456 }
457
458 void DNSSECKeeper::getPublishCDS(const DNSName& zname, std::string& value)
459 {
460 getFromMetaOrDefault(zname, "PUBLISH-CDS", value, ::arg()["default-publish-cds"]);
461 }
462
463 /**
464 * Remove domainmetadata to stop publishing CDS records for zone zname
465 *
466 * @param zname DNSName of the zone
467 * @return true if the operation was successful, false otherwise
468 */
469 bool DNSSECKeeper::unsetPublishCDS(const DNSName& zname)
470 {
471 if (d_keymetadb->inTransaction()) {
472 d_metaUpdate = true;
473 }
474
475 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>()) && clearMetaCache(zname);
476 }
477
478 /**
479 * Add domainmetadata to allow publishing CDNSKEY records.for zone zname
480 *
481 * @param zname DNSName of the zone
482 * @return true if the data was inserted, false otherwise
483 */
484 bool DNSSECKeeper::setPublishCDNSKEY(const DNSName& zname, bool deleteAlg)
485 {
486 if (d_keymetadb->inTransaction()) {
487 d_metaUpdate = true;
488 }
489
490 vector<string> meta;
491 meta.push_back(deleteAlg ? "0" : "1");
492 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta) && clearMetaCache(zname);
493 }
494
495 void DNSSECKeeper::getPublishCDNSKEY(const DNSName& zname, std::string& value)
496 {
497 getFromMetaOrDefault(zname, "PUBLISH-CDNSKEY", value, ::arg()["default-publish-cdnskey"]);
498 }
499
500 /**
501 * Remove domainmetadata to stop publishing CDNSKEY records for zone zname
502 *
503 * @param zname DNSName of the zone
504 * @return true if the operation was successful, false otherwise
505 */
506 bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname)
507 {
508 if (d_keymetadb->inTransaction()) {
509 d_metaUpdate = true;
510 }
511
512 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>()) && clearMetaCache(zname);
513 }
514
515 /**
516 * Returns all keys that are used to sign the DNSKEY RRSet in a zone
517 *
518 * @param zname DNSName of the zone
519 * @return a keyset_t with all keys that are used to sign the DNSKEY
520 * RRSet (these are the entrypoint(s) to the zone)
521 */
522 DNSSECKeeper::keyset_t DNSSECKeeper::getEntryPoints(const DNSName& zname)
523 {
524 DNSSECKeeper::keyset_t ret;
525 DNSSECKeeper::keyset_t keys = getKeys(zname);
526
527 for(auto const &keymeta : keys)
528 if(keymeta.second.keyType == KSK || keymeta.second.keyType == CSK)
529 ret.push_back(keymeta);
530 return ret;
531 }
532
533 DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, bool useCache)
534 {
535 static int ttl = ::arg().asNum("dnssec-key-cache-ttl");
536 // coverity[store_truncates_time_t]
537 unsigned int now = time(nullptr);
538
539 if(!((++s_ops) % 100000)) {
540 cleanup();
541 }
542
543 if (useCache && ttl > 0) {
544 auto keycache = s_keycache.read_lock();
545 auto iter = keycache->find(zone);
546
547 if (iter != keycache->end() && iter->d_ttd > now) {
548 keyset_t ret;
549 ret.reserve(iter->d_keys.size());
550 for(const keyset_t::value_type& value : iter->d_keys)
551 ret.push_back(value);
552 return ret;
553 }
554 }
555
556 keyset_t retkeyset;
557 vector<DNSBackend::KeyData> dbkeyset;
558
559 d_keymetadb->getDomainKeys(zone, dbkeyset);
560
561 // Determine the algorithms that have a KSK/ZSK split
562 set<uint8_t> algoSEP, algoNoSEP;
563 vector<uint8_t> algoHasSeparateKSK;
564 for(const DNSBackend::KeyData &keydata : dbkeyset) {
565 DNSKEYRecordContent dkrc;
566 auto key = shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content));
567 DNSSECPrivateKey dpk;
568 dpk.setKey(key, dkrc.d_algorithm);
569
570 if(keydata.active) {
571 if(keydata.flags == 257)
572 algoSEP.insert(dkrc.d_algorithm);
573 else
574 algoNoSEP.insert(dkrc.d_algorithm);
575 }
576 }
577 set_intersection(algoSEP.begin(), algoSEP.end(), algoNoSEP.begin(), algoNoSEP.end(), std::back_inserter(algoHasSeparateKSK));
578 retkeyset.reserve(dbkeyset.size());
579
580 for(DNSBackend::KeyData& kd : dbkeyset)
581 {
582 DNSKEYRecordContent dkrc;
583 auto key = shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content));
584 DNSSECPrivateKey dpk;
585 dpk.setKey(key, kd.flags, dkrc.d_algorithm);
586
587 KeyMetaData kmd;
588
589 kmd.active = kd.active;
590 kmd.published = kd.published;
591 kmd.hasSEPBit = (kd.flags == 257);
592 kmd.id = kd.id;
593
594 if (find(algoHasSeparateKSK.begin(), algoHasSeparateKSK.end(), dpk.getAlgorithm()) == algoHasSeparateKSK.end())
595 kmd.keyType = CSK;
596 else if(kmd.hasSEPBit)
597 kmd.keyType = KSK;
598 else
599 kmd.keyType = ZSK;
600
601 retkeyset.emplace_back(dpk, kmd);
602 }
603 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
604
605 if (ttl > 0) {
606 KeyCacheEntry kce;
607 kce.d_domain=zone;
608 kce.d_keys = retkeyset;
609 kce.d_ttd = now + ttl;
610 {
611 lruReplacingInsert<SequencedTag>(*(s_keycache.write_lock()), kce);
612 }
613 }
614
615 return retkeyset;
616 }
617
618 bool DNSSECKeeper::checkKeys(const DNSName& zone, std::optional<std::reference_wrapper<std::vector<std::string>>> errorMessages)
619 {
620 vector<DNSBackend::KeyData> dbkeyset;
621 d_keymetadb->getDomainKeys(zone, dbkeyset);
622 bool retval = true;
623
624 for(const DNSBackend::KeyData &keydata : dbkeyset) {
625 DNSKEYRecordContent dkrc;
626 auto dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content);
627 retval = dke->checkKey(errorMessages) && retval;
628 }
629
630 return retval;
631 }
632
633 void DNSSECKeeper::getPreRRSIGs(UeberBackend& db, vector<DNSZoneRecord>& rrs, uint32_t signTTL)
634 {
635 if(rrs.empty()) {
636 return;
637 }
638
639 const auto rr = *rrs.rbegin();
640
641 DNSZoneRecord dzr;
642
643 db.lookup(QType(QType::RRSIG), !rr.wildcardname.empty() ? rr.wildcardname : rr.dr.d_name, rr.domain_id);
644 while(db.get(dzr)) {
645 auto rrsig = getRR<RRSIGRecordContent>(dzr.dr);
646 if (rrsig->d_type == rr.dr.d_type) {
647 if(!rr.wildcardname.empty()) {
648 dzr.dr.d_name = rr.dr.d_name;
649 }
650 dzr.dr.d_place = rr.dr.d_place;
651 dzr.dr.d_ttl = signTTL;
652
653 rrs.emplace_back(dzr);
654 }
655 }
656 }
657
658 bool DNSSECKeeper::TSIGGrantsAccess(const DNSName& zone, const DNSName& keyname)
659 {
660 vector<string> allowed;
661
662 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
663
664 for(const string& dbkey : allowed) {
665 if(DNSName(dbkey)==keyname)
666 return true;
667 }
668 return false;
669 }
670
671 bool DNSSECKeeper::getTSIGForAccess(const DNSName& zone, const ComboAddress& /* primary */, DNSName* keyname)
672 {
673 vector<string> keynames;
674 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
675 keyname->trimToLabels(0);
676
677 // XXX FIXME this should check for a specific primary!
678 for(const string& dbkey : keynames) {
679 *keyname=DNSName(dbkey);
680 return true;
681 }
682 return false;
683 }
684
685 bool DNSSECKeeper::unSecureZone(const DNSName& zone, string& error) {
686 // Not calling isSecuredZone(), as it will return false for zones with zero
687 // active keys.
688 DNSSECKeeper::keyset_t keyset=getKeys(zone);
689
690 if(keyset.empty()) {
691 error = "No keys for zone '" + zone.toLogString() + "'.";
692 return false;
693 }
694
695 for(auto& key : keyset) {
696 deactivateKey(zone, key.second.id);
697 removeKey(zone, key.second.id);
698 }
699
700 unsetNSEC3PARAM(zone);
701 unsetPresigned(zone);
702 return true;
703 }
704
705
706 struct RecordStatus
707 {
708 DNSName ordername;
709 bool auth{false};
710 bool update{false};
711 };
712
713
714 /* Rectifies the zone
715 *
716 * \param zone The zone to rectify
717 * \param error& A string where error messages are added
718 * \param info& A string where informational messages are added
719 * \param doTransaction Whether or not to wrap the rectify in a transaction
720 */
721 bool DNSSECKeeper::rectifyZone(const DNSName& zone, string& error, string& info, bool doTransaction) {
722 if (isPresigned(zone, doTransaction)) {
723 error = "Rectify presigned zone '"+zone.toLogString()+"' is not allowed/necessary.";
724 return false;
725 }
726
727 UeberBackend* B = d_keymetadb;
728 std::unique_ptr<UeberBackend> b;
729
730 if (d_ourDB) {
731 if (!doTransaction) {
732 error = "Can not rectify a zone with a new Ueberbackend inside a transaction.";
733 return false;
734 }
735 // We don't have a *full* Ueberbackend, just a key-only one.
736 // Let's create one and use it
737 b = std::make_unique<UeberBackend>();
738 B = b.get();
739 }
740
741 SOAData sd;
742
743 if(!B->getSOAUncached(zone, sd)) {
744 error = "No SOA known for '" + zone.toLogString() + "', is such a zone in the database?";
745 return false;
746 }
747
748 sd.db->list(zone, sd.domain_id);
749
750 ostringstream infostream;
751 DNSResourceRecord rr;
752 set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
753 std::unordered_map<DNSName,bool> nonterm;
754 vector<DNSResourceRecord> rrs;
755 std::unordered_map<DNSName,RecordStatus> rss;
756
757 NSEC3PARAMRecordContent ns3pr;
758 bool securedZone = isSecuredZone(zone, doTransaction);
759 bool haveNSEC3 = false, isOptOut = false, narrow = false;
760
761 if(securedZone) {
762 haveNSEC3 = getNSEC3PARAM(zone, &ns3pr, &narrow, doTransaction);
763 isOptOut = (haveNSEC3 && ns3pr.d_flags);
764 }
765
766 while(sd.db->get(rr)) {
767 rr.qname.makeUsLowerCase();
768
769 auto res=rss.insert({rr.qname,{rr.ordername, rr.auth, rr.ordername.empty() != (!securedZone || narrow)}}); // only a set ordername is reliable
770 if (!res.second && !res.first->second.update) {
771 res.first->second.update = res.first->second.auth != rr.auth || res.first->second.ordername != rr.ordername;
772 }
773 else if ((!securedZone || narrow) && rr.qname == zone) {
774 res.first->second.update = true;
775 }
776
777 if (rr.qtype.getCode())
778 {
779 qnames.insert(rr.qname);
780 if(rr.qtype.getCode() == QType::NS && rr.qname != zone)
781 nsset.insert(rr.qname);
782 if(rr.qtype.getCode() == QType::DS)
783 dsnames.insert(rr.qname);
784 rrs.emplace_back(rr);
785 }
786 else
787 delnonterm.insert(std::move(rr.qname));
788 }
789
790 if(securedZone) {
791 if(!haveNSEC3) {
792 infostream<<"Adding NSEC ordering information for zone '"<<zone<<"'";
793 }
794 else if(!narrow) {
795 if(!isOptOut) {
796 infostream<<"Adding NSEC3 hashed ordering information for zone '"<<zone<<"'";
797 }
798 else {
799 infostream<<"Adding NSEC3 opt-out hashed ordering information for zone '"<<zone<<"'";
800 }
801 } else {
802 infostream<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields for zone '"<<zone<<"'";
803 }
804 }
805 else {
806 infostream<<"Adding empty non-terminals for non-DNSSEC zone '"<<zone<<"'";
807 }
808
809 set<DNSName> nsec3set;
810 if (haveNSEC3 && (!narrow || !isOptOut)) {
811 for (auto &loopRR: rrs) {
812 bool skip=false;
813 DNSName shorter = loopRR.qname;
814 if (shorter != zone && shorter.chopOff() && shorter != zone) {
815 do {
816 if(nsset.count(shorter)) {
817 skip=true;
818 break;
819 }
820 } while(shorter.chopOff() && shorter != zone);
821 }
822 shorter = loopRR.qname;
823 if(!skip && (loopRR.qtype.getCode() != QType::NS || !isOptOut)) {
824
825 do {
826 if(!nsec3set.count(shorter)) {
827 nsec3set.insert(shorter);
828 }
829 } while(shorter != zone && shorter.chopOff());
830 }
831 }
832 }
833
834 if (doTransaction)
835 sd.db->startTransaction(zone, -1);
836
837 bool realrr=true;
838 bool doent=true;
839 int updates=0;
840 uint32_t maxent = ::arg().asNum("max-ent-entries");
841
842 dononterm:;
843 std::unordered_map<DNSName,RecordStatus>::const_iterator it;
844 for (const auto& qname: qnames)
845 {
846 bool auth=true;
847 DNSName ordername;
848 auto shorter(qname);
849
850 if(realrr) {
851 do {
852 if(nsset.count(shorter)) {
853 auth=false;
854 break;
855 }
856 } while(shorter.chopOff());
857 } else {
858 auth=nonterm.find(qname)->second;
859 }
860
861 if(haveNSEC3) // NSEC3
862 {
863 if(nsec3set.count(qname)) {
864 if(!narrow)
865 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, qname)));
866 if(!realrr && !isOptOut)
867 auth=true;
868 }
869 }
870 else if (realrr && securedZone) // NSEC
871 {
872 ordername=qname.makeRelative(zone);
873 }
874
875 it = rss.find(qname);
876 if(it == rss.end() || it->second.update || it->second.auth != auth || it->second.ordername != ordername) {
877 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, auth);
878 ++updates;
879 }
880
881 if(realrr)
882 {
883 if (dsnames.count(qname)) {
884 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, true, QType::DS);
885 ++updates;
886 }
887 if (!auth || nsset.count(qname)) {
888 ordername.clear();
889 if(isOptOut && !dsnames.count(qname)){
890 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::NS);
891 ++updates;
892 }
893 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::A);
894 ++updates;
895 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::AAAA);
896 ++updates;
897 }
898
899 if(doent)
900 {
901 shorter=qname;
902 while(shorter!=zone && shorter.chopOff())
903 {
904 if(!qnames.count(shorter))
905 {
906 if(!(maxent))
907 {
908 g_log<<Logger::Warning<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
909 insnonterm.clear();
910 delnonterm.clear();
911 doent=false;
912 break;
913 }
914
915 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
916 insnonterm.insert(shorter);
917 else
918 delnonterm.erase(shorter);
919
920 if (!nonterm.count(shorter)) {
921 nonterm.insert(pair<DNSName, bool>(shorter, auth));
922 --maxent;
923 } else if (auth)
924 nonterm[shorter]=true;
925 }
926 }
927 }
928 }
929 }
930
931 if(realrr)
932 {
933 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
934 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
935 {
936 sd.db->updateEmptyNonTerminals(sd.domain_id, insnonterm, delnonterm, !doent);
937 }
938 if(doent)
939 {
940 realrr=false;
941 qnames.clear();
942 for(const auto& nt : nonterm){
943 qnames.insert(nt.first);
944 }
945 goto dononterm;
946 }
947 }
948
949 if (doTransaction)
950 sd.db->commitTransaction();
951
952 infostream<<", "<<updates<<" updates";
953 info = infostream.str();
954 return true;
955 }
956
957 void DNSSECKeeper::cleanup()
958 {
959 struct timeval now;
960 Utility::gettimeofday(&now, nullptr);
961
962 if(now.tv_sec - s_last_prune > (time_t)(30)) {
963 {
964 pruneCollection<SequencedTag>((*s_metacache.write_lock()), s_maxEntries);
965 }
966 {
967 pruneCollection<SequencedTag>((*s_keycache.write_lock()), s_maxEntries);
968 }
969 s_last_prune = time(nullptr);
970 }
971 }
972
973 void DNSSECKeeper::setMaxEntries(size_t maxEntries)
974 {
975 s_maxEntries = maxEntries;
976 #if BOOST_VERSION >= 105600
977 s_keycache.write_lock()->get<KeyCacheTag>().reserve(s_maxEntries);
978 #endif /* BOOST_VERSION >= 105600 */
979 }