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