]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dbdnsseckeeper.cc
Merge pull request #5892 from pieterlexis/make-travis-happy
[thirdparty/pdns.git] / pdns / dbdnsseckeeper.cc
CommitLineData
882358c8 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
c0273500
BH
25#include "dnsseckeeper.hh"
26#include "dnssecinfra.hh"
27#include "ueberbackend.hh"
28#include "statbag.hh"
29#include <iostream>
fa8fd4d2 30
c0273500
BH
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <fstream>
34#include <boost/algorithm/string.hpp>
35#include <boost/format.hpp>
36#include <boost/assign/std/vector.hpp> // for 'operator+=()'
37#include <boost/assign/list_inserter.hpp>
25b1ce13 38#include "base32.hh"
78bcb858 39#include "base64.hh"
157f806e
PD
40#include "cachecleaner.hh"
41#include "arguments.hh"
d473cb9a
BH
42
43
c0273500 44using namespace boost::assign;
9cb28241 45#include "namespaces.hh"
de43ec0f 46
c0273500 47
40fe813d 48DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
d6f3dcdc 49DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
18a144ef
BH
50pthread_rwlock_t DNSSECKeeper::s_metacachelock = PTHREAD_RWLOCK_INITIALIZER;
51pthread_rwlock_t DNSSECKeeper::s_keycachelock = PTHREAD_RWLOCK_INITIALIZER;
16f7d28d 52AtomicCounter DNSSECKeeper::s_ops;
157f806e 53time_t DNSSECKeeper::s_last_prune;
d473cb9a 54
7fa35c07
KM
55bool DNSSECKeeper::doesDNSSEC()
56{
57 return d_keymetadb->doesDNSSEC();
58}
59
675fa24c 60bool DNSSECKeeper::isSecuredZone(const DNSName& zone)
c0273500 61{
d3e7090c 62 if(isPresigned(zone))
d6f3dcdc 63 return true;
157f806e 64
8a95b04c
KM
65 keyset_t keys = getKeys(zone); // does the cache
66
ef7cd021 67 for(keyset_t::value_type& val : keys) {
ade1b1e9
BH
68 if(val.second.active) {
69 return true;
70 }
c0273500 71 }
ade1b1e9 72 return false;
c0273500
BH
73}
74
675fa24c 75bool DNSSECKeeper::isPresigned(const DNSName& name)
d3e7090c 76{
9cb28241
BH
77 string meta;
78 getFromMeta(name, "PRESIGNED", meta);
79 return meta=="1";
d3e7090c 80}
c0273500 81
82cc0761 82bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active)
c0273500 83{
022e5e0b
BH
84 if(!bits) {
85 if(algorithm <= 10)
685a70c2 86 throw runtime_error("Creating an algorithm " +std::to_string(algorithm)+" ("+algorithm2name(algorithm)+") key requires the size (in bits) to be passed.");
022e5e0b 87 else {
9d3727e0 88 if(algorithm == 12 || algorithm == 13 || algorithm == 15) // GOST, ECDSAP256SHA256, ED25519
022e5e0b 89 bits = 256;
45826dd7 90 else if(algorithm == 14) // ECDSAP384SHA384
022e5e0b 91 bits = 384;
21a8834a
KM
92 else if(algorithm == 16) // ED448
93 bits = 456;
022e5e0b 94 else {
685a70c2 95 throw runtime_error("Can not guess key size for algorithm "+std::to_string(algorithm));
022e5e0b
BH
96 }
97 }
98 }
699e6e37 99 DNSSECPrivateKey dspk;
b6bd795c 100 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm));
34aa40ce
BZ
101 try{
102 dpk->create(bits);
103 } catch (std::runtime_error error){
685a70c2 104 throw runtime_error("The algorithm does not support the given bit size.");
34aa40ce 105 }
699e6e37
BH
106 dspk.setKey(dpk);
107 dspk.d_algorithm = algorithm;
b6bd795c 108 dspk.d_flags = setSEPBit ? 257 : 256;
82cc0761 109 return addKey(name, dspk, id, active);
f7bcc763
BH
110}
111
627d2ca2
PD
112void DNSSECKeeper::clearAllCaches() {
113 {
18a144ef 114 WriteLock l(&s_keycachelock);
627d2ca2
PD
115 s_keycache.clear();
116 }
18a144ef 117 WriteLock l(&s_metacachelock);
627d2ca2
PD
118 s_metacache.clear();
119}
120
675fa24c 121void DNSSECKeeper::clearCaches(const DNSName& name)
5e91adff 122{
40fe813d 123 {
18a144ef 124 WriteLock l(&s_keycachelock);
40fe813d
BH
125 s_keycache.erase(name);
126 }
18a144ef 127 WriteLock l(&s_metacachelock);
0b7d7191 128 pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(tie(name));
d6f3dcdc
BH
129 while(range.first != range.second)
130 s_metacache.erase(range.first++);
5e91adff
BH
131}
132
133
82cc0761 134bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active)
f7bcc763 135{
5e91adff 136 clearCaches(name);
c0273500 137 DNSBackend::KeyData kd;
7ddd79a7 138 kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
c0273500 139 kd.active = active;
189bb9d2 140 kd.content = dpk.getKey()->convertToISC();
c0273500 141 // now store it
82cc0761 142 return d_keymetadb->addDomainKey(name, kd, id);
c0273500
BH
143}
144
145
146static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
147{
b6bd795c
PL
148 return make_pair(!a.second.keyType, a.second.id) <
149 make_pair(!b.second.keyType, b.second.id);
c0273500
BH
150}
151
675fa24c 152DNSSECPrivateKey DNSSECKeeper::getKeyById(const DNSName& zname, unsigned int id)
c0273500 153{
c0273500 154 vector<DNSBackend::KeyData> keys;
9c1c5d49 155 d_keymetadb->getDomainKeys(zname, keys);
ef7cd021 156 for(const DNSBackend::KeyData& kd : keys) {
c0273500
BH
157 if(kd.id != id)
158 continue;
159
160 DNSSECPrivateKey dpk;
699e6e37 161 DNSKEYRecordContent dkrc;
8d9f38f2 162 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
c0273500 163 dpk.d_flags = kd.flags;
a254438f 164 dpk.d_algorithm = dkrc.d_algorithm;
c0273500 165
a254438f
BH
166 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zname)) {
167 dpk.d_algorithm += 2;
168 }
c0273500
BH
169
170 return dpk;
171 }
335da0ba 172 throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toString()+"'");
c0273500
BH
173}
174
175
675fa24c 176bool DNSSECKeeper::removeKey(const DNSName& zname, unsigned int id)
c0273500 177{
5e91adff 178 clearCaches(zname);
a84a8203 179 return d_keymetadb->removeDomainKey(zname, id);
c0273500
BH
180}
181
675fa24c 182bool DNSSECKeeper::deactivateKey(const DNSName& zname, unsigned int id)
c0273500 183{
5e91adff 184 clearCaches(zname);
a84a8203 185 return d_keymetadb->deactivateDomainKey(zname, id);
c0273500
BH
186}
187
675fa24c 188bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
c0273500 189{
5e91adff 190 clearCaches(zname);
a84a8203 191 return d_keymetadb->activateDomainKey(zname, id);
c0273500
BH
192}
193
d6f3dcdc 194
675fa24c 195void DNSSECKeeper::getFromMeta(const DNSName& zname, const std::string& key, std::string& value)
c0273500 196{
030850a9 197 static int ttl = ::arg().asNum("domain-metadata-cache-ttl");
d6f3dcdc 198 value.clear();
5e91adff 199 unsigned int now = time(0);
157f806e 200
16f7d28d 201 if(!((++s_ops) % 100000)) {
157f806e
PD
202 cleanup();
203 }
204
030850a9 205 if (ttl > 0) {
18a144ef 206 ReadLock l(&s_metacachelock);
d473cb9a 207
d6f3dcdc
BH
208 metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
209 if(iter != s_metacache.end() && iter->d_ttd > now) {
210 value = iter->d_value;
211 return;
d473cb9a 212 }
22c5aa60 213 }
d473cb9a 214 vector<string> meta;
936eb34a 215 d_keymetadb->getDomainMetadata(zname, key, meta);
d6f3dcdc
BH
216 if(!meta.empty())
217 value=*meta.begin();
030850a9
RG
218
219 if (ttl > 0) {
220 METACacheEntry nce;
221 nce.d_domain=zname;
222 nce.d_ttd = now + ttl;
223 nce.d_key= key;
224 nce.d_value = value;
225 {
226 WriteLock l(&s_metacachelock);
227 replacing_insert(s_metacache, nce);
228 }
d473cb9a 229 }
d6f3dcdc
BH
230}
231
4192773a
KM
232void DNSSECKeeper::getSoaEdit(const DNSName& zname, std::string& value)
233{
234 static const string soaEdit(::arg()["default-soa-edit"]);
235 static const string soaEditSigned(::arg()["default-soa-edit-signed"]);
236
3ba10656
PD
237 if (isPresigned(zname)) {
238 // SOA editing on a presigned zone never makes sense
239 return;
240 }
241
4192773a
KM
242 getFromMeta(zname, "SOA-EDIT", value);
243
3ba10656 244 if ((!soaEdit.empty() || !soaEditSigned.empty()) && value.empty()) {
4192773a
KM
245 if (!soaEditSigned.empty() && isSecuredZone(zname))
246 value=soaEditSigned;
247 if (value.empty())
248 value=soaEdit;
249 }
250
251 return;
252}
253
e903706d 254uint64_t DNSSECKeeper::dbdnssecCacheSizes(const std::string& str)
255{
256 if(str=="meta-cache-size") {
257 ReadLock l(&s_metacachelock);
258 return s_metacache.size();
259 }
260 else if(str=="key-cache-size") {
261 ReadLock l(&s_keycachelock);
262 return s_keycache.size();
263 }
264 return (uint64_t)-1;
265}
266
675fa24c 267bool DNSSECKeeper::getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
d6f3dcdc
BH
268{
269 string value;
270 getFromMeta(zname, "NSEC3PARAM", value);
5935cede 271 if(value.empty()) { // "no NSEC3"
d6f3dcdc 272 return false;
5935cede 273 }
28b66a94
KM
274
275 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
c0273500 276 if(ns3p) {
f6a81077 277 *ns3p = NSEC3PARAMRecordContent(value);
28b66a94
KM
278 if (ns3p->d_iterations > maxNSEC3Iterations) {
279 ns3p->d_iterations = maxNSEC3Iterations;
f43c4448 280 L<<Logger::Error<<"Number of NSEC3 iterations for zone '"<<zname<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations<<endl;
28b66a94 281 }
c7fbe6c9
PL
282 if (ns3p->d_algorithm != 1) {
283 L<<Logger::Error<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p->d_algorithm)<<"', setting to 1 for zone '"<<zname<<"'."<<endl;
284 ns3p->d_algorithm = 1;
285 }
c0273500 286 }
d6f3dcdc
BH
287 if(narrow) {
288 getFromMeta(zname, "NSEC3NARROW", value);
289 *narrow = (value=="1");
290 }
c0273500
BH
291 return true;
292}
293
68fd1167
PL
294/*
295 * Check is the provided NSEC3PARAM record is something we can work with
296 *
297 * \param ns3p NSEC3PARAMRecordContent to check
298 * \param msg string to fill with an error message
299 * \return true on valid, false otherwise
300 */
301bool DNSSECKeeper::checkNSEC3PARAM(const NSEC3PARAMRecordContent& ns3p, string& msg)
c0273500 302{
28b66a94 303 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
68fd1167
PL
304 bool ret = true;
305 if (ns3p.d_iterations > maxNSEC3Iterations) {
306 msg += "Number of NSEC3 iterations is above 'max-nsec3-iterations'.";
307 ret = false;
308 }
28b66a94 309
68fd1167
PL
310 if (ns3p.d_algorithm != 1) {
311 if (!ret)
312 msg += ' ';
313 msg += "Invalid hash algorithm for NSEC3: '"+std::to_string(ns3p.d_algorithm)+"', the only valid value is '1'.";
314 ret = false;
315 }
316
317 return ret;
318}
319
320bool DNSSECKeeper::setNSEC3PARAM(const DNSName& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
321{
322 string error_msg = "";
323 if (!checkNSEC3PARAM(ns3p, error_msg))
324 throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toString()+"' are invalid: " + error_msg);
c7fbe6c9 325
5e91adff 326 clearCaches(zname);
c0273500
BH
327 string descr = ns3p.getZoneRepresentation();
328 vector<string> meta;
329 meta.push_back(descr);
a84a8203
PD
330 if (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", meta)) {
331 meta.clear();
332
333 if(narrow)
334 meta.push_back("1");
335
336 return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta);
337 }
338 return false;
c0273500
BH
339}
340
675fa24c 341bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName& zname)
c0273500 342{
5e91adff 343 clearCaches(zname);
a84a8203 344 return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>()));
c0273500
BH
345}
346
347
675fa24c 348bool DNSSECKeeper::setPresigned(const DNSName& zname)
d3e7090c
BH
349{
350 clearCaches(zname);
351 vector<string> meta;
352 meta.push_back("1");
a84a8203 353 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta);
d3e7090c
BH
354}
355
675fa24c 356bool DNSSECKeeper::unsetPresigned(const DNSName& zname)
d3e7090c
BH
357{
358 clearCaches(zname);
a84a8203 359 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>());
d3e7090c
BH
360}
361
ef542223
PL
362/**
363 * Add domainmetadata to allow publishing CDS records for zone zname
364 *
365 * @param zname DNSName of the zone
366 * @param digestAlgos string with comma-separated numbers that describe the
367 * used digest algorithms. This is copied to the database
368 * verbatim
369 * @return true if the data was inserted, false otherwise
370 */
371bool DNSSECKeeper::setPublishCDS(const DNSName& zname, const string& digestAlgos)
372{
373 clearCaches(zname);
374 vector<string> meta;
375 meta.push_back(digestAlgos);
0900d2d3 376 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta);
ef542223
PL
377}
378
379/**
380 * Remove domainmetadata to stop publishing CDS records for zone zname
381 *
382 * @param zname DNSName of the zone
383 * @return true if the operation was successful, false otherwise
384 */
385bool DNSSECKeeper::unsetPublishCDS(const DNSName& zname)
386{
387 clearCaches(zname);
0900d2d3 388 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>());
ef542223
PL
389}
390
088370cd
PL
391/**
392 * Add domainmetadata to allow publishing CDNSKEY records.for zone zname
393 *
394 * @param zname DNSName of the zone
395 * @return true if the data was inserted, false otherwise
396 */
397bool DNSSECKeeper::setPublishCDNSKEY(const DNSName& zname)
398{
399 clearCaches(zname);
400 vector<string> meta;
401 meta.push_back("1");
0900d2d3 402 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta);
088370cd
PL
403}
404
405/**
406 * Remove domainmetadata to stop publishing CDNSKEY records for zone zname
407 *
408 * @param zname DNSName of the zone
409 * @return true if the operation was successful, false otherwise
410 */
411bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname)
412{
413 clearCaches(zname);
0900d2d3 414 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>());
088370cd 415}
d3e7090c 416
f889ab99
PL
417/**
418 * Returns all keys that are used to sign the DNSKEY RRSet in a zone
419 *
9091cf89 420 * @param zname DNSName of the zone
f889ab99
PL
421 * @return a keyset_t with all keys that are used to sign the DNSKEY
422 * RRSet (these are the entrypoint(s) to the zone)
423 */
9091cf89 424DNSSECKeeper::keyset_t DNSSECKeeper::getEntryPoints(const DNSName& zname)
f889ab99
PL
425{
426 DNSSECKeeper::keyset_t ret;
9091cf89 427 DNSSECKeeper::keyset_t keys = getKeys(zname);
f889ab99 428
b6bd795c 429 for(auto const &keymeta : keys)
c74f51e2 430 if(keymeta.second.keyType == KSK || keymeta.second.keyType == CSK)
f889ab99 431 ret.push_back(keymeta);
f889ab99
PL
432 return ret;
433}
434
b6bd795c 435DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, bool useCache)
c0273500 436{
030850a9 437 static int ttl = ::arg().asNum("dnssec-key-cache-ttl");
5e91adff 438 unsigned int now = time(0);
157f806e 439
16f7d28d 440 if(!((++s_ops) % 100000)) {
157f806e
PD
441 cleanup();
442 }
443
030850a9 444 if (useCache && ttl > 0) {
18a144ef 445 ReadLock l(&s_keycachelock);
40fe813d 446 keycache_t::const_iterator iter = s_keycache.find(zone);
b6bd795c
PL
447
448 if(iter != s_keycache.end() && iter->d_ttd > now) {
40fe813d 449 keyset_t ret;
b6bd795c
PL
450 for(const keyset_t::value_type& value : iter->d_keys)
451 ret.push_back(value);
40fe813d 452 return ret;
7aff7ead 453 }
b6bd795c
PL
454 }
455
456 keyset_t retkeyset;
3971cf53 457 vector<DNSBackend::KeyData> dbkeyset;
b6bd795c 458
9c1c5d49 459 d_keymetadb->getDomainKeys(zone, dbkeyset);
b6bd795c
PL
460
461 // Determine the algorithms that have a KSK/ZSK split
462 set<uint8_t> algoSEP, algoNoSEP;
463 vector<uint8_t> algoHasSeparateKSK;
464 for(const DNSBackend::KeyData &keydata : dbkeyset) {
c0273500 465 DNSSECPrivateKey dpk;
b6bd795c
PL
466 DNSKEYRecordContent dkrc;
467
468 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content)));
c0273500 469
b6bd795c
PL
470 if(keydata.active) {
471 if(keydata.flags == 257)
472 algoSEP.insert(dkrc.d_algorithm);
473 else
474 algoNoSEP.insert(dkrc.d_algorithm);
475 }
476 }
477 set_intersection(algoSEP.begin(), algoSEP.end(), algoNoSEP.begin(), algoNoSEP.end(), std::back_inserter(algoHasSeparateKSK));
478
479 for(DNSBackend::KeyData& kd : dbkeyset)
480 {
481 DNSSECPrivateKey dpk;
699e6e37 482 DNSKEYRecordContent dkrc;
b6bd795c 483
8d9f38f2 484 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
b6bd795c 485
c0273500 486 dpk.d_flags = kd.flags;
a254438f 487 dpk.d_algorithm = dkrc.d_algorithm;
89c8b2ce 488 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zone))
a254438f 489 dpk.d_algorithm+=2;
b6bd795c 490
c0273500
BH
491 KeyMetaData kmd;
492
493 kmd.active = kd.active;
b6bd795c 494 kmd.hasSEPBit = (kd.flags == 257);
c0273500 495 kmd.id = kd.id;
b6bd795c
PL
496
497 if (find(algoHasSeparateKSK.begin(), algoHasSeparateKSK.end(), dpk.d_algorithm) == algoHasSeparateKSK.end())
498 kmd.keyType = CSK;
499 else if(kmd.hasSEPBit)
500 kmd.keyType = KSK;
501 else
502 kmd.keyType = ZSK;
503
504 retkeyset.push_back(make_pair(dpk, kmd));
c0273500 505 }
7aff7ead 506 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
b6bd795c 507
030850a9
RG
508 if (ttl > 0) {
509 KeyCacheEntry kce;
510 kce.d_domain=zone;
511 kce.d_keys = retkeyset;
512 kce.d_ttd = now + ttl;
513 {
514 WriteLock l(&s_keycachelock);
515 replacing_insert(s_keycache, kce);
516 }
40fe813d 517 }
030850a9 518
7aff7ead 519 return retkeyset;
c0273500
BH
520}
521
8ca3ea33
RG
522bool DNSSECKeeper::checkKeys(const DNSName& zone)
523{
524 vector<DNSBackend::KeyData> dbkeyset;
9c1c5d49 525 d_keymetadb->getDomainKeys(zone, dbkeyset);
8ca3ea33
RG
526
527 for(const DNSBackend::KeyData &keydata : dbkeyset) {
528 DNSKEYRecordContent dkrc;
529 shared_ptr<DNSCryptoKeyEngine> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content));
45c2bc60 530 if (!dke->checkKey()) {
8ca3ea33
RG
531 return false;
532 }
533 }
534
535 return true;
536}
537
675fa24c
PD
538bool DNSSECKeeper::getPreRRSIGs(UeberBackend& db, const DNSName& signer, const DNSName& qname,
539 const DNSName& wildcardname, const QType& qtype,
90ba52e0 540 DNSResourceRecord::Place signPlace, vector<DNSZoneRecord>& rrsigs, uint32_t signTTL)
d3e7090c 541{
f1485b68 542 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
232f0877 543 SOAData sd;
79ba7763 544 if(!db.getSOAUncached(signer, sd)) {
232f0877
CH
545 DLOG(L<<"Could not get SOA for domain"<<endl);
546 return false;
547 }
675fa24c 548 db.lookup(QType(QType::RRSIG), wildcardname.countLabels() ? wildcardname : qname, NULL, sd.domain_id);
90ba52e0 549 DNSZoneRecord rr;
550 while(db.get(rr)) {
551 auto rrsig = getRR<RRSIGRecordContent>(rr.dr);
552 if(rrsig->d_type == qtype.getCode() && rrsig->d_signer==signer) {
553 if (wildcardname.countLabels())
554 rr.dr.d_name = qname;
555 rr.dr.d_place = signPlace;
556 rr.dr.d_ttl = signTTL;
557 rrsigs.push_back(rr);
558 }
232f0877
CH
559 }
560 return true;
d3e7090c 561}
78bcb858 562
675fa24c 563bool DNSSECKeeper::TSIGGrantsAccess(const DNSName& zone, const DNSName& keyname)
78bcb858
BH
564{
565 vector<string> allowed;
566
936eb34a 567 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
78bcb858 568
ef7cd021 569 for(const string& dbkey : allowed) {
675fa24c 570 if(DNSName(dbkey)==keyname)
78bcb858
BH
571 return true;
572 }
573 return false;
574}
29b92d6f 575
675fa24c 576bool DNSSECKeeper::getTSIGForAccess(const DNSName& zone, const string& master, DNSName* keyname)
29b92d6f
BH
577{
578 vector<string> keynames;
936eb34a 579 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
675fa24c 580 keyname->trimToLabels(0);
29b92d6f
BH
581
582 // XXX FIXME this should check for a specific master!
ef7cd021 583 for(const string& dbkey : keynames) {
675fa24c 584 *keyname=DNSName(dbkey);
29b92d6f
BH
585 return true;
586 }
587 return false;
588}
157f806e 589
8d0f207b
PL
590/* Rectifies the zone
591 *
592 * \param zone The zone to rectify
593 * \param error& A string where error messages are added
594 * \param doTransaction Whether or not to wrap the rectify in a transaction
595 */
596bool DNSSECKeeper::rectifyZone(const DNSName& zone, string& error, bool doTransaction) {
25b1ce13
PL
597 if (isPresigned(zone)) {
598 error = "Rectify presigned zone '"+zone.toLogString()+"' is not allowed/necessary.";
599 return false;
600 }
601
a2e700d4
PL
602 UeberBackend* B = d_keymetadb;
603 std::unique_ptr<UeberBackend> b;
604
605 if (d_ourDB) {
606 // We don't have a *full* Ueberbackend, just a key-only one.
607 // Let's create one and use it
608 b = std::unique_ptr<UeberBackend>(new UeberBackend());
609 B = b.get();
610 }
611
25b1ce13
PL
612 SOAData sd;
613
a2e700d4 614 if(!B->getSOAUncached(zone, sd)) {
25b1ce13
PL
615 error = "No SOA known for '" + zone.toLogString() + "', is such a zone in the database?";
616 return false;
617 }
618
619 sd.db->list(zone, sd.domain_id);
620
621 DNSResourceRecord rr;
622 set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
623 map<DNSName,bool> nonterm;
624 vector<DNSResourceRecord> rrs;
625
626 while(sd.db->get(rr)) {
627 rr.qname.makeUsLowerCase();
628 if (rr.qtype.getCode())
629 {
630 rrs.push_back(rr);
631 qnames.insert(rr.qname);
632 if(rr.qtype.getCode() == QType::NS && rr.qname != zone)
633 nsset.insert(rr.qname);
634 if(rr.qtype.getCode() == QType::DS)
635 dsnames.insert(rr.qname);
636 }
637 else
638 delnonterm.insert(rr.qname);
639 }
640
641 NSEC3PARAMRecordContent ns3pr;
642 bool narrow;
643 bool haveNSEC3 = getNSEC3PARAM(zone, &ns3pr, &narrow);
644 bool isOptOut = (haveNSEC3 && ns3pr.d_flags);
645
646 set<DNSName> nsec3set;
647 if (haveNSEC3 && !narrow) {
2010ac95 648 for (auto &loopRR: rrs) {
25b1ce13 649 bool skip=false;
2010ac95 650 DNSName shorter = loopRR.qname;
25b1ce13
PL
651 if (shorter != zone && shorter.chopOff() && shorter != zone) {
652 do {
653 if(nsset.count(shorter)) {
654 skip=true;
655 break;
656 }
657 } while(shorter.chopOff() && shorter != zone);
658 }
2010ac95
RG
659 shorter = loopRR.qname;
660 if(!skip && (loopRR.qtype.getCode() != QType::NS || !isOptOut)) {
25b1ce13
PL
661
662 do {
663 if(!nsec3set.count(shorter)) {
664 nsec3set.insert(shorter);
665 }
666 } while(shorter != zone && shorter.chopOff());
667 }
668 }
669 }
670
8d0f207b
PL
671 if (doTransaction)
672 sd.db->startTransaction(zone, -1);
25b1ce13
PL
673
674 bool realrr=true;
675 bool doent=true;
676 uint32_t maxent = ::arg().asNum("max-ent-entries");
677
678 dononterm:;
679 for (const auto& qname: qnames)
680 {
681 bool auth=true;
682 DNSName ordername;
683 auto shorter(qname);
684
685 if(realrr) {
686 do {
687 if(nsset.count(shorter)) {
688 auth=false;
689 break;
690 }
691 } while(shorter.chopOff());
692 } else {
693 auth=nonterm.find(qname)->second;
694 }
695
696 if(haveNSEC3) // NSEC3
697 {
698 if(!narrow && nsec3set.count(qname)) {
699 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, qname)));
700 if(!realrr)
701 auth=true;
702 } else if(!realrr)
703 auth=false;
704 }
705 else if (realrr) // NSEC
706 ordername=qname.makeRelative(zone);
707
708 /*
709 if(g_verbose)
710 cerr<<"'"<<qname<<"' -> '"<< ordername <<"'"<<endl;
711 */
712 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, auth);
713
714 if(realrr)
715 {
716 if (dsnames.count(qname))
717 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, true, QType::DS);
718 if (!auth || nsset.count(qname)) {
719 ordername.clear();
720 if(isOptOut && !dsnames.count(qname))
721 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::NS);
722 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::A);
723 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::AAAA);
724 }
725
726 if(doent)
727 {
728 shorter=qname;
729 while(shorter!=zone && shorter.chopOff())
730 {
731 if(!qnames.count(shorter))
732 {
733 if(!(maxent))
734 {
735 L<<Logger::Warning<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
736 insnonterm.clear();
737 delnonterm.clear();
738 doent=false;
739 break;
740 }
741
742 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
743 insnonterm.insert(shorter);
744 else
745 delnonterm.erase(shorter);
746
747 if (!nonterm.count(shorter)) {
748 nonterm.insert(pair<DNSName, bool>(shorter, auth));
749 --maxent;
750 } else if (auth)
751 nonterm[shorter]=true;
752 }
753 }
754 }
755 }
756 }
757
758 if(realrr)
759 {
760 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
761 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
762 {
763 sd.db->updateEmptyNonTerminals(sd.domain_id, insnonterm, delnonterm, !doent);
764 }
765 if(doent)
766 {
767 realrr=false;
768 qnames.clear();
769 for(const auto& nt : nonterm){
770 qnames.insert(nt.first);
771 }
772 goto dononterm;
773 }
774 }
775
8d0f207b
PL
776 if (doTransaction)
777 sd.db->commitTransaction();
25b1ce13
PL
778
779 return true;
780}
781
157f806e
PD
782void DNSSECKeeper::cleanup()
783{
784 struct timeval now;
785 Utility::gettimeofday(&now, 0);
786
787 if(now.tv_sec - s_last_prune > (time_t)(30)) {
788 {
18a144ef 789 WriteLock l(&s_metacachelock);
e74f866a 790 pruneCollection(*this, s_metacache, ::arg().asNum("max-cache-entries"));
157f806e
PD
791 }
792 {
18a144ef 793 WriteLock l(&s_keycachelock);
e74f866a 794 pruneCollection(*this, s_keycache, ::arg().asNum("max-cache-entries"));
157f806e
PD
795 }
796 s_last_prune=time(0);
797 }
627d2ca2 798}