root/trunk/pdns/pdns/dbdnsseckeeper.cc @ 1982

Revision 1982, 9.4 KB (checked in by ahu, 2 years ago)

make sure that addKey lets us know if it worked, allowing us to spot non-working configurations
unthread the keycache, reintroducing the 'shared key' problem, but plugging a massive memory leak

Line 
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
19#include "dnsseckeeper.hh"
20#include "dnssecinfra.hh"
21#include "ueberbackend.hh"
22#include "statbag.hh"
23#include <iostream>
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>
32
33
34using namespace boost::assign;
35#include "namespaces.hh"
36using namespace boost;
37
38DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
39DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
40pthread_mutex_t DNSSECKeeper::s_metacachelock = PTHREAD_MUTEX_INITIALIZER;
41pthread_mutex_t DNSSECKeeper::s_keycachelock = PTHREAD_MUTEX_INITIALIZER;
42
43bool DNSSECKeeper::isSecuredZone(const std::string& zone) 
44{
45  if(isPresigned(zone))
46    return true;
47 
48  {
49    Lock l(&s_keycachelock);
50    keycache_t::const_iterator iter = s_keycache.find(zone);
51    if(iter != s_keycache.end() && iter->d_ttd > (unsigned int)time(0)) { 
52      if(iter->d_keys.empty())
53        return false;
54      else
55        return true;
56    }
57    else
58      ; 
59  } 
60  keyset_t keys = getKeys(zone, true);
61 
62  BOOST_FOREACH(keyset_t::value_type& val, keys) {
63    if(val.second.active) {
64      return true;
65    }
66  }
67  return false;
68}
69
70bool DNSSECKeeper::isPresigned(const std::string& name)
71{
72  string meta;
73  getFromMeta(name, "PRESIGNED", meta);
74  return meta=="1";
75}
76
77bool DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, int bits, bool active)
78{
79  if(!bits) {
80    if(algorithm <= 10)
81      bits = keyOrZone ? 2048 : 1024;
82    else {
83      if(algorithm == 12 || algorithm == 13)
84        bits = 256;
85      else if(algorithm == 14)
86        bits = 384;
87      else {
88        throw runtime_error("Can't guess key size for algoritm "+lexical_cast<string>(algorithm));
89      }
90    }
91  }
92  DNSSECPrivateKey dspk;
93  shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
94  dpk->create(bits);
95  dspk.setKey(dpk);
96  dspk.d_algorithm = algorithm;
97  dspk.d_flags = keyOrZone ? 257 : 256;
98  return addKey(name, dspk, active);
99}
100
101void DNSSECKeeper::clearCaches(const std::string& name)
102{
103  {
104    Lock l(&s_keycachelock);
105    s_keycache.erase(name); 
106  }
107  Lock l(&s_metacachelock);
108  pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(name);
109  while(range.first != range.second)
110    s_metacache.erase(range.first++);
111}
112
113
114bool DNSSECKeeper::addKey(const std::string& name, const DNSSECPrivateKey& dpk, bool active)
115{
116  clearCaches(name);
117  DNSBackend::KeyData kd;
118  kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
119  kd.active = active;
120  kd.content = dpk.getKey()->convertToISC();
121 // now store it
122  return d_keymetadb.addDomainKey(name, kd) >= 0; // >= 0 == s
123}
124
125
126static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
127{
128  return make_pair(!a.second.keyOrZone, a.second.id) <
129         make_pair(!b.second.keyOrZone, b.second.id);
130}
131
132DNSSECPrivateKey DNSSECKeeper::getKeyById(const std::string& zname, unsigned int id)
133{ 
134  vector<DNSBackend::KeyData> keys;
135  d_keymetadb.getDomainKeys(zname, 0, keys);
136  BOOST_FOREACH(const DNSBackend::KeyData& kd, keys) {
137    if(kd.id != id) 
138      continue;
139   
140    DNSSECPrivateKey dpk;
141    DNSKEYRecordContent dkrc;
142    dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
143    dpk.d_flags = kd.flags;
144    dpk.d_algorithm = dkrc.d_algorithm;
145   
146    if(dpk.d_algorithm == 5 && getNSEC3PARAM(zname)) {
147      dpk.d_algorithm += 2;
148    }
149   
150    return dpk;   
151  }
152  throw runtime_error("Can't find a key with id "+lexical_cast<string>(id)+" for zone '"+zname+"'");
153}
154
155
156void DNSSECKeeper::removeKey(const std::string& zname, unsigned int id)
157{
158  clearCaches(zname);
159  d_keymetadb.removeDomainKey(zname, id);
160}
161
162void DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id)
163{
164  clearCaches(zname);
165  d_keymetadb.deactivateDomainKey(zname, id);
166}
167
168void DNSSECKeeper::activateKey(const std::string& zname, unsigned int id)
169{
170  clearCaches(zname);
171  d_keymetadb.activateDomainKey(zname, id);
172}
173
174
175void DNSSECKeeper::getFromMeta(const std::string& zname, const std::string& key, std::string& value)
176{
177  value.clear();
178  unsigned int now = time(0);
179  {
180    Lock l(&s_metacachelock); 
181   
182    metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
183    if(iter != s_metacache.end() && iter->d_ttd > now) {
184      value = iter->d_value;
185      return;
186    }
187  }
188  vector<string> meta;
189  d_keymetadb.getDomainMetadata(zname, key, meta);
190  if(!meta.empty())
191    value=*meta.begin();
192   
193  METACacheEntry nce;
194  nce.d_domain=zname;
195  nce.d_ttd = now+60;
196  nce.d_key= key;
197  nce.d_value = value;
198  { 
199    Lock l(&s_metacachelock);
200    replacing_insert(s_metacache, nce);
201  }
202}
203
204bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p, bool* narrow)
205{
206  string value;
207  getFromMeta(zname, "NSEC3PARAM", value);
208  if(value.empty()) // "no NSEC3"
209    return false;
210     
211  if(ns3p) {
212    NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
213    *ns3p = *tmp;
214    delete tmp;
215  }
216  if(narrow) {
217    getFromMeta(zname, "NSEC3NARROW", value);
218    *narrow = (value=="1");
219  }
220 
221 
222  return true;
223}
224
225void DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent& ns3p, const bool& narrow)
226{
227  clearCaches(zname);
228  string descr = ns3p.getZoneRepresentation();
229  vector<string> meta;
230  meta.push_back(descr);
231  d_keymetadb.setDomainMetadata(zname, "NSEC3PARAM", meta);
232 
233  meta.clear();
234  if(narrow)
235    meta.push_back("1");
236  d_keymetadb.setDomainMetadata(zname, "NSEC3NARROW", meta);
237}
238
239void DNSSECKeeper::unsetNSEC3PARAM(const std::string& zname)
240{
241  clearCaches(zname);
242  d_keymetadb.setDomainMetadata(zname, "NSEC3PARAM", vector<string>());
243}
244
245
246void DNSSECKeeper::setPresigned(const std::string& zname)
247{
248  clearCaches(zname);
249  vector<string> meta;
250  meta.push_back("1");
251  d_keymetadb.setDomainMetadata(zname, "PRESIGNED", meta);
252}
253
254void DNSSECKeeper::unsetPresigned(const std::string& zname)
255{
256  clearCaches(zname);
257  d_keymetadb.setDomainMetadata(zname, "PRESIGNED", vector<string>());
258}
259
260
261DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone) 
262{
263  unsigned int now = time(0);
264  {
265    Lock l(&s_keycachelock);
266    keycache_t::const_iterator iter = s_keycache.find(zone);
267     
268    if(iter != s_keycache.end() && iter->d_ttd > now) { 
269      keyset_t ret;
270      BOOST_FOREACH(const keyset_t::value_type& value, iter->d_keys) {
271        if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == value.second.keyOrZone)
272          ret.push_back(value);
273      }
274      return ret;
275    }
276  }   
277  keyset_t retkeyset, allkeyset;
278  vector<UeberBackend::KeyData> dbkeyset;
279 
280  d_keymetadb.getDomainKeys(zone, 0, dbkeyset);
281 
282  BOOST_FOREACH(UeberBackend::KeyData& kd, dbkeyset) 
283  {
284    DNSSECPrivateKey dpk;
285
286    DNSKEYRecordContent dkrc;
287    dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content)));
288    dpk.d_flags = kd.flags;
289    dpk.d_algorithm = dkrc.d_algorithm;
290    if(dpk.d_algorithm == 5 && getNSEC3PARAM(zone))
291      dpk.d_algorithm+=2;
292   
293    KeyMetaData kmd;
294
295    kmd.active = kd.active;
296    kmd.keyOrZone = (kd.flags == 257);
297    kmd.id = kd.id;
298   
299    if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone)
300      retkeyset.push_back(make_pair(dpk, kmd));
301    allkeyset.push_back(make_pair(dpk, kmd));
302  }
303  sort(retkeyset.begin(), retkeyset.end(), keyCompareByKindAndID);
304  sort(allkeyset.begin(), allkeyset.end(), keyCompareByKindAndID);
305 
306  KeyCacheEntry kce;
307  kce.d_domain=zone;
308  kce.d_keys = allkeyset;
309  kce.d_ttd = now + 30;
310  {
311    Lock l(&s_keycachelock);
312    replacing_insert(s_keycache, kce);
313  }
314 
315  return retkeyset;
316}
317
318bool DNSSECKeeper::secureZone(const std::string& name, int algorithm)
319{
320  clearCaches(name); // just to be sure ;)
321  return addKey(name, true, algorithm);
322}
323
324bool DNSSECKeeper::getPreRRSIGs(DNSBackend& db, const std::string& signer, const std::string& qname, const QType& qtype, 
325        DNSPacketWriter::Place signPlace, vector<DNSResourceRecord>& rrsigs)
326{
327  // cerr<<"Doing DB lookup for precomputed RRSIGs for '"<<qname<<"'"<<endl;
328        db.lookup(QType(QType::RRSIG), qname);
329        DNSResourceRecord rr;
330        while(db.get(rr)) { 
331                // cerr<<"Considering for '"<<qtype.getName()<<"' RRSIG '"<<rr.content<<"'\n";
332                if(boost::starts_with(rr.content, qtype.getName()+" ")) {
333                        // cerr<<"Got it"<<endl;
334                        rr.d_place = (DNSResourceRecord::Place)signPlace;
335                        rrsigs.push_back(rr);
336                }
337                else ; // cerr<<"Skipping!"<<endl;
338        }
339        return true;
340}
Note: See TracBrowser for help on using the browser.