]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dbdnsseckeeper.cc
and now we remove the unused variable ;-)
[thirdparty/pdns.git] / pdns / dbdnsseckeeper.cc
CommitLineData
882358c8
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2001 - 2011 PowerDNS.COM BV
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
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
c0273500
BH
19#include "dnsseckeeper.hh"
20#include "dnssecinfra.hh"
21#include "ueberbackend.hh"
22#include "statbag.hh"
23#include <iostream>
c0273500
BH
24#include <boost/foreach.hpp>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <fstream>
28#include <boost/algorithm/string.hpp>
29#include <boost/format.hpp>
30#include <boost/assign/std/vector.hpp> // for 'operator+=()'
31#include <boost/assign/list_inserter.hpp>
78bcb858 32#include "base64.hh"
d473cb9a
BH
33
34
c0273500 35using namespace boost::assign;
9cb28241 36#include "namespaces.hh"
de43ec0f 37
c0273500 38
40fe813d 39DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
d6f3dcdc
BH
40DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
41pthread_mutex_t DNSSECKeeper::s_metacachelock = PTHREAD_MUTEX_INITIALIZER;
40fe813d 42pthread_mutex_t DNSSECKeeper::s_keycachelock = PTHREAD_MUTEX_INITIALIZER;
d473cb9a 43
d3e7090c 44bool DNSSECKeeper::isSecuredZone(const std::string& zone)
c0273500 45{
d3e7090c 46 if(isPresigned(zone))
d6f3dcdc 47 return true;
631580dd 48
40fe813d
BH
49 {
50 Lock l(&s_keycachelock);
51 keycache_t::const_iterator iter = s_keycache.find(zone);
52 if(iter != s_keycache.end() && iter->d_ttd > (unsigned int)time(0)) {
53 if(iter->d_keys.empty())
54 return false;
55 else
56 return true;
57 }
58 else
59 ;
60 }
7df3258b 61 keyset_t keys = getKeys(zone, true); // does the cache
d473cb9a 62
ade1b1e9
BH
63 BOOST_FOREACH(keyset_t::value_type& val, keys) {
64 if(val.second.active) {
65 return true;
66 }
c0273500 67 }
ade1b1e9 68 return false;
c0273500
BH
69}
70
d3e7090c
BH
71bool DNSSECKeeper::isPresigned(const std::string& name)
72{
9cb28241
BH
73 string meta;
74 getFromMeta(name, "PRESIGNED", meta);
75 return meta=="1";
d3e7090c 76}
c0273500 77
40fe813d 78bool DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, int bits, bool active)
c0273500 79{
022e5e0b
BH
80 if(!bits) {
81 if(algorithm <= 10)
82 bits = keyOrZone ? 2048 : 1024;
83 else {
f7982887 84 if(algorithm == 12 || algorithm == 13)
022e5e0b
BH
85 bits = 256;
86 else if(algorithm == 14)
87 bits = 384;
88 else {
89 throw runtime_error("Can't guess key size for algoritm "+lexical_cast<string>(algorithm));
90 }
91 }
92 }
699e6e37 93 DNSSECPrivateKey dspk;
8d9f38f2 94 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
699e6e37
BH
95 dpk->create(bits);
96 dspk.setKey(dpk);
97 dspk.d_algorithm = algorithm;
98 dspk.d_flags = keyOrZone ? 257 : 256;
40fe813d 99 return addKey(name, dspk, active);
f7bcc763
BH
100}
101
5e91adff
BH
102void DNSSECKeeper::clearCaches(const std::string& name)
103{
40fe813d
BH
104 {
105 Lock l(&s_keycachelock);
106 s_keycache.erase(name);
107 }
d6f3dcdc
BH
108 Lock l(&s_metacachelock);
109 pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(name);
110 while(range.first != range.second)
111 s_metacache.erase(range.first++);
5e91adff
BH
112}
113
114
40fe813d 115bool DNSSECKeeper::addKey(const std::string& name, const DNSSECPrivateKey& dpk, bool active)
f7bcc763 116{
5e91adff 117 clearCaches(name);
c0273500 118 DNSBackend::KeyData kd;
7ddd79a7 119 kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
c0273500 120 kd.active = active;
189bb9d2 121 kd.content = dpk.getKey()->convertToISC();
c0273500 122 // now store it
40fe813d 123 return d_keymetadb.addDomainKey(name, kd) >= 0; // >= 0 == s
c0273500
BH
124}
125
126
127static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
128{
129 return make_pair(!a.second.keyOrZone, a.second.id) <
130 make_pair(!b.second.keyOrZone, b.second.id);
131}
132
133DNSSECPrivateKey DNSSECKeeper::getKeyById(const std::string& zname, unsigned int id)
134{
c0273500 135 vector<DNSBackend::KeyData> keys;
631580dd 136 d_keymetadb.getDomainKeys(zname, 0, keys);
c0273500
BH
137 BOOST_FOREACH(const DNSBackend::KeyData& kd, keys) {
138 if(kd.id != id)
139 continue;
140
141 DNSSECPrivateKey dpk;
699e6e37 142 DNSKEYRecordContent dkrc;
8d9f38f2 143 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
c0273500 144 dpk.d_flags = kd.flags;
a254438f 145 dpk.d_algorithm = dkrc.d_algorithm;
c0273500 146
a254438f
BH
147 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zname)) {
148 dpk.d_algorithm += 2;
149 }
c0273500
BH
150
151 return dpk;
152 }
153 throw runtime_error("Can't find a key with id "+lexical_cast<string>(id)+" for zone '"+zname+"'");
c0273500
BH
154}
155
156
157void DNSSECKeeper::removeKey(const std::string& zname, unsigned int id)
158{
5e91adff 159 clearCaches(zname);
631580dd 160 d_keymetadb.removeDomainKey(zname, id);
c0273500
BH
161}
162
163void DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id)
164{
5e91adff 165 clearCaches(zname);
631580dd 166 d_keymetadb.deactivateDomainKey(zname, id);
c0273500
BH
167}
168
169void DNSSECKeeper::activateKey(const std::string& zname, unsigned int id)
170{
5e91adff 171 clearCaches(zname);
631580dd 172 d_keymetadb.activateDomainKey(zname, id);
c0273500
BH
173}
174
d6f3dcdc
BH
175
176void DNSSECKeeper::getFromMeta(const std::string& zname, const std::string& key, std::string& value)
c0273500 177{
d6f3dcdc 178 value.clear();
5e91adff 179 unsigned int now = time(0);
d473cb9a 180 {
d6f3dcdc 181 Lock l(&s_metacachelock);
d473cb9a 182
d6f3dcdc
BH
183 metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
184 if(iter != s_metacache.end() && iter->d_ttd > now) {
185 value = iter->d_value;
186 return;
d473cb9a 187 }
22c5aa60 188 }
d473cb9a 189 vector<string> meta;
631580dd 190 d_keymetadb.getDomainMetadata(zname, key, meta);
d6f3dcdc
BH
191 if(!meta.empty())
192 value=*meta.begin();
193
194 METACacheEntry nce;
d473cb9a
BH
195 nce.d_domain=zname;
196 nce.d_ttd = now+60;
d6f3dcdc
BH
197 nce.d_key= key;
198 nce.d_value = value;
199 {
200 Lock l(&s_metacachelock);
201 replacing_insert(s_metacache, nce);
d473cb9a 202 }
d6f3dcdc
BH
203}
204
205bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
206{
207 string value;
208 getFromMeta(zname, "NSEC3PARAM", value);
209 if(value.empty()) // "no NSEC3"
210 return false;
211
c0273500 212 if(ns3p) {
d6f3dcdc 213 NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
c0273500
BH
214 *ns3p = *tmp;
215 delete tmp;
216 }
d6f3dcdc
BH
217 if(narrow) {
218 getFromMeta(zname, "NSEC3NARROW", value);
219 *narrow = (value=="1");
220 }
221
d473cb9a 222
c0273500
BH
223 return true;
224}
225
22c5aa60 226void DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
c0273500 227{
5e91adff 228 clearCaches(zname);
c0273500
BH
229 string descr = ns3p.getZoneRepresentation();
230 vector<string> meta;
231 meta.push_back(descr);
631580dd 232 d_keymetadb.setDomainMetadata(zname, "NSEC3PARAM", meta);
22c5aa60
BH
233
234 meta.clear();
235 if(narrow)
236 meta.push_back("1");
631580dd 237 d_keymetadb.setDomainMetadata(zname, "NSEC3NARROW", meta);
c0273500
BH
238}
239
240void DNSSECKeeper::unsetNSEC3PARAM(const std::string& zname)
241{
5e91adff 242 clearCaches(zname);
631580dd 243 d_keymetadb.setDomainMetadata(zname, "NSEC3PARAM", vector<string>());
a68bd91a 244 d_keymetadb.setDomainMetadata(zname, "NSEC3NARROW", vector<string>());
c0273500
BH
245}
246
247
d3e7090c
BH
248void DNSSECKeeper::setPresigned(const std::string& zname)
249{
250 clearCaches(zname);
251 vector<string> meta;
252 meta.push_back("1");
631580dd 253 d_keymetadb.setDomainMetadata(zname, "PRESIGNED", meta);
d3e7090c
BH
254}
255
256void DNSSECKeeper::unsetPresigned(const std::string& zname)
257{
258 clearCaches(zname);
631580dd 259 d_keymetadb.setDomainMetadata(zname, "PRESIGNED", vector<string>());
d3e7090c
BH
260}
261
262
e0d84497 263DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone)
c0273500 264{
5e91adff 265 unsigned int now = time(0);
40fe813d
BH
266 {
267 Lock l(&s_keycachelock);
268 keycache_t::const_iterator iter = s_keycache.find(zone);
269
270 if(iter != s_keycache.end() && iter->d_ttd > now) {
271 keyset_t ret;
272 BOOST_FOREACH(const keyset_t::value_type& value, iter->d_keys) {
273 if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == value.second.keyOrZone)
274 ret.push_back(value);
275 }
276 return ret;
7aff7ead 277 }
40fe813d 278 }
7aff7ead 279 keyset_t retkeyset, allkeyset;
c0273500
BH
280 vector<UeberBackend::KeyData> dbkeyset;
281
631580dd 282 d_keymetadb.getDomainKeys(zone, 0, dbkeyset);
d473cb9a 283
c0273500
BH
284 BOOST_FOREACH(UeberBackend::KeyData& kd, dbkeyset)
285 {
286 DNSSECPrivateKey dpk;
287
699e6e37 288 DNSKEYRecordContent dkrc;
7df3258b 289
8d9f38f2 290 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
7df3258b 291
c0273500 292 dpk.d_flags = kd.flags;
a254438f
BH
293 dpk.d_algorithm = dkrc.d_algorithm;
294 if(dpk.d_algorithm == 5 && getNSEC3PARAM(zone))
295 dpk.d_algorithm+=2;
c0273500
BH
296
297 KeyMetaData kmd;
298
299 kmd.active = kd.active;
300 kmd.keyOrZone = (kd.flags == 257);
301 kmd.id = kd.id;
302
303 if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone)
7aff7ead
BH
304 retkeyset.push_back(make_pair(dpk, kmd));
305 allkeyset.push_back(make_pair(dpk, kmd));
c0273500 306 }
7aff7ead
BH
307 sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
308 sort(allkeyset.begin(), allkeyset.end(), keyCompareByKindAndID);
d473cb9a
BH
309
310 KeyCacheEntry kce;
311 kce.d_domain=zone;
7aff7ead
BH
312 kce.d_keys = allkeyset;
313 kce.d_ttd = now + 30;
40fe813d
BH
314 {
315 Lock l(&s_keycachelock);
316 replacing_insert(s_keycache, kce);
317 }
d473cb9a 318
7aff7ead 319 return retkeyset;
c0273500
BH
320}
321
40fe813d 322bool DNSSECKeeper::secureZone(const std::string& name, int algorithm)
c0273500 323{
5e91adff 324 clearCaches(name); // just to be sure ;)
40fe813d 325 return addKey(name, true, algorithm);
c0273500 326}
d3e7090c 327
51a3a4d4 328bool DNSSECKeeper::getPreRRSIGs(DNSBackend& db, const std::string& signer, const std::string& qname, const QType& qtype,
d3e7090c
BH
329 DNSPacketWriter::Place signPlace, vector<DNSResourceRecord>& rrsigs)
330{
51a3a4d4
BH
331 // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<qname<<"'"<<endl;
332 db.lookup(QType(QType::RRSIG), qname);
d3e7090c 333 DNSResourceRecord rr;
51a3a4d4
BH
334 while(db.get(rr)) {
335 // cerr<<"Considering for '"<<qtype.getName()<<"' RRSIG '"<<rr.content<<"'\n";
d3e7090c 336 if(boost::starts_with(rr.content, qtype.getName()+" ")) {
51a3a4d4 337 // cerr<<"Got it"<<endl;
d3e7090c
BH
338 rr.d_place = (DNSResourceRecord::Place)signPlace;
339 rrsigs.push_back(rr);
340 }
51a3a4d4 341 else ; // cerr<<"Skipping!"<<endl;
d3e7090c
BH
342 }
343 return true;
344}
78bcb858
BH
345
346bool DNSSECKeeper::TSIGGrantsAccess(const string& zone, const string& keyname, const string& algorithm)
347{
348 vector<string> allowed;
349
350 d_keymetadb.getDomainMetadata(zone, "TSIG-ALLOW-AXFR", allowed);
351
352 BOOST_FOREACH(const string& dbkey, allowed) {
353 if(pdns_iequals(dbkey, keyname))
354 return true;
355 }
356 return false;
357}
29b92d6f 358
7597b3cf 359bool DNSSECKeeper::getTSIGForAccess(const string& zone, const string& master, string* keyname)
29b92d6f
BH
360{
361 vector<string> keynames;
362 d_keymetadb.getDomainMetadata(zone, "AXFR-MASTER-TSIG", keynames);
363 keyname->clear();
364
365 // XXX FIXME this should check for a specific master!
366 BOOST_FOREACH(const string& dbkey, keynames) {
367 *keyname=dbkey;
368
369 return true;
370 }
371 return false;
372}