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