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

Revision 1893, 9.0 KB (checked in by ahu, 2 years ago)

implement 'pdnssec set-presigned', allowing PowerDNSSEC to serve pre-signed zones. Rather experimental, but does appear to work

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