]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dbdnsseckeeper.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[thirdparty/pdns.git] / pdns / dbdnsseckeeper.cc
CommitLineData
882358c8
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
ccc3f9ed 3 Copyright (C) 2001 - 2012 PowerDNS.COM BV
882358c8
BH
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
8
f782fe38
MH
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
12
882358c8
BH
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 St, Fifth Floor, Boston, MA 02110-1301 USA
21*/
22
c0273500
BH
23#include "dnsseckeeper.hh"
24#include "dnssecinfra.hh"
25#include "ueberbackend.hh"
26#include "statbag.hh"
27#include <iostream>
c0273500
BH
28#include <boost/foreach.hpp>
29#include <sys/stat.h>
30#include <sys/types.h>
31#include <fstream>
32#include <boost/algorithm/string.hpp>
33#include <boost/format.hpp>
34#include <boost/assign/std/vector.hpp> // for 'operator+=()'
35#include <boost/assign/list_inserter.hpp>
78bcb858 36#include "base64.hh"
157f806e
PD
37#include "cachecleaner.hh"
38#include "arguments.hh"
d473cb9a
BH
39
40
c0273500 41using namespace boost::assign;
9cb28241 42#include "namespaces.hh"
de43ec0f 43
c0273500 44
40fe813d 45DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
d6f3dcdc 46DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
18a144ef
BH
47pthread_rwlock_t DNSSECKeeper::s_metacachelock = PTHREAD_RWLOCK_INITIALIZER;
48pthread_rwlock_t DNSSECKeeper::s_keycachelock = PTHREAD_RWLOCK_INITIALIZER;
16f7d28d 49AtomicCounter DNSSECKeeper::s_ops;
157f806e 50time_t DNSSECKeeper::s_last_prune;
d473cb9a 51
d3e7090c 52bool DNSSECKeeper::isSecuredZone(const std::string& zone)
c0273500 53{
d3e7090c 54 if(isPresigned(zone))
d6f3dcdc 55 return true;
631580dd 56
16f7d28d 57 if(!((++s_ops) % 100000)) {
157f806e
PD
58 cleanup();
59 }
60
40fe813d 61 {
18a144ef 62 ReadLock l(&s_keycachelock);
40fe813d
BH
63 keycache_t::const_iterator iter = s_keycache.find(zone);
64 if(iter != s_keycache.end() && iter->d_ttd > (unsigned int)time(0)) {
65 if(iter->d_keys.empty())
66 return false;
67 else
68 return true;
69 }
70 else
71 ;
72 }
7df3258b 73 keyset_t keys = getKeys(zone, true); // does the cache
d473cb9a 74
ade1b1e9
BH
75 BOOST_FOREACH(keyset_t::value_type& val, keys) {
76 if(val.second.active) {
77 return true;
78 }
c0273500 79 }
ade1b1e9 80 return false;
c0273500
BH
81}
82
d3e7090c
BH
83bool DNSSECKeeper::isPresigned(const std::string& name)
84{
9cb28241
BH
85 string meta;
86 getFromMeta(name, "PRESIGNED", meta);
87 return meta=="1";
d3e7090c 88}
c0273500 89
40fe813d 90bool DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, int bits, bool active)
c0273500 91{
022e5e0b
BH
92 if(!bits) {
93 if(algorithm <= 10)
94 bits = keyOrZone ? 2048 : 1024;
95 else {
59d84dc2 96 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // ECDSA, GOST, ED25519
022e5e0b
BH
97 bits = 256;
98 else if(algorithm == 14)
99 bits = 384;
100 else {
0f4b0d97 101 throw runtime_error("Can't guess key size for algorithm "+lexical_cast<string>(algorithm));
022e5e0b
BH
102 }
103 }
104 }
699e6e37 105 DNSSECPrivateKey dspk;
8d9f38f2 106 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
699e6e37
BH
107 dpk->create(bits);
108 dspk.setKey(dpk);
109 dspk.d_algorithm = algorithm;
110 dspk.d_flags = keyOrZone ? 257 : 256;
40fe813d 111 return addKey(name, dspk, active);
f7bcc763
BH
112}
113
627d2ca2
PD
114void DNSSECKeeper::clearAllCaches() {
115 {
18a144ef 116 WriteLock l(&s_keycachelock);
627d2ca2
PD
117 s_keycache.clear();
118 }
18a144ef 119 WriteLock l(&s_metacachelock);
627d2ca2
PD
120 s_metacache.clear();
121}
122
5e91adff
BH
123void DNSSECKeeper::clearCaches(const std::string& name)
124{
40fe813d 125 {
18a144ef 126 WriteLock l(&s_keycachelock);
40fe813d
BH
127 s_keycache.erase(name);
128 }
18a144ef 129 WriteLock l(&s_metacachelock);
d6f3dcdc
BH
130 pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(name);
131 while(range.first != range.second)
132 s_metacache.erase(range.first++);
5e91adff
BH
133}
134
135
40fe813d 136bool DNSSECKeeper::addKey(const std::string& name, const DNSSECPrivateKey& dpk, bool active)
f7bcc763 137{
5e91adff 138 clearCaches(name);
c0273500 139 DNSBackend::KeyData kd;
7ddd79a7 140 kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
c0273500 141 kd.active = active;
189bb9d2 142 kd.content = dpk.getKey()->convertToISC();
c0273500 143 // now store it
936eb34a 144 return d_keymetadb->addDomainKey(name, kd) >= 0; // >= 0 == s
c0273500
BH
145}
146
147
148static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
149{
150 return make_pair(!a.second.keyOrZone, a.second.id) <
151 make_pair(!b.second.keyOrZone, b.second.id);
152}
153
154DNSSECPrivateKey DNSSECKeeper::getKeyById(const std::string& zname, unsigned int id)
155{
c0273500 156 vector<DNSBackend::KeyData> keys;
936eb34a 157 d_keymetadb->getDomainKeys(zname, 0, keys);
c0273500
BH
158 BOOST_FOREACH(const DNSBackend::KeyData& kd, keys) {
159 if(kd.id != id)
160 continue;
161
162 DNSSECPrivateKey dpk;
699e6e37 163 DNSKEYRecordContent dkrc;
8d9f38f2 164 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
c0273500 165 dpk.d_flags = kd.flags;
a254438f 166 dpk.d_algorithm = dkrc.d_algorithm;
c0273500 167
a254438f
BH
168 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zname)) {
169 dpk.d_algorithm += 2;
170 }
c0273500
BH
171
172 return dpk;
173 }
174 throw runtime_error("Can't find a key with id "+lexical_cast<string>(id)+" for zone '"+zname+"'");
c0273500
BH
175}
176
177
a84a8203 178bool DNSSECKeeper::removeKey(const std::string& zname, unsigned int id)
c0273500 179{
5e91adff 180 clearCaches(zname);
a84a8203 181 return d_keymetadb->removeDomainKey(zname, id);
c0273500
BH
182}
183
a84a8203 184bool DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id)
c0273500 185{
5e91adff 186 clearCaches(zname);
a84a8203 187 return d_keymetadb->deactivateDomainKey(zname, id);
c0273500
BH
188}
189
a84a8203 190bool DNSSECKeeper::activateKey(const std::string& zname, unsigned int id)
c0273500 191{
5e91adff 192 clearCaches(zname);
a84a8203 193 return d_keymetadb->activateDomainKey(zname, id);
c0273500
BH
194}
195
d6f3dcdc
BH
196
197void DNSSECKeeper::getFromMeta(const std::string& zname, const std::string& key, std::string& value)
c0273500 198{
d6f3dcdc 199 value.clear();
5e91adff 200 unsigned int now = time(0);
157f806e 201
16f7d28d 202 if(!((++s_ops) % 100000)) {
157f806e
PD
203 cleanup();
204 }
205
d473cb9a 206 {
18a144ef 207 ReadLock l(&s_metacachelock);
d473cb9a 208
d6f3dcdc
BH
209 metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
210 if(iter != s_metacache.end() && iter->d_ttd > now) {
211 value = iter->d_value;
212 return;
d473cb9a 213 }
22c5aa60 214 }
d473cb9a 215 vector<string> meta;
936eb34a 216 d_keymetadb->getDomainMetadata(zname, key, meta);
d6f3dcdc
BH
217 if(!meta.empty())
218 value=*meta.begin();
219
220 METACacheEntry nce;
d473cb9a
BH
221 nce.d_domain=zname;
222 nce.d_ttd = now+60;
d6f3dcdc
BH
223 nce.d_key= key;
224 nce.d_value = value;
225 {
18a144ef 226 WriteLock l(&s_metacachelock);
d6f3dcdc 227 replacing_insert(s_metacache, nce);
d473cb9a 228 }
d6f3dcdc
BH
229}
230
231bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
232{
233 string value;
234 getFromMeta(zname, "NSEC3PARAM", value);
5935cede 235 if(value.empty()) { // "no NSEC3"
d6f3dcdc 236 return false;
5935cede 237 }
d6f3dcdc 238
c0273500 239 if(ns3p) {
d6f3dcdc 240 NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
c0273500
BH
241 *ns3p = *tmp;
242 delete tmp;
243 }
d6f3dcdc
BH
244 if(narrow) {
245 getFromMeta(zname, "NSEC3NARROW", value);
246 *narrow = (value=="1");
247 }
c0273500
BH
248 return true;
249}
250
a84a8203 251bool DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
c0273500 252{
5e91adff 253 clearCaches(zname);
c0273500
BH
254 string descr = ns3p.getZoneRepresentation();
255 vector<string> meta;
256 meta.push_back(descr);
a84a8203
PD
257 if (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", meta)) {
258 meta.clear();
259
260 if(narrow)
261 meta.push_back("1");
262
263 return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta);
264 }
265 return false;
c0273500
BH
266}
267
a84a8203 268bool DNSSECKeeper::unsetNSEC3PARAM(const std::string& zname)
c0273500 269{
5e91adff 270 clearCaches(zname);
a84a8203 271 return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>()));
c0273500
BH
272}
273
274
a84a8203 275bool DNSSECKeeper::setPresigned(const std::string& zname)
d3e7090c
BH
276{
277 clearCaches(zname);
278 vector<string> meta;
279 meta.push_back("1");
a84a8203 280 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta);
d3e7090c
BH
281}
282
a84a8203 283bool DNSSECKeeper::unsetPresigned(const std::string& zname)
d3e7090c
BH
284{
285 clearCaches(zname);
a84a8203 286 return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>());
d3e7090c
BH
287}
288
289
e0d84497 290DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone)
c0273500 291{
5e91adff 292 unsigned int now = time(0);
157f806e 293
16f7d28d 294 if(!((++s_ops) % 100000)) {
157f806e
PD
295 cleanup();
296 }
297
40fe813d 298 {
18a144ef 299 ReadLock l(&s_keycachelock);
40fe813d
BH
300 keycache_t::const_iterator iter = s_keycache.find(zone);
301
302 if(iter != s_keycache.end() && iter->d_ttd > now) {
303 keyset_t ret;
304 BOOST_FOREACH(const keyset_t::value_type& value, iter->d_keys) {
305 if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == value.second.keyOrZone)
306 ret.push_back(value);
307 }
308 return ret;
7aff7ead 309 }
40fe813d 310 }
7aff7ead 311 keyset_t retkeyset, allkeyset;
c0273500
BH
312 vector<UeberBackend::KeyData> dbkeyset;
313
936eb34a 314 d_keymetadb->getDomainKeys(zone, 0, dbkeyset);
d473cb9a 315
c0273500
BH
316 BOOST_FOREACH(UeberBackend::KeyData& kd, dbkeyset)
317 {
318 DNSSECPrivateKey dpk;
319
699e6e37 320 DNSKEYRecordContent dkrc;
7df3258b 321
8d9f38f2 322 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
7df3258b 323
c0273500 324 dpk.d_flags = kd.flags;
a254438f
BH
325 dpk.d_algorithm = dkrc.d_algorithm;
326 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zone))
327 dpk.d_algorithm+=2;
c0273500
BH
328
329 KeyMetaData kmd;
330
331 kmd.active = kd.active;
332 kmd.keyOrZone = (kd.flags == 257);
333 kmd.id = kd.id;
334
335 if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone)
7aff7ead
BH
336 retkeyset.push_back(make_pair(dpk, kmd));
337 allkeyset.push_back(make_pair(dpk, kmd));
c0273500 338 }
7aff7ead
BH
339 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
340 sort(allkeyset.begin(), allkeyset.end(), keyCompareByKindAndID);
d473cb9a
BH
341
342 KeyCacheEntry kce;
343 kce.d_domain=zone;
7aff7ead
BH
344 kce.d_keys = allkeyset;
345 kce.d_ttd = now + 30;
40fe813d 346 {
18a144ef 347 WriteLock l(&s_keycachelock);
40fe813d
BH
348 replacing_insert(s_keycache, kce);
349 }
d473cb9a 350
7aff7ead 351 return retkeyset;
c0273500
BH
352}
353
36758d25 354bool DNSSECKeeper::secureZone(const std::string& name, int algorithm, int size)
c0273500 355{
5e91adff 356 clearCaches(name); // just to be sure ;)
36758d25 357 return addKey(name, true, algorithm, size);
c0273500 358}
d3e7090c 359
f1485b68
PD
360bool DNSSECKeeper::getPreRRSIGs(DNSBackend& db, const std::string& signer, const std::string& qname,
361 const std::string& wildcardname, const QType& qtype,
794c2f92 362 DNSPacketWriter::Place signPlace, vector<DNSResourceRecord>& rrsigs, uint32_t signTTL)
d3e7090c 363{
f1485b68 364 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<(wildcardname.empty() ? qname : wildcardname)<<"'"<<endl;
b563f71b
PD
365 SOAData sd;
366 sd.db=(DNSBackend *)-1; // force uncached answer
367 if(!db.getSOA(signer, sd)) {
368 DLOG(L<<"Could not get SOA for domain"<<endl);
369 return false;
370 }
371 db.lookup(QType(QType::RRSIG), wildcardname.empty() ? qname : wildcardname, NULL, sd.domain_id);
d3e7090c 372 DNSResourceRecord rr;
51a3a4d4
BH
373 while(db.get(rr)) {
374 // cerr<<"Considering for '"<<qtype.getName()<<"' RRSIG '"<<rr.content<<"'\n";
794c2f92
PD
375 vector<string> parts;
376 stringtok(parts, rr.content);
377 if(parts[0] == qtype.getName() && pdns_iequals(parts[7], signer+".")) {
51a3a4d4 378 // cerr<<"Got it"<<endl;
f1485b68
PD
379 if (!wildcardname.empty())
380 rr.qname = qname;
d3e7090c 381 rr.d_place = (DNSResourceRecord::Place)signPlace;
b563f71b 382 rr.ttl = signTTL;
d3e7090c
BH
383 rrsigs.push_back(rr);
384 }
51a3a4d4 385 else ; // cerr<<"Skipping!"<<endl;
d3e7090c
BH
386 }
387 return true;
388}
78bcb858
BH
389
390bool DNSSECKeeper::TSIGGrantsAccess(const string& zone, const string& keyname, const string& algorithm)
391{
392 vector<string> allowed;
393
936eb34a 394 d_keymetadb->getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
78bcb858
BH
395
396 BOOST_FOREACH(const string& dbkey, allowed) {
397 if(pdns_iequals(dbkey, keyname))
398 return true;
399 }
400 return false;
401}
29b92d6f 402
7597b3cf 403bool DNSSECKeeper::getTSIGForAccess(const string& zone, const string& master, string* keyname)
29b92d6f
BH
404{
405 vector<string> keynames;
936eb34a 406 d_keymetadb->getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
29b92d6f
BH
407 keyname->clear();
408
409 // XXX FIXME this should check for a specific master!
410 BOOST_FOREACH(const string& dbkey, keynames) {
411 *keyname=dbkey;
29b92d6f
BH
412 return true;
413 }
414 return false;
415}
157f806e
PD
416
417void DNSSECKeeper::cleanup()
418{
419 struct timeval now;
420 Utility::gettimeofday(&now, 0);
421
422 if(now.tv_sec - s_last_prune > (time_t)(30)) {
423 {
18a144ef 424 WriteLock l(&s_metacachelock);
157f806e
PD
425 pruneCollection(s_metacache, ::arg().asNum("max-cache-entries"));
426 }
427 {
18a144ef 428 WriteLock l(&s_keycachelock);
157f806e
PD
429 pruneCollection(s_keycache, ::arg().asNum("max-cache-entries"));
430 }
431 s_last_prune=time(0);
432 }
627d2ca2 433}