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

Revision 1616, 9.4 KB (checked in by ahu, 3 years ago)

implement 'NSEC3', and enable it for NXDOMAIN responses - other cases have yet to be hooked up.
Only works for generic mysql right now. To test, run 'echo 1 0 100 ABCD > ./keys/yourdomain/nsec3param'
And then run pdnssec order-zone yourdomain

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_server";
20
21ArgvMap &arg()
22{
23  static ArgvMap arg;
24  return arg;
25}
26
27
28string humanTime(time_t t)
29{
30  char ret[256];
31  struct tm tm;
32  localtime_r(&t, &tm);
33  strftime(ret, sizeof(ret)-1, "%c", &tm);   // %h:%M %Y-%m-%d
34  return ret;
35}
36
37void loadMainConfig()
38{
39   static char pietje[128]="!@@SYSCONFDIR@@:";
40  ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=
41    strcmp(pietje+1,"@@SYSCONFDIR@@:") ? pietje+strlen("@@SYSCONFDIR@@:")+1 : SYSCONFDIR;
42 
43  ::arg().set("launch","Which backends to launch");
44 
45  ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
46  ::arg().setCmd("help","Provide a helpful message");
47  //::arg().laxParse(argc,argv);
48
49  if(::arg().mustDo("help")) {
50    cerr<<"syntax:"<<endl<<endl;
51    cerr<<::arg().helpstring(::arg()["help"])<<endl;
52    exit(99);
53  }
54
55  if(::arg()["config-name"]!="") 
56    s_programname+="-"+::arg()["config-name"];
57
58  string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
59  cleanSlashes(configname);
60
61  cerr<<"configname: '"<<configname<<"'\n";
62 
63  ::arg().laxFile(configname.c_str());
64
65
66  BackendMakers().launch(::arg()["launch"]); // vrooooom!
67  ::arg().laxFile(configname.c_str());   
68  cerr<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
69
70
71  S.declare("qsize-q","Number of questions waiting for database attention");
72   
73  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
74  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
75         
76  S.declare("query-cache-hit","Number of hits on the query cache");
77  S.declare("query-cache-miss","Number of misses on the query cache");
78  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
79  ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; 
80  ::arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10";
81  ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";             
82  ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60";
83  ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20";             
84  ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
85  ::arg().set("soa-retry-default","Default SOA retry")="3600";
86  ::arg().set("soa-expire-default","Default SOA expire")="604800";
87    ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
88  ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";   
89  UeberBackend::go();
90}
91
92void orderZone(DNSSECKeeper& dk, const std::string& zone)
93{
94  loadMainConfig();
95  reportAllTypes(); 
96  UeberBackend* B = new UeberBackend("default");
97  SOAData sd;
98 
99  if(!B->getSOA(zone, sd)) {
100    cerr<<"No SOA!"<<endl;
101    return;
102  } 
103  cerr<<"ID: "<<sd.domain_id<<endl;
104  sd.db->list(zone, sd.domain_id);
105  DNSResourceRecord rr;
106
107  set<string> qnames;
108 
109  while(sd.db->get(rr)) {
110  //  cerr<<rr.qname<<endl;
111    qnames.insert(rr.qname);
112  }
113 
114  string salt;
115  char tmp[]={0xab, 0xcd};
116  salt.assign(tmp, 2);
117 
118  NSEC3PARAMRecordContent ns3pr;
119  dk.getNSEC3PARAM(zone, &ns3pr);
120  string hashed;
121  BOOST_FOREACH(const string& qname, qnames)
122  {
123    if(ns3pr.d_salt.empty()) // NSEC
124      sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, true);
125    else {
126      hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
127      cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
128      sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, true);
129    }
130  }
131  cerr<<"Done listing"<<endl;
132}
133
134int main(int argc, char** argv)
135try
136{
137  po::options_description desc("Allowed options");
138  desc.add_options()
139    ("help,h", "produce help message")
140    ("key-repository,k", po::value<string>()->default_value("./keys"), "Location of keys")
141    ("verbose,v", po::value<bool>(), "be verbose")
142    ("force", "force an action")
143    ("commands", po::value<vector<string> >());
144
145  po::positional_options_description p;
146  p.add("commands", -1);
147  po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
148  po::notify(g_vm);
149
150  vector<string> cmds;
151
152  if(g_vm.count("commands")) 
153    cmds = g_vm["commands"].as<vector<string> >();
154
155  if(cmds.empty() || g_vm.count("help")) {
156    cerr<<"Usage: \npdnssec [options] [show-zone] [secure-zone] [alter-zone] [order-zone] [update-zone-keys]\n";
157    cerr<<desc<<endl;
158    return 0;
159  }
160
161  DNSSECKeeper dk(g_vm["key-repository"].as<string>());
162
163  if(cmds[0] == "order-zone") {
164    if(cmds.size() != 2) {
165      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
166      return 0;
167    }
168    orderZone(dk, cmds[1]);
169  }
170  else if(cmds[0] == "update-zone-keys") {
171    if(cmds.size() != 2) {
172      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
173      return 0;
174    }
175
176    const string& zone=cmds[1];
177    DNSSECPrivateKey dpk;
178   
179    if(!dk.haveKSKFor(zone, &dpk)) {
180      cerr << "No KSK for zone '"<<zone<<"', can't update the ZSKs"<<endl;
181      return 0;
182    }
183    DNSSECKeeper::zskset_t zskset=dk.getZSKsFor(zone);
184
185    int inforce=0;
186    time_t now = time(&now);
187   
188   
189    if(!zskset.empty())  {
190      cerr<<"There were ZSKs already for zone '"<<zone<<"': "<<endl;
191     
192      BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
193        cerr<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<", "<<humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
194        if(value.second.active) 
195          inforce++;
196        if(value.second.endValidity < now - 2*86400) { // 'expired more than two days ago' 
197          cerr<<"\tThis key is no longer used and too old to keep around, deleting!\n";
198          dk.deleteZSKFor(zone, value.second.fname);
199        } else if(value.second.endValidity < now) { // 'expired more than two days ago' 
200          cerr<<"\tThis key is no longer in active use, but needs to linger\n";
201        }
202      }
203    }
204     
205    if(inforce >= 2) {
206      cerr << "Two or more ZSKs were active already, not generating a third" << endl;
207      return 0;
208    }
209    dk.addZSKFor(zone);
210    dk.addZSKFor(zone, true); // 'next'
211
212    zskset = dk.getZSKsFor(zone);
213    if(zskset.empty()) {
214      cerr<<"This should not happen, still no ZSK!"<<endl;
215    }
216
217    cerr<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
218    BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
219      cerr<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<endl;
220    }
221
222  }
223  else if(cmds[0] == "show-zone") {
224    if(cmds.size() != 2) {
225      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
226      return 0;
227    }
228    const string& zone=cmds[1];
229    DNSSECPrivateKey dpk;
230   
231    if(!dk.haveKSKFor(zone, &dpk)) {
232      cerr << "No KSK for zone '"<<zone<<"'."<<endl;
233    }
234    else {
235      cerr<<"KSK present:"<<endl;
236      cerr<<"Tag = "<<dpk.getDNSKEY().getTag()<<endl;
237      cerr<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< dpk.getDNSKEY().getZoneRepresentation() << endl;
238      cerr<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY()).getZoneRepresentation() << endl << endl;
239    }
240   
241   
242    DNSSECKeeper::zskset_t zskset=dk.getZSKsFor(zone);
243
244    if(zskset.empty())  {
245      cerr << "No ZSKs for zone '"<<zone<<"'."<<endl;
246    }
247    else { 
248      cerr << "ZSKs for zone '"<<zone<<"':"<<endl;
249      BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
250        cerr<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<", "<< humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
251      }
252    }
253  }
254  else if(cmds[0] == "secure-zone") {
255    if(cmds.size() != 2) {
256      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
257      return 0;
258    }
259    const string& zone=cmds[1];
260    DNSSECPrivateKey dpk;
261   
262    if(dk.haveKSKFor(zone, &dpk) && !g_vm.count("force")) {
263      cerr << "There is a key already for zone '"<<zone<<"', use --force to overwrite"<<endl;
264      return 0;
265    }
266     
267    dk.addZone(zone);
268
269    if(!dk.haveKSKFor(zone, &dpk)) {
270      cerr << "This should not happen, still no key!" << endl;
271    }
272    cerr<<"Created KSK with tag "<<dpk.getDNSKEY().getTag()<<endl;
273 
274    DNSSECKeeper::zskset_t zskset=dk.getZSKsFor(zone);
275
276    if(!zskset.empty() && !g_vm.count("force"))  {
277      cerr<<"There were ZSKs already for zone '"<<zone<<"'"<<endl;
278      return 0;
279    }
280     
281    dk.addZSKFor(zone);
282    dk.addZSKFor(zone, true); // 'next'
283
284    zskset = dk.getZSKsFor(zone);
285    if(zskset.empty()) {
286      cerr<<"This should not happen, still no ZSK!"<<endl;
287    }
288
289    cerr<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
290    BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
291      cerr<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<endl;
292    }
293  }
294  else {
295    cerr<<"Unknown command '"<<cmds[0]<<"'\n";
296    return 1;
297  }
298  return 0;
299}
300catch(AhuException& ae) {
301  cerr<<"Error: "<<ae.reason<<endl;
302}
Note: See TracBrowser for help on using the browser.