]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dbdnsseckeeper.cc
Merge pull request #7628 from tcely/patch-3
[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 <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>
38 #include "base32.hh"
39 #include "base64.hh"
40 #include "cachecleaner.hh"
41 #include "arguments.hh"
42
43
44 using namespace boost::assign;
45 #include "namespaces.hh"
46
47
48 DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
49 DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
50 pthread_rwlock_t DNSSECKeeper::s_metacachelock = PTHREAD_RWLOCK_INITIALIZER;
51 pthread_rwlock_t DNSSECKeeper::s_keycachelock = PTHREAD_RWLOCK_INITIALIZER;
52 AtomicCounter DNSSECKeeper::s_ops;
53 time_t DNSSECKeeper::s_last_prune;
54
55 bool DNSSECKeeper::doesDNSSEC()
56 {
57 return d_keymetadb->doesDNSSEC();
58 }
59
60 bool DNSSECKeeper::isSecuredZone(const DNSName& zone)
61 {
62 if(isPresigned(zone))
63 return true;
64
65 keyset_t keys = getKeys(zone); // does the cache
66
67 for(keyset_t::value_type& val : keys) {
68 if(val.second.active) {
69 return true;
70 }
71 }
72 return false;
73 }
74
75 bool DNSSECKeeper::isPresigned(const DNSName& name)
76 {
77 string meta;
78 getFromMeta(name, "PRESIGNED", meta);
79 return meta=="1";
80 }
81
82 bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active)
83 {
84 if(!bits) {
85 if(algorithm <= 10)
86 throw runtime_error("Creating an algorithm " +std::to_string(algorithm)+" ("+algorithm2name(algorithm)+") key requires the size (in bits) to be passed.");
87 else {
88 if(algorithm == DNSSECKeeper::ECCGOST || algorithm == DNSSECKeeper::ECDSA256 || algorithm == DNSSECKeeper::ED25519)
89 bits = 256;
90 else if(algorithm == DNSSECKeeper::ECDSA384)
91 bits = 384;
92 else if(algorithm == DNSSECKeeper::ED448)
93 bits = 456;
94 else {
95 throw runtime_error("Can not guess key size for algorithm "+std::to_string(algorithm));
96 }
97 }
98 }
99 DNSSECPrivateKey dspk;
100 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm));
101 try{
102 dpk->create(bits);
103 } catch (const std::runtime_error& error){
104 throw runtime_error("The algorithm does not support the given bit size.");
105 }
106 dspk.setKey(dpk);
107 dspk.d_algorithm = algorithm;
108 dspk.d_flags = setSEPBit ? 257 : 256;
109 return addKey(name, dspk, id, active);
110 }
111
112 void DNSSECKeeper::clearAllCaches() {
113 {
114 WriteLock l(&s_keycachelock);
115 s_keycache.clear();
116 }
117 WriteLock l(&s_metacachelock);
118 s_metacache.clear();
119 }
120
121 void DNSSECKeeper::clearCaches(const DNSName& name)
122 {
123 {
124 WriteLock l(&s_keycachelock);
125 s_keycache.erase(name);
126 }
127 WriteLock l(&s_metacachelock);
128 pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(tie(name));
129 while(range.first != range.second)
130 s_metacache.erase(range.first++);
131 }
132
133
134 bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active)
135 {
136 clearCaches(name);
137 DNSBackend::KeyData kd;
138 kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
139 kd.active = active;
140 kd.content = dpk.getKey()->convertToISC();
141 // now store it
142 return d_keymetadb->addDomainKey(name, kd, id);
143 }
144
145
146 static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
147 {
148 return make_pair(!a.second.keyType, a.second.id) <
149 make_pair(!b.second.keyType, b.second.id);
150 }
151
152 DNSSECPrivateKey DNSSECKeeper::getKeyById(const DNSName& zname, unsigned int id)
153 {
154 vector<DNSBackend::KeyData> keys;
155 d_keymetadb->getDomainKeys(zname, keys);
156 for(const DNSBackend::KeyData& kd : keys) {
157 if(kd.id != id)
158 continue;
159
160 DNSSECPrivateKey dpk;
161 DNSKEYRecordContent dkrc;
162 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
163 dpk.d_flags = kd.flags;
164 dpk.d_algorithm = dkrc.d_algorithm;
165
166 if(dpk.d_algorithm == DNSSECKeeper::RSASHA1 && getNSEC3PARAM(zname)) {
167 dpk.d_algorithm = DNSSECKeeper::RSASHA1NSEC3SHA1;
168 }
169
170 return dpk;
171 }
172 throw runtime_error("Can't find a key with id "+std::to_string(id)+" for zone '"+zname.toLogString()+"'");
173 }
174
175
176 bool DNSSECKeeper::removeKey(const DNSName& zname, unsigned int id)
177 {
178 clearCaches(zname);
179 return d_keymetadb->removeDomainKey(zname, id);
180 }
181
182 bool DNSSECKeeper::deactivateKey(const DNSName& zname, unsigned int id)
183 {
184 clearCaches(zname);
185 return d_keymetadb->deactivateDomainKey(zname, id);
186 }
187
188 bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
189 {
190 clearCaches(zname);
191 return d_keymetadb->activateDomainKey(zname, id);
192 }
193
194
195 void DNSSECKeeper::getFromMeta(const DNSName& zname, const std::string& key, std::string& value)
196 {
197 static int ttl = ::arg().asNum("domain-metadata-cache-ttl");
198 value.clear();
199 unsigned int now = time(0);
200
201 if(!((++s_ops) % 100000)) {
202 cleanup();
203 }
204
205 if (ttl > 0) {
206 ReadLock l(&s_metacachelock);
207
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;
212 }
213 }
214 vector<string> meta;
215 d_keymetadb->getDomainMetadata(zname, key, meta);
216 if(!meta.empty())
217 value=*meta.begin();
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 }
229 }
230 }
231
232 void 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
237 if (isPresigned(zname)) {
238 // SOA editing on a presigned zone never makes sense
239 return;
240 }
241
242 getFromMeta(zname, "SOA-EDIT", value);
243
244 if ((!soaEdit.empty() || !soaEditSigned.empty()) && value.empty()) {
245 if (!soaEditSigned.empty() && isSecuredZone(zname))
246 value=soaEditSigned;
247 if (value.empty())
248 value=soaEdit;
249 }
250
251 return;
252 }
253
254 uint64_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
267 bool DNSSECKeeper::getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
268 {
269 string value;
270 getFromMeta(zname, "NSEC3PARAM", value);
271 if(value.empty()) { // "no NSEC3"
272 return false;
273 }
274
275 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
276 if(ns3p) {
277 *ns3p = NSEC3PARAMRecordContent(value);
278 if (ns3p->d_iterations > maxNSEC3Iterations) {
279 ns3p->d_iterations = maxNSEC3Iterations;
280 g_log<<Logger::Error<<"Number of NSEC3 iterations for zone '"<<zname<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations<<endl;
281 }
282 if (ns3p->d_algorithm != 1) {
283 g_log<<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 }
286 }
287 if(narrow) {
288 getFromMeta(zname, "NSEC3NARROW", value);
289 *narrow = (value=="1");
290 }
291 return true;
292 }
293
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 */
301 bool DNSSECKeeper::checkNSEC3PARAM(const NSEC3PARAMRecordContent& ns3p, string& msg)
302 {
303 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
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 }
309
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
320 bool 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.toLogString()+"' are invalid: " + error_msg);
325
326 clearCaches(zname);
327 string descr = ns3p.getZoneRepresentation();
328 vector<string> meta;
329 meta.push_back(descr);
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;
339 }
340
341 bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName& zname)
342 {
343 clearCaches(zname);
344 return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>()));
345 }
346
347
348 bool DNSSECKeeper::setPresigned(const DNSName& zname)
349 {
350 clearCaches(zname);
351 vector<string> meta;
352 meta.push_back("1");
353 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta);
354 }
355
356 bool DNSSECKeeper::unsetPresigned(const DNSName& zname)
357 {
358 clearCaches(zname);
359 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>());
360 }
361
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 */
371 bool DNSSECKeeper::setPublishCDS(const DNSName& zname, const string& digestAlgos)
372 {
373 clearCaches(zname);
374 vector<string> meta;
375 meta.push_back(digestAlgos);
376 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta);
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 */
385 bool DNSSECKeeper::unsetPublishCDS(const DNSName& zname)
386 {
387 clearCaches(zname);
388 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>());
389 }
390
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 */
397 bool DNSSECKeeper::setPublishCDNSKEY(const DNSName& zname)
398 {
399 clearCaches(zname);
400 vector<string> meta;
401 meta.push_back("1");
402 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta);
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 */
411 bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname)
412 {
413 clearCaches(zname);
414 return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>());
415 }
416
417 /**
418 * Returns all keys that are used to sign the DNSKEY RRSet in a zone
419 *
420 * @param zname DNSName of the zone
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 */
424 DNSSECKeeper::keyset_t DNSSECKeeper::getEntryPoints(const DNSName& zname)
425 {
426 DNSSECKeeper::keyset_t ret;
427 DNSSECKeeper::keyset_t keys = getKeys(zname);
428
429 for(auto const &keymeta : keys)
430 if(keymeta.second.keyType == KSK || keymeta.second.keyType == CSK)
431 ret.push_back(keymeta);
432 return ret;
433 }
434
435 DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, bool useCache)
436 {
437 static int ttl = ::arg().asNum("dnssec-key-cache-ttl");
438 unsigned int now = time(0);
439
440 if(!((++s_ops) % 100000)) {
441 cleanup();
442 }
443
444 if (useCache && ttl > 0) {
445 ReadLock l(&s_keycachelock);
446 keycache_t::const_iterator iter = s_keycache.find(zone);
447
448 if(iter != s_keycache.end() && iter->d_ttd > now) {
449 keyset_t ret;
450 for(const keyset_t::value_type& value : iter->d_keys)
451 ret.push_back(value);
452 return ret;
453 }
454 }
455
456 keyset_t retkeyset;
457 vector<DNSBackend::KeyData> dbkeyset;
458
459 d_keymetadb->getDomainKeys(zone, dbkeyset);
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) {
465 DNSSECPrivateKey dpk;
466 DNSKEYRecordContent dkrc;
467
468 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content)));
469
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;
482 DNSKEYRecordContent dkrc;
483
484 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
485
486 dpk.d_flags = kd.flags;
487 dpk.d_algorithm = dkrc.d_algorithm;
488 if(dpk.d_algorithm == DNSSECKeeper::RSASHA1 && getNSEC3PARAM(zone)) {
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;
490 dpk.d_algorithm = DNSSECKeeper::RSASHA1NSEC3SHA1;
491 }
492
493 KeyMetaData kmd;
494
495 kmd.active = kd.active;
496 kmd.hasSEPBit = (kd.flags == 257);
497 kmd.id = kd.id;
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));
507 }
508 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
509
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);
517 replacing_insert(s_keycache, kce);
518 }
519 }
520
521 return retkeyset;
522 }
523
524 bool DNSSECKeeper::checkKeys(const DNSName& zone, vector<string>* errorMessages)
525 {
526 vector<DNSBackend::KeyData> dbkeyset;
527 d_keymetadb->getDomainKeys(zone, dbkeyset);
528 bool retval = true;
529
530 for(const DNSBackend::KeyData &keydata : dbkeyset) {
531 DNSKEYRecordContent dkrc;
532 shared_ptr<DNSCryptoKeyEngine> dke(DNSCryptoKeyEngine::makeFromISCString(dkrc, keydata.content));
533 retval = dke->checkKey(errorMessages) && retval;
534 }
535
536 return retval;
537 }
538
539 bool DNSSECKeeper::getPreRRSIGs(UeberBackend& db, const DNSName& signer, const DNSName& qname,
540 const DNSName& wildcardname, const QType& qtype,
541 DNSResourceRecord::Place signPlace, vector<DNSZoneRecord>& rrsigs, uint32_t signTTL)
542 {
543 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
544 SOAData sd;
545 if(!db.getSOAUncached(signer, sd)) {
546 DLOG(g_log<<"Could not get SOA for domain"<<endl);
547 return false;
548 }
549 db.lookup(QType(QType::RRSIG), wildcardname.countLabels() ? wildcardname : qname, NULL, sd.domain_id);
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 }
560 }
561 return true;
562 }
563
564 bool DNSSECKeeper::TSIGGrantsAccess(const DNSName& zone, const DNSName& keyname)
565 {
566 vector<string> allowed;
567
568 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
569
570 for(const string& dbkey : allowed) {
571 if(DNSName(dbkey)==keyname)
572 return true;
573 }
574 return false;
575 }
576
577 bool DNSSECKeeper::getTSIGForAccess(const DNSName& zone, const ComboAddress& master, DNSName* keyname)
578 {
579 vector<string> keynames;
580 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
581 keyname->trimToLabels(0);
582
583 // XXX FIXME this should check for a specific master!
584 for(const string& dbkey : keynames) {
585 *keyname=DNSName(dbkey);
586 return true;
587 }
588 return false;
589 }
590
591 bool 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
611 /* Rectifies the zone
612 *
613 * \param zone The zone to rectify
614 * \param error& A string where error messages are added
615 * \param info& A string where informational messages are added
616 * \param doTransaction Whether or not to wrap the rectify in a transaction
617 */
618 bool DNSSECKeeper::rectifyZone(const DNSName& zone, string& error, string& info, bool doTransaction) {
619 if (isPresigned(zone)) {
620 error = "Rectify presigned zone '"+zone.toLogString()+"' is not allowed/necessary.";
621 return false;
622 }
623
624 UeberBackend* B = d_keymetadb;
625 std::unique_ptr<UeberBackend> b;
626
627 if (d_ourDB) {
628 if (!doTransaction) {
629 error = "Can not rectify a zone with a new Ueberbackend inside a transaction.";
630 return false;
631 }
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
638 SOAData sd;
639
640 if(!B->getSOAUncached(zone, sd)) {
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
647 ostringstream infostream;
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;
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);
675
676 if(!haveNSEC3) {
677 infostream<<"Adding NSEC ordering information ";
678 }
679 else if(!narrow) {
680 if(!isOptOut) {
681 infostream<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'";
682 }
683 else {
684 infostream<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone<<"'";
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
694 set<DNSName> nsec3set;
695 if (haveNSEC3 && (!narrow || !isOptOut)) {
696 for (auto &loopRR: rrs) {
697 bool skip=false;
698 DNSName shorter = loopRR.qname;
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 }
707 shorter = loopRR.qname;
708 if(!skip && (loopRR.qtype.getCode() != QType::NS || !isOptOut)) {
709
710 do {
711 if(!nsec3set.count(shorter)) {
712 nsec3set.insert(shorter);
713 }
714 } while(shorter != zone && shorter.chopOff());
715 }
716 }
717 }
718
719 if (doTransaction)
720 sd.db->startTransaction(zone, -1);
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 {
746 if(nsec3set.count(qname)) {
747 if(!narrow)
748 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, qname)));
749 if(!realrr && !isOptOut)
750 auth=true;
751 }
752 }
753 else if (realrr && securedZone) // NSEC
754 ordername=qname.makeRelative(zone);
755
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 {
779 g_log<<Logger::Warning<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
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
820 if (doTransaction)
821 sd.db->commitTransaction();
822
823 info = infostream.str();
824 return true;
825 }
826
827 void 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 {
834 WriteLock l(&s_metacachelock);
835 pruneCollection(*this, s_metacache, ::arg().asNum("max-cache-entries"));
836 }
837 {
838 WriteLock l(&s_keycachelock);
839 pruneCollection(*this, s_keycache, ::arg().asNum("max-cache-entries"));
840 }
841 s_last_prune=time(0);
842 }
843 }