]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dbdnsseckeeper.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[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 {
902c4e9c 88 if(algorithm == DNSSECKeeper::ECCGOST || algorithm == DNSSECKeeper::ECDSA256 || algorithm == DNSSECKeeper::ED25519)
022e5e0b 89 bits = 256;
902c4e9c 90 else if(algorithm == DNSSECKeeper::ECDSA384)
022e5e0b 91 bits = 384;
902c4e9c 92 else if(algorithm == DNSSECKeeper::ED448)
21a8834a 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);
d38e2ba9 103 } catch (const 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
902c4e9c
CH
166 if(dpk.d_algorithm == DNSSECKeeper::RSASHA1 && getNSEC3PARAM(zname)) {
167 dpk.d_algorithm = DNSSECKeeper::RSASHA1NSEC3SHA1;
a254438f 168 }
c0273500
BH
169
170 return dpk;
171 }
86f1af1c 172 throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toLogString()+"'");
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);
ca8f1621 227 lruReplacingInsert(s_metacache, nce);
030850a9 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;
e6a9dde5 280 g_log<<Logger::Error<<"Number of NSEC3 iterations for zone '"<<zname<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations<<endl;
28b66a94 281 }
c7fbe6c9 282 if (ns3p->d_algorithm != 1) {
e6a9dde5 283 g_log<<Logger::Error<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p->d_algorithm)<<"', setting to 1 for zone '"<<zname<<"'."<<endl;
c7fbe6c9
PL
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))
86f1af1c 324 throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toLogString()+"' 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;
902c4e9c 488 if(dpk.d_algorithm == DNSSECKeeper::RSASHA1 && getNSEC3PARAM(zone)) {
e6a9dde5 489 g_log<<Logger::Warning<<"Zone '"<<zone<<"' has NSEC3 semantics, but the "<< (kd.active ? "" : "in" ) <<"active key with id "<<kd.id<<" has 'Algorithm: 5'. This should be corrected to 'Algorithm: 7' in the database (or NSEC3 should be disabled)."<<endl;
902c4e9c 490 dpk.d_algorithm = DNSSECKeeper::RSASHA1NSEC3SHA1;
13136697 491 }
b6bd795c 492
c0273500
BH
493 KeyMetaData kmd;
494
495 kmd.active = kd.active;
b6bd795c 496 kmd.hasSEPBit = (kd.flags == 257);
c0273500 497 kmd.id = kd.id;
b6bd795c
PL
498
499 if (find(algoHasSeparateKSK.begin(), algoHasSeparateKSK.end(), dpk.d_algorithm) == algoHasSeparateKSK.end())
500 kmd.keyType = CSK;
501 else if(kmd.hasSEPBit)
502 kmd.keyType = KSK;
503 else
504 kmd.keyType = ZSK;
505
506 retkeyset.push_back(make_pair(dpk, kmd));
c0273500 507 }
7aff7ead 508 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
b6bd795c 509
030850a9
RG
510 if (ttl > 0) {
511 KeyCacheEntry kce;
512 kce.d_domain=zone;
513 kce.d_keys = retkeyset;
514 kce.d_ttd = now + ttl;
515 {
516 WriteLock l(&s_keycachelock);
ca8f1621 517 lruReplacingInsert(s_keycache, kce);
030850a9 518 }
40fe813d 519 }
030850a9 520
7aff7ead 521 return retkeyset;
c0273500
BH
522}
523
ac3f3893 524bool DNSSECKeeper::checkKeys(const DNSName& zone, vector<string>* errorMessages)
8ca3ea33
RG
525{
526 vector<DNSBackend::KeyData> dbkeyset;
9c1c5d49 527 d_keymetadb->getDomainKeys(zone, dbkeyset);
ac3f3893 528 bool retval = true;
8ca3ea33
RG
529
530 for(const DNSBackend::KeyData &keydata : dbkeyset) {
531 DNSKEYRecordContent dkrc;
532 shared_ptr<DNSCryptoKeyEngine> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content));
ac3f3893 533 retval = dke->checkKey(errorMessages) && retval;
8ca3ea33
RG
534 }
535
ac3f3893 536 return retval;
8ca3ea33
RG
537}
538
675fa24c
PD
539bool DNSSECKeeper::getPreRRSIGs(UeberBackend& db, const DNSName& signer, const DNSName& qname,
540 const DNSName& wildcardname, const QType& qtype,
90ba52e0 541 DNSResourceRecord::Place signPlace, vector<DNSZoneRecord>& rrsigs, uint32_t signTTL)
d3e7090c 542{
f1485b68 543 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
232f0877 544 SOAData sd;
79ba7763 545 if(!db.getSOAUncached(signer, sd)) {
e6a9dde5 546 DLOG(g_log<<"Could not get SOA for domain"<<endl);
232f0877
CH
547 return false;
548 }
675fa24c 549 db.lookup(QType(QType::RRSIG), wildcardname.countLabels() ? wildcardname : qname, NULL, sd.domain_id);
90ba52e0 550 DNSZoneRecord rr;
551 while(db.get(rr)) {
552 auto rrsig = getRR<RRSIGRecordContent>(rr.dr);
553 if(rrsig->d_type == qtype.getCode() && rrsig->d_signer==signer) {
554 if (wildcardname.countLabels())
555 rr.dr.d_name = qname;
556 rr.dr.d_place = signPlace;
557 rr.dr.d_ttl = signTTL;
558 rrsigs.push_back(rr);
559 }
232f0877
CH
560 }
561 return true;
d3e7090c 562}
78bcb858 563
675fa24c 564bool DNSSECKeeper::TSIGGrantsAccess(const DNSName& zone, const DNSName& keyname)
78bcb858
BH
565{
566 vector<string> allowed;
567
936eb34a 568 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
78bcb858 569
ef7cd021 570 for(const string& dbkey : allowed) {
675fa24c 571 if(DNSName(dbkey)==keyname)
78bcb858
BH
572 return true;
573 }
574 return false;
575}
29b92d6f 576
d622042f 577bool DNSSECKeeper::getTSIGForAccess(const DNSName& zone, const ComboAddress& master, DNSName* keyname)
29b92d6f
BH
578{
579 vector<string> keynames;
936eb34a 580 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
675fa24c 581 keyname->trimToLabels(0);
29b92d6f
BH
582
583 // XXX FIXME this should check for a specific master!
ef7cd021 584 for(const string& dbkey : keynames) {
675fa24c 585 *keyname=DNSName(dbkey);
29b92d6f
BH
586 return true;
587 }
588 return false;
589}
157f806e 590
cbe8b186
PL
591bool DNSSECKeeper::unSecureZone(const DNSName& zone, string& error, string& info) {
592 // Not calling isSecuredZone(), as it will return false for zones with zero
593 // active keys.
594 DNSSECKeeper::keyset_t keyset=getKeys(zone);
595
596 if(keyset.empty()) {
597 error = "No keys for zone '" + zone.toLogString() + "'.";
598 return false;
599 }
600
601 for(auto& key : keyset) {
602 deactivateKey(zone, key.second.id);
603 removeKey(zone, key.second.id);
604 }
605
606 unsetNSEC3PARAM(zone);
607 unsetPresigned(zone);
608 return true;
609}
610
8d0f207b
PL
611/* Rectifies the zone
612 *
613 * \param zone The zone to rectify
614 * \param error& A string where error messages are added
59102608 615 * \param info& A string where informational messages are added
8d0f207b
PL
616 * \param doTransaction Whether or not to wrap the rectify in a transaction
617 */
59102608 618bool DNSSECKeeper::rectifyZone(const DNSName& zone, string& error, string& info, bool doTransaction) {
25b1ce13
PL
619 if (isPresigned(zone)) {
620 error = "Rectify presigned zone '"+zone.toLogString()+"' is not allowed/necessary.";
621 return false;
622 }
623
a2e700d4
PL
624 UeberBackend* B = d_keymetadb;
625 std::unique_ptr<UeberBackend> b;
626
627 if (d_ourDB) {
b0486ea5
PL
628 if (!doTransaction) {
629 error = "Can not rectify a zone with a new Ueberbackend inside a transaction.";
630 return false;
631 }
a2e700d4
PL
632 // We don't have a *full* Ueberbackend, just a key-only one.
633 // Let's create one and use it
634 b = std::unique_ptr<UeberBackend>(new UeberBackend());
635 B = b.get();
636 }
637
25b1ce13
PL
638 SOAData sd;
639
a2e700d4 640 if(!B->getSOAUncached(zone, sd)) {
25b1ce13
PL
641 error = "No SOA known for '" + zone.toLogString() + "', is such a zone in the database?";
642 return false;
643 }
644
645 sd.db->list(zone, sd.domain_id);
646
59102608 647 ostringstream infostream;
25b1ce13
PL
648 DNSResourceRecord rr;
649 set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
650 map<DNSName,bool> nonterm;
651 vector<DNSResourceRecord> rrs;
652
653 while(sd.db->get(rr)) {
654 rr.qname.makeUsLowerCase();
655 if (rr.qtype.getCode())
656 {
657 rrs.push_back(rr);
658 qnames.insert(rr.qname);
659 if(rr.qtype.getCode() == QType::NS && rr.qname != zone)
660 nsset.insert(rr.qname);
661 if(rr.qtype.getCode() == QType::DS)
662 dsnames.insert(rr.qname);
663 }
664 else
665 delnonterm.insert(rr.qname);
666 }
667
668 NSEC3PARAMRecordContent ns3pr;
2977b073
KM
669 bool securedZone = isSecuredZone(zone);
670 bool haveNSEC3 = false, isOptOut = false, narrow = false;
671
672 if(securedZone) {
673 haveNSEC3 = getNSEC3PARAM(zone, &ns3pr, &narrow);
674 isOptOut = (haveNSEC3 && ns3pr.d_flags);
25b1ce13 675
59102608
RG
676 if(!haveNSEC3) {
677 infostream<<"Adding NSEC ordering information ";
678 }
679 else if(!narrow) {
680 if(!isOptOut) {
42b6e1b7 681 infostream<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'";
59102608
RG
682 }
683 else {
42b6e1b7 684 infostream<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone<<"'";
59102608
RG
685 }
686 } else {
687 infostream<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields";
688 }
689 }
690 else {
691 infostream<<"Adding empty non-terminals for non-DNSSEC zone";
692 }
693
25b1ce13 694 set<DNSName> nsec3set;
2885750a 695 if (haveNSEC3 && (!narrow || !isOptOut)) {
2010ac95 696 for (auto &loopRR: rrs) {
25b1ce13 697 bool skip=false;
2010ac95 698 DNSName shorter = loopRR.qname;
25b1ce13
PL
699 if (shorter != zone && shorter.chopOff() && shorter != zone) {
700 do {
701 if(nsset.count(shorter)) {
702 skip=true;
703 break;
704 }
705 } while(shorter.chopOff() && shorter != zone);
706 }
2010ac95
RG
707 shorter = loopRR.qname;
708 if(!skip && (loopRR.qtype.getCode() != QType::NS || !isOptOut)) {
25b1ce13
PL
709
710 do {
711 if(!nsec3set.count(shorter)) {
712 nsec3set.insert(shorter);
713 }
714 } while(shorter != zone && shorter.chopOff());
715 }
716 }
717 }
718
8d0f207b
PL
719 if (doTransaction)
720 sd.db->startTransaction(zone, -1);
25b1ce13
PL
721
722 bool realrr=true;
723 bool doent=true;
724 uint32_t maxent = ::arg().asNum("max-ent-entries");
725
726 dononterm:;
727 for (const auto& qname: qnames)
728 {
729 bool auth=true;
730 DNSName ordername;
731 auto shorter(qname);
732
733 if(realrr) {
734 do {
735 if(nsset.count(shorter)) {
736 auth=false;
737 break;
738 }
739 } while(shorter.chopOff());
740 } else {
741 auth=nonterm.find(qname)->second;
742 }
743
744 if(haveNSEC3) // NSEC3
745 {
666c7211
KM
746 if(nsec3set.count(qname)) {
747 if(!narrow)
748 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, qname)));
749 if(!realrr && !isOptOut)
25b1ce13 750 auth=true;
666c7211 751 }
25b1ce13 752 }
2977b073 753 else if (realrr && securedZone) // NSEC
25b1ce13
PL
754 ordername=qname.makeRelative(zone);
755
25b1ce13
PL
756 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, auth);
757
758 if(realrr)
759 {
760 if (dsnames.count(qname))
761 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, true, QType::DS);
762 if (!auth || nsset.count(qname)) {
763 ordername.clear();
764 if(isOptOut && !dsnames.count(qname))
765 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::NS);
766 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::A);
767 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, qname, ordername, false, QType::AAAA);
768 }
769
770 if(doent)
771 {
772 shorter=qname;
773 while(shorter!=zone && shorter.chopOff())
774 {
775 if(!qnames.count(shorter))
776 {
777 if(!(maxent))
778 {
e6a9dde5 779 g_log<<Logger::Warning<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
25b1ce13
PL
780 insnonterm.clear();
781 delnonterm.clear();
782 doent=false;
783 break;
784 }
785
786 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
787 insnonterm.insert(shorter);
788 else
789 delnonterm.erase(shorter);
790
791 if (!nonterm.count(shorter)) {
792 nonterm.insert(pair<DNSName, bool>(shorter, auth));
793 --maxent;
794 } else if (auth)
795 nonterm[shorter]=true;
796 }
797 }
798 }
799 }
800 }
801
802 if(realrr)
803 {
804 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
805 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
806 {
807 sd.db->updateEmptyNonTerminals(sd.domain_id, insnonterm, delnonterm, !doent);
808 }
809 if(doent)
810 {
811 realrr=false;
812 qnames.clear();
813 for(const auto& nt : nonterm){
814 qnames.insert(nt.first);
815 }
816 goto dononterm;
817 }
818 }
819
8d0f207b
PL
820 if (doTransaction)
821 sd.db->commitTransaction();
25b1ce13 822
59102608 823 info = infostream.str();
25b1ce13
PL
824 return true;
825}
826
157f806e
PD
827void DNSSECKeeper::cleanup()
828{
829 struct timeval now;
830 Utility::gettimeofday(&now, 0);
831
832 if(now.tv_sec - s_last_prune > (time_t)(30)) {
833 {
18a144ef 834 WriteLock l(&s_metacachelock);
e74f866a 835 pruneCollection(*this, s_metacache, ::arg().asNum("max-cache-entries"));
157f806e
PD
836 }
837 {
18a144ef 838 WriteLock l(&s_keycachelock);
e74f866a 839 pruneCollection(*this, s_keycache, ::arg().asNum("max-cache-entries"));
157f806e
PD
840 }
841 s_last_prune=time(0);
842 }
627d2ca2 843}