root/trunk/pdns/pdns/pdnssec.cc @ 1852

Revision 1852, 14.6 KB (checked in by ahu, 2 years ago)

add support for unsalted nsec3 hashes ('1 0 1 -')

Line 
1#include "dnsseckeeper.hh"
2#include "dnssecinfra.hh"
3#include "statbag.hh"
4#include "base32.hh"
5#include <boost/foreach.hpp>
6#include <boost/program_options.hpp>
7#include "dnsbackend.hh"
8#include "ueberbackend.hh"
9#include "arguments.hh"
10#include "packetcache.hh"
11
12StatBag S;
13PacketCache PC;
14
15using namespace boost;
16namespace po = boost::program_options;
17po::variables_map g_vm;
18
19string s_programname="pdns";
20
21ArgvMap &arg()
22{
23  static ArgvMap arg;
24  return arg;
25}
26
27string humanTime(time_t t)
28{
29  char ret[256];
30  struct tm tm;
31  localtime_r(&t, &tm);
32  strftime(ret, sizeof(ret)-1, "%c", &tm);   // %h:%M %Y-%m-%d
33  return ret;
34}
35
36void loadMainConfig(const std::string& configdir)
37{
38  ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
39 
40  ::arg().set("launch","Which backends to launch");
41  ::arg().set("dnssec","if we should do dnssec")="true";
42  ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
43  ::arg().setCmd("help","Provide a helpful message");
44  //::arg().laxParse(argc,argv);
45
46  if(::arg().mustDo("help")) {
47    cerr<<"syntax:"<<endl<<endl;
48    cerr<<::arg().helpstring(::arg()["help"])<<endl;
49    exit(99);
50  }
51
52  if(::arg()["config-name"]!="") 
53    s_programname+="-"+::arg()["config-name"];
54
55  string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
56  cleanSlashes(configname);
57
58  cerr<<"configname: '"<<configname<<"'\n";
59 
60  ::arg().laxFile(configname.c_str());
61  ::arg().set("module-dir","Default directory for modules")=LIBDIR;
62  BackendMakers().launch(::arg()["launch"]); // vrooooom!
63  ::arg().laxFile(configname.c_str());   
64  //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
65
66  S.declare("qsize-q","Number of questions waiting for database attention");
67   
68  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
69  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
70         
71  S.declare("query-cache-hit","Number of hits on the query cache");
72  S.declare("query-cache-miss","Number of misses on the query cache");
73  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
74  ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; 
75  ::arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10";
76  ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";             
77  ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60";
78  ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20";             
79  ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
80  ::arg().set("soa-retry-default","Default SOA retry")="3600";
81  ::arg().set("soa-expire-default","Default SOA expire")="604800";
82    ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
83  ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";   
84 
85  UeberBackend::go();
86}
87
88void rectifyZone(DNSSECKeeper& dk, const std::string& zone)
89{
90  UeberBackend* B = new UeberBackend("default");
91  SOAData sd;
92 
93  if(!B->getSOA(zone, sd)) {
94    cerr<<"No SOA known for '"<<zone<<"', is such a zone in the database?"<<endl;
95    return;
96  } 
97  sd.db->list(zone, sd.domain_id);
98  DNSResourceRecord rr;
99
100  set<string> qnames, nsset;
101 
102  while(sd.db->get(rr)) {
103    qnames.insert(rr.qname);
104    if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, zone)) 
105      nsset.insert(rr.qname);
106  }
107
108  NSEC3PARAMRecordContent ns3pr;
109  bool narrow;
110  bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
111  string hashed;
112  if(!haveNSEC3) 
113    cerr<<"Adding NSEC ordering information"<<endl;
114  else if(!narrow)
115    cerr<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'"<<endl;
116  else 
117    cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
118 
119  BOOST_FOREACH(const string& qname, qnames)
120  {
121    string shorter(qname);
122    bool auth=true;
123    do {
124      if(nsset.count(shorter)) { 
125        auth=false;
126        break;
127      }
128    }while(chopOff(shorter));
129
130    if(!haveNSEC3) // NSEC
131      sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, auth);
132    else {
133      if(!narrow) {
134        hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
135        cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
136      }
137      sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
138    }
139  }
140  cerr<<"Done listing"<<endl;
141}
142
143void checkZone(DNSSECKeeper& dk, const std::string& zone)
144{
145  UeberBackend* B = new UeberBackend("default");
146  SOAData sd;
147 
148  if(!B->getSOA(zone, sd)) {
149    cerr<<"No SOA!"<<endl;
150    return;
151  } 
152  sd.db->list(zone, sd.domain_id);
153  DNSResourceRecord rr;
154  uint64_t numrecords=0, numerrors=0;
155 
156  while(sd.db->get(rr)) {
157    if(rr.qtype.getCode() == QType::MX) 
158      rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
159    if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A)
160    {
161      cerr<<"Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
162    }
163    try {
164      shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
165      string tmp=drc->serialize(rr.qname);
166    }
167    catch(std::exception& e) 
168    {
169      cerr<<"Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
170      cerr<<"Error was: "<<e.what()<<endl;
171      numerrors++;
172    }
173    numrecords++;
174  }
175  cerr<<"Checked "<<numrecords<<" records, "<<numerrors<<" errors"<<endl;
176}
177
178void showZone(DNSSECKeeper& dk, const std::string& zone)
179{
180  NSEC3PARAMRecordContent ns3pr;
181  bool narrow;
182  bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
183 
184  if(!haveNSEC3) 
185    cout<<"Zone has NSEC semantics"<<endl;
186  else
187    cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
188 
189  DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
190
191  if(keyset.empty())  {
192    cerr << "No keys for zone '"<<zone<<"'."<<endl;
193  }
194  else { 
195    cout << "keys: "<<endl;
196    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
197      cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
198      cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.d_key.getConstContext().len*8<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
199      if(value.second.keyOrZone) {
200        cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
201        cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
202        cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl << endl;
203      }
204    }
205  }
206}
207
208int main(int argc, char** argv)
209try
210{
211  po::options_description desc("Allowed options");
212  desc.add_options()
213    ("help,h", "produce help message")
214    ("verbose,v", po::value<bool>(), "be verbose")
215    ("force", "force an action")
216    ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
217    ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
218    ("commands", po::value<vector<string> >());
219
220  po::positional_options_description p;
221  p.add("commands", -1);
222  po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
223  po::notify(g_vm);
224
225  vector<string> cmds;
226
227  if(g_vm.count("commands")) 
228    cmds = g_vm["commands"].as<vector<string> >();
229
230  if(cmds.empty() || g_vm.count("help")) {
231    cerr<<"Usage: \npdnssec [options] [show-zone] [secure-zone] [rectify-zone] [add-zone-key] [deactivate-zone-key] [remove-zone-key] [activate-zone-key]\n";
232    cerr<<"         [import-zone-key] [export-zone-key] [set-nsec3] [unset-nsec3] [export-zone-dnskey]\n\n";
233    cerr<<"activate-zone-key ZONE KEY-ID   Activate the key with key id KEY-ID in ZONE\n";
234    cerr<<"add-zone-key ZONE [zsk|ksk]     Add a ZSK or KSK to a zone\n";
235    cerr<<"  [bits] [rsasha1|rsasha256]    and specify algorithm & bits\n";
236    cerr<<"check-zone ZONE                 Check a zone for correctness\n";
237    cerr<<"deactivate-zone-key             Dectivate the key with key id KEY-ID in ZONE\n";
238    cerr<<"export-zone-dnskey ZONE KEY-ID  Export to stdout the public DNSKEY described\n";
239    cerr<<"export-zone-key ZONE KEY-ID     Export to stdout the private key described\n";
240    cerr<<"import-zone-key ZONE FILE       Import from a file a private key, ZSK or KSK\n";           
241    cerr<<"                [ksk|zsk]       Defaults to KSK\n";
242    cerr<<"rectify-zone ZONE               Fix up DNSSEC fields (order, auth)\n";
243    cerr<<"remove-zone-key ZONE KEY-ID     Remove key with KEY-ID from ZONE\n";
244    cerr<<"secure-zone                     Add KSK and two ZSKs\n";
245    cerr<<"set-nsec3 'params' [narrow]     Enable NSEC3 with PARAMs. Optionally narrow\n";
246    cerr<<"show-zone ZONE                  Show DNSSEC (public) key details about a zone\n";
247    cerr<<"unset-nsec3 ZONE                Switch back to NSEC\n\n";
248
249    cerr<<"Options:"<<endl;
250    cerr<<desc<<endl;
251    return 0;
252  }
253 
254  loadMainConfig(g_vm["config-dir"].as<string>());
255  reportAllTypes();
256  DNSSECKeeper dk;
257
258  if(cmds[0] == "rectify-zone" || cmds[0] == "order-zone") {
259    if(cmds.size() != 2) {
260      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
261      return 0;
262    }
263    rectifyZone(dk, cmds[1]);
264  }
265  else if(cmds[0] == "check-zone") {
266    if(cmds.size() != 2) {
267      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
268      return 0;
269    }
270    checkZone(dk, cmds[1]);
271  }
272
273  else if(cmds[0] == "show-zone") {
274    if(cmds.size() != 2) {
275      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
276      return 0;
277    }
278    const string& zone=cmds[1];
279    showZone(dk, zone);
280  }
281  else if(cmds[0] == "activate-zone-key") {
282    const string& zone=cmds[1];
283    unsigned int id=atoi(cmds[2].c_str());
284    dk.activateKey(zone, id);
285  }
286  else if(cmds[0] == "deactivate-zone-key") {
287    const string& zone=cmds[1];
288    unsigned int id=atoi(cmds[2].c_str());
289    dk.deactivateKey(zone, id);
290  }
291  else if(cmds[0] == "add-zone-key") {
292    const string& zone=cmds[1];
293    // need to get algorithm, bits & ksk or zsk from commandline
294    bool keyOrZone=false;
295    int bits=0;
296    int algorithm=5;
297    for(unsigned int n=2; n < cmds.size(); ++n) {
298      if(pdns_iequals(cmds[n], "zsk"))
299        keyOrZone = false;
300      else if(pdns_iequals(cmds[n], "ksk"))
301        keyOrZone = true;
302      else if(pdns_iequals(cmds[n], "rsasha1"))
303        algorithm=5;
304      else if(pdns_iequals(cmds[n], "rsasha256"))
305        algorithm=8;
306      else if(atoi(cmds[n].c_str()))
307        bits = atoi(cmds[n].c_str());
308      else { 
309        cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
310      }
311    }
312    cerr<<"Adding a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
313    if(bits)
314      cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
315    dk.addKey(zone, keyOrZone, algorithm, bits); 
316  }
317  else if(cmds[0] == "remove-zone-key") {
318    const string& zone=cmds[1];
319    unsigned int id=atoi(cmds[2].c_str());
320    dk.removeKey(zone, id);
321  }
322 
323  else if(cmds[0] == "secure-zone") {
324    if(cmds.size() != 2) {
325      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
326      return 0;
327    }
328    const string& zone=cmds[1];
329    DNSSECPrivateKey dpk;
330   
331    if(dk.haveActiveKSKFor(zone)) {
332      cerr << "There is a KSK already for zone '"<<zone<<"', remove with pdnssec remove-zone-key if needed"<<endl;
333      return 0;
334    }
335     
336    dk.secureZone(zone, 8);
337
338    if(!dk.haveActiveKSKFor(zone)) {
339      cerr << "This should not happen, still no key!" << endl;
340      return 0;
341    }
342 
343    DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
344
345    if(!zskset.empty())  {
346      cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
347      return 0;
348    }
349     
350    dk.addKey(zone, false, 8);
351    dk.addKey(zone, false, 8, 0, false); // not active
352    rectifyZone(dk, zone);
353    showZone(dk, zone);
354  }
355  else if(cmds[0]=="set-nsec3") {
356    string nsec3params =  cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
357    bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
358    NSEC3PARAMRecordContent ns3pr(nsec3params);
359    dk.setNSEC3PARAM(cmds[1], ns3pr, narrow);
360  }
361  else if(cmds[0]=="unset-nsec3") {
362    dk.unsetNSEC3PARAM(cmds[1]);
363  }
364  else if(cmds[0]=="export-zone-key") {
365    if(cmds.size() < 3) {
366      cerr<<"Syntax: pdnssec export-zone-key zone-name id"<<endl;
367      cerr<<cmds.size()<<endl;
368      exit(1);
369    }
370
371    string zone=cmds[1];
372    unsigned int id=atoi(cmds[2].c_str());
373    DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
374    cout << dpk.d_key.convertToISC(dpk.d_algorithm) <<endl;
375  } 
376  else if(cmds[0]=="import-zone-key") {
377    if(cmds.size() < 3) {
378      cerr<<"Syntax: pdnssec import-zone-key zone-name filename [zsk|ksk]"<<endl;
379      exit(1);
380    }
381    string zone=cmds[1];
382    string fname=cmds[2];
383    DNSSECPrivateKey dpk;
384    DNSKEYRecordContent drc = getRSAKeyFromISC(&dpk.d_key.getContext(), fname.c_str());
385    dpk.d_algorithm = drc.d_algorithm;
386   
387    if(dpk.d_algorithm == 7)
388      dpk.d_algorithm = 5;
389     
390    cerr<<(int)dpk.d_algorithm<<endl;
391   
392    if(cmds.size() > 3) {
393      if(pdns_iequals(cmds[3], "ZSK"))
394        dpk.d_flags = 256;
395      else if(pdns_iequals(cmds[3], "KSK"))
396        dpk.d_flags = 257;
397      else {
398        cerr<<"Unknown key flag '"<<cmds[3]<<"'\n";
399        exit(1);
400      }
401    }
402    else
403      dpk.d_flags = 257; 
404     
405    dk.addKey(zone, dpk); 
406  }
407  else if(cmds[0]=="export-zone-dnskey") {
408    if(cmds.size() < 3) {
409      cerr<<"Syntax: pdnssec export-zone-dnskey zone-name id"<<endl;
410      exit(1);
411    }
412
413    string zone=cmds[1];
414    unsigned int id=atoi(cmds[2].c_str());
415    DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
416    cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
417    if(dpk.d_flags == 257) {
418      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
419      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
420    }
421  }
422  else {
423    cerr<<"Unknown command '"<<cmds[0]<<"'\n";
424    return 1;
425  }
426  return 0;
427}
428catch(AhuException& ae) {
429  cerr<<"Error: "<<ae.reason<<endl;
430}
431catch(std::exception& e) {
432  cerr<<"Error: "<<e.what()<<endl;
433}
Note: See TracBrowser for help on using the browser.