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

Revision 1801, 11.3 KB (checked in by ahu, 2 years ago)

make pdnssec (hopefully) support dynamically loaded modules too

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()
37{
38   static char pietje[128]="!@@SYSCONFDIR@@:";
39  ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=
40    strcmp(pietje+1,"@@SYSCONFDIR@@:") ? pietje+strlen("@@SYSCONFDIR@@:")+1 : SYSCONFDIR;
41 
42  ::arg().set("launch","Which backends to launch");
43  ::arg().set("dnssec","if we should do dnssec")="true";
44  ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
45  ::arg().setCmd("help","Provide a helpful message");
46  //::arg().laxParse(argc,argv);
47
48  if(::arg().mustDo("help")) {
49    cerr<<"syntax:"<<endl<<endl;
50    cerr<<::arg().helpstring(::arg()["help"])<<endl;
51    exit(99);
52  }
53
54  if(::arg()["config-name"]!="") 
55    s_programname+="-"+::arg()["config-name"];
56
57  string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
58  cleanSlashes(configname);
59
60  cerr<<"configname: '"<<configname<<"'\n";
61 
62  ::arg().laxFile(configname.c_str());
63
64  BackendMakers().launch(::arg()["launch"]); // vrooooom!
65  ::arg().laxFile(configname.c_str());   
66  //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
67
68  S.declare("qsize-q","Number of questions waiting for database attention");
69   
70  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
71  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
72         
73  S.declare("query-cache-hit","Number of hits on the query cache");
74  S.declare("query-cache-miss","Number of misses on the query cache");
75  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
76  ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; 
77  ::arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10";
78  ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";             
79  ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60";
80  ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20";             
81  ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
82  ::arg().set("soa-retry-default","Default SOA retry")="3600";
83  ::arg().set("soa-expire-default","Default SOA expire")="604800";
84    ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
85  ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";   
86  ::arg().set("module-dir","Default directory for modules")=LIBDIR;
87
88  UeberBackend::go();
89}
90
91void orderZone(DNSSECKeeper& dk, const std::string& zone)
92{
93   
94  UeberBackend* B = new UeberBackend("default");
95  SOAData sd;
96 
97  if(!B->getSOA(zone, sd)) {
98    cerr<<"No SOA!"<<endl;
99    return;
100  } 
101  cerr<<"ID: "<<sd.domain_id<<endl;
102  sd.db->list(zone, sd.domain_id);
103  DNSResourceRecord rr;
104
105  set<string> qnames;
106 
107  while(sd.db->get(rr)) {
108  //  cerr<<rr.qname<<endl;
109    qnames.insert(rr.qname);
110  }
111
112  NSEC3PARAMRecordContent ns3pr;
113  dk.getNSEC3PARAM(zone, &ns3pr);
114  string hashed;
115  if(ns3pr.d_salt.empty()) 
116    cerr<<"Adding NSEC ordering information"<<endl;
117  else
118    cerr<<"Adding NSEC3 hashed ordering information"<<endl;
119 
120  BOOST_FOREACH(const string& qname, qnames)
121  {
122    if(ns3pr.d_salt.empty()) // NSEC
123      sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, true);
124    else {
125      hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
126      cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
127      sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, true);
128    }
129  }
130  cerr<<"Done listing"<<endl;
131}
132
133void checkZone(DNSSECKeeper& dk, const std::string& zone)
134{
135  loadMainConfig();
136  reportAllTypes(); 
137  UeberBackend* B = new UeberBackend("default");
138  SOAData sd;
139 
140  if(!B->getSOA(zone, sd)) {
141    cerr<<"No SOA!"<<endl;
142    return;
143  } 
144  cerr<<"ID: "<<sd.domain_id<<endl;
145  sd.db->list(zone, sd.domain_id);
146  DNSResourceRecord rr;
147  uint64_t numrecords=0, numerrors=0;
148 
149  while(sd.db->get(rr)) {
150    if(rr.qtype.getCode() == QType::MX) 
151      rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
152     
153    try {
154      shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
155      string tmp=drc->serialize(rr.qname);
156    }
157    catch(std::exception& e) 
158    {
159      cerr<<"Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
160      cerr<<"Error was: "<<e.what()<<endl;
161      numerrors++;
162    }
163    numrecords++;
164  }
165  cerr<<"Checked "<<numrecords<<" records, "<<numerrors<<" errors"<<endl;
166}
167
168
169int main(int argc, char** argv)
170try
171{
172  po::options_description desc("Allowed options");
173  desc.add_options()
174    ("help,h", "produce help message")
175    ("verbose,v", po::value<bool>(), "be verbose")
176    ("force", "force an action")
177    ("commands", po::value<vector<string> >());
178
179  po::positional_options_description p;
180  p.add("commands", -1);
181  po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
182  po::notify(g_vm);
183
184  vector<string> cmds;
185
186  if(g_vm.count("commands")) 
187    cmds = g_vm["commands"].as<vector<string> >();
188
189  if(cmds.empty() || g_vm.count("help")) {
190    cerr<<"Usage: \npdnssec [options] [show-zone] [secure-zone] [alter-zone] [order-zone] [add-zone-key] [deactivate-zone-key] [remove-zone-key] [activate-zone-key]\n";
191    cerr<<"         [import-zone-key] [export-zone-key] [set-nsec3] [unset-nsec3] [export-zone-dnskey]"<<endl;
192    cerr<<desc<<endl;
193    return 0;
194  }
195
196  loadMainConfig();
197  reportAllTypes();
198  DNSSECKeeper dk;
199
200  if(cmds[0] == "order-zone") {
201    if(cmds.size() != 2) {
202      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
203      return 0;
204    }
205    orderZone(dk, cmds[1]);
206  }
207  else if(cmds[0] == "check-zone") {
208    if(cmds.size() != 2) {
209      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
210      return 0;
211    }
212    checkZone(dk, cmds[1]);
213  }
214
215  else if(cmds[0] == "show-zone") {
216    if(cmds.size() != 2) {
217      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
218      return 0;
219    }
220    const string& zone=cmds[1];
221
222    NSEC3PARAMRecordContent ns3pr;
223    dk.getNSEC3PARAM(zone, &ns3pr);
224   
225    if(ns3pr.d_salt.empty()) 
226      cerr<<"Zone has NSEC semantics"<<endl;
227    else
228      cerr<<"Zone has hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
229   
230    DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
231
232    if(keyset.empty())  {
233      cerr << "No keys for zone '"<<zone<<"'."<<endl;
234    }
235    else { 
236      cout << "keys: "<<endl;
237      BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
238        cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
239        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;
240        if(value.second.keyOrZone) {
241          cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
242          cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY()).getZoneRepresentation() << endl << endl;
243        }
244      }
245    }
246  }
247  else if(cmds[0] == "activate-zone-key") {
248    const string& zone=cmds[1];
249    unsigned int id=atoi(cmds[2].c_str());
250    dk.activateKey(zone, id);
251  }
252  else if(cmds[0] == "deactivate-zone-key") {
253    const string& zone=cmds[1];
254    unsigned int id=atoi(cmds[2].c_str());
255    dk.deactivateKey(zone, id);
256  }
257  else if(cmds[0] == "add-zone-key") {
258    const string& zone=cmds[1];
259    // need to get algorithm & ksk or zsk from commandline
260    cerr<<"Adding a ZSK"<<endl;
261    dk.addKey(zone, 0, 5, 0); 
262  }
263  else if(cmds[0] == "remove-zone-key") {
264    const string& zone=cmds[1];
265    unsigned int id=atoi(cmds[2].c_str());
266    dk.removeKey(zone, id);
267  }
268 
269  else if(cmds[0] == "secure-zone") {
270    if(cmds.size() != 2) {
271      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
272      return 0;
273    }
274    const string& zone=cmds[1];
275    DNSSECPrivateKey dpk;
276   
277    if(dk.haveActiveKSKFor(zone, &dpk) && !g_vm.count("force")) {
278      cerr << "There is a key already for zone '"<<zone<<"', use --force to overwrite"<<endl;
279      return 0;
280    }
281     
282    dk.secureZone(zone, 5);
283
284    if(!dk.haveActiveKSKFor(zone, &dpk)) {
285      cerr << "This should not happen, still no key!" << endl;
286      return 0;
287    }
288    cout<<"Created KSK with tag "<<dpk.getDNSKEY().getTag()<<endl;
289 
290    DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
291
292    if(!zskset.empty() && !g_vm.count("force"))  {
293      cerr<<"There were ZSKs already for zone '"<<zone<<"'"<<endl;
294      return 0;
295    }
296     
297    dk.addKey(zone, false, 5);
298    dk.addKey(zone, false, 5, 0, false); // not active
299
300    zskset = dk.getKeys(zone, false);
301    if(zskset.empty()) {
302      cerr<<"This should not happen, still no ZSK!"<<endl;
303    }
304
305    cout<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
306    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
307      cout<<"id = "<<value.second.id<<", tag = "<<value.first.getDNSKEY().getTag()<<", algo = "<<(int)value.first.d_algorithm<<
308        ", bits = "<<value.first.d_key.getContext().len*8;
309      cout<<"\tActive: "<<value.second.active<<endl;
310    }
311  }
312  else if(cmds[0]=="set-nsec3") {
313    string nsec3params =  cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
314     
315    NSEC3PARAMRecordContent ns3pr(nsec3params);
316    dk.setNSEC3PARAM(cmds[1], ns3pr);
317  }
318  else if(cmds[0]=="unset-nsec3") {
319    dk.unsetNSEC3PARAM(cmds[1]);
320  }
321  else if(cmds[0]=="export-zone-key") {
322    string zone=cmds[1];
323    unsigned int id=atoi(cmds[2].c_str());
324    DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
325    cout << dpk.d_key.convertToISC(dpk.d_algorithm) <<endl;
326  } 
327  else if(cmds[0]=="import-zone-key") {
328    if(cmds.size()!=3) {
329      cerr<<"Syntax: pdnssec import-zone-key zone-name filename"<<endl;
330      exit(1);
331    }
332    string zone=cmds[1];
333    string fname=cmds[2];
334    DNSSECPrivateKey dpk;
335    getRSAKeyFromISC(&dpk.d_key.getContext(), fname.c_str());
336    dpk.d_algorithm = 5;
337    dpk.d_flags = 257;
338    dk.addKey(zone, true, dpk); // add a KSK
339  }
340  else if(cmds[0]=="export-zone-dnskey") {
341    string zone=cmds[1];
342    unsigned int id=atoi(cmds[2].c_str());
343    DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
344    cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
345    if(dpk.d_flags == 257)
346      cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY()).getZoneRepresentation() << endl;
347  }
348  else {
349    cerr<<"Unknown command '"<<cmds[0]<<"'\n";
350    return 1;
351  }
352  return 0;
353}
354catch(AhuException& ae) {
355  cerr<<"Error: "<<ae.reason<<endl;
356}
Note: See TracBrowser for help on using the browser.