]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/pdnssec.cc
Merge pull request #2078 from jpmens/typo1
[thirdparty/pdns.git] / pdns / pdnssec.cc
1 #include "dnsseckeeper.hh"
2 #include "dnssecinfra.hh"
3 #include "statbag.hh"
4 #include "base32.hh"
5 #include "base64.hh"
6 #include <boost/foreach.hpp>
7 #include <boost/program_options.hpp>
8 #include <boost/assign/std/vector.hpp>
9 #include <boost/assign/list_of.hpp>
10 #include "dnsbackend.hh"
11 #include "ueberbackend.hh"
12 #include "arguments.hh"
13 #include "packetcache.hh"
14 #include "zoneparser-tng.hh"
15 #include "signingpipe.hh"
16 #include "dns_random.hh"
17 #include <fstream>
18 #ifdef HAVE_SQLITE3
19 #include "ssqlite3.hh"
20 #include "bind-dnssec.schema.sqlite3.sql.h"
21 #endif
22
23 StatBag S;
24 PacketCache PC;
25
26 namespace po = boost::program_options;
27 po::variables_map g_vm;
28
29 string s_programname="pdns";
30
31 namespace {
32 bool g_verbose;
33 }
34
35 ArgvMap &arg()
36 {
37 static ArgvMap arg;
38 return arg;
39 }
40
41 string humanTime(time_t t)
42 {
43 char ret[256];
44 struct tm tm;
45 localtime_r(&t, &tm);
46 strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d
47 return ret;
48 }
49
50 static void algorithm2name(uint8_t algo, string &name) {
51 switch(algo) {
52 case 0:
53 name = "Reserved"; return;
54 case 1:
55 name = "RSAMD5"; return;
56 case 2:
57 name = "DH"; return;
58 case 3:
59 name = "DSA"; return;
60 case 4:
61 name = "ECC"; return;
62 case 5:
63 name = "RSASHA1"; return;
64 case 6:
65 name = "DSA-NSEC3-SHA1"; return;
66 case 7:
67 name = "RSASHA1-NSEC3-SHA1"; return;
68 case 8:
69 name = "RSASHA256"; return;
70 case 9:
71 name = "Reserved"; return;
72 case 10:
73 name = "RSASHA512"; return;
74 case 11:
75 name = "Reserved"; return;
76 case 12:
77 name = "ECC-GOST"; return;
78 case 13:
79 name = "ECDSAP256SHA256"; return;
80 case 14:
81 name = "ECDSAP384SHA384"; return;
82 case 252:
83 name = "INDIRECT"; return;
84 case 253:
85 name = "PRIVATEDNS"; return;
86 case 254:
87 name = "PRIVATEOID"; return;
88 default:
89 name = "Unallocated/Reserved"; return;
90 }
91 };
92
93 static int shorthand2algorithm(const string &algorithm)
94 {
95 if (!algorithm.compare("rsamd5")) return 1;
96 if (!algorithm.compare("dh")) return 2;
97 if (!algorithm.compare("dsa")) return 3;
98 if (!algorithm.compare("ecc")) return 4;
99 if (!algorithm.compare("rsasha1")) return 5;
100 if (!algorithm.compare("rsasha256")) return 8;
101 if (!algorithm.compare("rsasha512")) return 10;
102 if (!algorithm.compare("gost")) return 12;
103 if (!algorithm.compare("ecdsa256")) return 13;
104 if (!algorithm.compare("ecdsa384")) return 14;
105 if (!algorithm.compare("ed25519")) return 250;
106 return -1;
107 }
108
109 void loadMainConfig(const std::string& configdir)
110 {
111 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
112 ::arg().set("pipebackend-abi-version","Version of the pipe backend ABI")="1";
113 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
114 ::arg().set("launch","Which backends to launch");
115 ::arg().set("dnssec","if we should do dnssec")="true";
116 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
117 ::arg().setCmd("help","Provide a helpful message");
118 //::arg().laxParse(argc,argv);
119
120 if(::arg().mustDo("help")) {
121 cout<<"syntax:"<<endl<<endl;
122 cout<<::arg().helpstring(::arg()["help"])<<endl;
123 exit(0);
124 }
125
126 if(::arg()["config-name"]!="")
127 s_programname+="-"+::arg()["config-name"];
128
129 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
130 cleanSlashes(configname);
131
132 ::arg().set("default-ksk-algorithms","Default KSK algorithms")="rsasha256";
133 ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
134 ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="rsasha256";
135 ::arg().set("default-zsk-size","Default KSK size (0 means default)")="0";
136 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
137 ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
138 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
139 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
140 ::arg().set("loglevel","Amount of logging. Higher is more.")="0";
141 ::arg().setSwitch("direct-dnskey","Fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
142 ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
143 ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
144 ::arg().laxFile(configname.c_str());
145
146 L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
147
148 BackendMakers().launch(::arg()["launch"]); // vrooooom!
149 ::arg().laxFile(configname.c_str());
150 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
151
152 S.declare("qsize-q","Number of questions waiting for database attention");
153
154 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
155 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
156
157 S.declare("query-cache-hit","Number of hits on the query cache");
158 S.declare("query-cache-miss","Number of misses on the query cache");
159 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
160 ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no";
161 ::arg().set("recursive-cache-ttl","Seconds to store packets for recursive queries in the PacketCache")="10";
162 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
163 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
164 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
165 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
166 ::arg().set("default-soa-mail","mail address to insert in the SOA record if none set in the backend")="";
167 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
168 ::arg().set("soa-retry-default","Default SOA retry")="3600";
169 ::arg().set("soa-expire-default","Default SOA expire")="604800";
170 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
171
172 UeberBackend::go();
173 }
174
175 // irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
176 // I think this has to do with interlocking transactions between B and DK, but unsure.
177 bool rectifyZone(DNSSECKeeper& dk, const std::string& zone)
178 {
179 if(dk.isPresigned(zone)){
180 cerr<<"Rectify presigned zone '"<<zone<<"' is not allowed/necessary."<<endl;
181 return false;
182 }
183
184 UeberBackend B("default");
185 bool doTransaction=true; // but see above
186 SOAData sd;
187 sd.db = (DNSBackend*)-1;
188
189 if(!B.getSOA(zone, sd)) {
190 cerr<<"No SOA known for '"<<zone<<"', is such a zone in the database?"<<endl;
191 return false;
192 }
193 sd.db->list(zone, sd.domain_id);
194
195 DNSResourceRecord rr;
196 set<string> qnames, nsset, dsnames, insnonterm, delnonterm;
197 map<string,bool> nonterm;
198 bool doent=true;
199
200 while(sd.db->get(rr)) {
201 if (rr.qtype.getCode())
202 {
203 qnames.insert(rr.qname);
204 if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, zone))
205 nsset.insert(rr.qname);
206 if(rr.qtype.getCode() == QType::DS)
207 dsnames.insert(rr.qname);
208 }
209 else
210 if(doent)
211 delnonterm.insert(rr.qname);
212 }
213
214 NSEC3PARAMRecordContent ns3pr;
215 bool narrow;
216 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
217 bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
218 if(sd.db->doesDNSSEC())
219 {
220 if(!haveNSEC3)
221 cerr<<"Adding NSEC ordering information "<<endl;
222 else if(!narrow) {
223 if(!isOptOut)
224 cerr<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'"<<endl;
225 else
226 cerr<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone<<"'"<<endl;
227 } else
228 cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
229 }
230 else
231 cerr<<"Non DNSSEC zone, only adding empty non-terminals"<<endl;
232
233 if(doTransaction)
234 sd.db->startTransaction("", -1);
235
236 bool realrr=true;
237 string hashed;
238
239 uint32_t maxent = ::arg().asNum("max-ent-entries");
240
241 dononterm:;
242 BOOST_FOREACH(const string& qname, qnames)
243 {
244 bool auth=true;
245 string shorter(qname);
246
247 if(realrr) {
248 do {
249 if(nsset.count(shorter)) {
250 auth=false;
251 break;
252 }
253 } while(chopOff(shorter));
254 }
255
256 if(haveNSEC3)
257 {
258 if(!narrow && (realrr || !isOptOut || nonterm.find(qname)->second)) {
259 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname));
260 if(g_verbose)
261 cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
262 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
263 }
264 else {
265 if(!realrr)
266 auth=false;
267 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
268 }
269 }
270 else // NSEC
271 {
272 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, auth);
273 if (!realrr)
274 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
275 }
276
277 if(realrr)
278 {
279 if (dsnames.count(qname))
280 sd.db->setDNSSECAuthOnDsRecord(sd.domain_id, qname);
281 if (!auth || nsset.count(qname)) {
282 if(isOptOut)
283 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "NS");
284 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "A");
285 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "AAAA");
286 }
287
288 if(doent)
289 {
290 shorter=qname;
291 while(!pdns_iequals(shorter, zone) && chopOff(shorter))
292 {
293 if(!qnames.count(shorter))
294 {
295 if(!(maxent))
296 {
297 cerr<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
298 insnonterm.clear();
299 delnonterm.clear();
300 doent=false;
301 break;
302 }
303
304 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
305 insnonterm.insert(shorter);
306 else
307 delnonterm.erase(shorter);
308
309 if (!nonterm.count(shorter)) {
310 nonterm.insert(pair<string, bool>(shorter, auth));
311 --maxent;
312 } else if (auth)
313 nonterm[shorter]=true;
314 }
315 }
316 }
317 }
318 }
319
320 if(realrr)
321 {
322 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
323 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
324 {
325 sd.db->updateEmptyNonTerminals(sd.domain_id, zone, insnonterm, delnonterm, !doent);
326 }
327 if(doent)
328 {
329 realrr=false;
330 qnames.clear();
331 pair<string,bool> nt;
332 BOOST_FOREACH(nt, nonterm){
333 qnames.insert(nt.first);
334 }
335 goto dononterm;
336 }
337 }
338
339 if(doTransaction)
340 sd.db->commitTransaction();
341
342 return true;
343 }
344
345 void dbBench(const std::string& fname)
346 {
347 ::arg().set("query-cache-ttl")="0";
348 ::arg().set("negquery-cache-ttl")="0";
349 UeberBackend B("default");
350
351 vector<string> domains;
352 if(!fname.empty()) {
353 ifstream ifs(fname.c_str());
354 if(!ifs) {
355 cerr<<"Could not open '"<<fname<<"' for reading domain names to query"<<endl;
356 }
357 string line;
358 while(getline(ifs,line)) {
359 trim(line);
360 domains.push_back(line);
361 }
362 }
363 if(domains.empty())
364 domains.push_back("powerdns.com");
365
366 int n=0;
367 DNSResourceRecord rr;
368 DTime dt;
369 dt.set();
370 unsigned int hits=0, misses=0;
371 for(; n < 10000; ++n) {
372 const string& domain = domains[random() % domains.size()];
373 B.lookup(QType(QType::NS), domain);
374 while(B.get(rr)) {
375 hits++;
376 }
377 B.lookup(QType(QType::A), boost::lexical_cast<string>(random())+"."+domain);
378 while(B.get(rr)) {
379 }
380 misses++;
381
382 }
383 cout<<0.001*dt.udiff()/n<<" millisecond/lookup"<<endl;
384 cout<<"Retrieved "<<hits<<" records, did "<<misses<<" queries which should have no match"<<endl;
385 cout<<"Packet cache reports: "<<S.read("query-cache-hit")<<" hits (should be 0) and "<<S.read("query-cache-miss") <<" misses"<<endl;
386 }
387
388 void rectifyAllZones(DNSSECKeeper &dk)
389 {
390 UeberBackend B("default");
391 vector<DomainInfo> domainInfo;
392
393 B.getAllDomains(&domainInfo);
394 BOOST_FOREACH(DomainInfo di, domainInfo) {
395 cerr<<"Rectifying "<<di.zone<<": ";
396 rectifyZone(dk, di.zone);
397 }
398 cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
399 }
400
401 int checkZone(DNSSECKeeper &dk, UeberBackend &B, const std::string& zone)
402 {
403 SOAData sd;
404 sd.db=(DNSBackend*)-1;
405 if(!B.getSOA(zone, sd)) {
406 cout<<"[error] No SOA record present, or active, in zone '"<<zone<<"'"<<endl;
407 cout<<"Checked 0 records of '"<<zone<<"', 1 errors, 0 warnings."<<endl;
408 return 1;
409 }
410 bool presigned=dk.isPresigned(zone);
411 sd.db->list(zone, sd.domain_id, true);
412 DNSResourceRecord rr;
413 uint64_t numrecords=0, numerrors=0, numwarnings=0;
414
415 bool hasNsAtApex = false;
416 set<string> records, cnames, noncnames;
417 map<string, unsigned int> ttl;
418
419 ostringstream content;
420 pair<map<string, unsigned int>::iterator,bool> ret;
421
422 while(sd.db->get(rr)) {
423 if(!rr.qtype.getCode())
424 continue;
425
426 numrecords++;
427
428 if(rr.qtype.getCode() == QType::SOA) {
429 vector<string>parts;
430 stringtok(parts, rr.content);
431
432 ostringstream o;
433 o<<rr.content;
434 for(int pleft=parts.size(); pleft < 7; ++pleft) {
435 o<<" 0";
436 }
437 rr.content=o.str();
438 }
439
440 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
441 rr.content = "\""+rr.content+"\"";
442
443 try {
444 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
445 string tmp=drc->serialize(rr.qname);
446 tmp = drc->getZoneRepresentation();
447 if (rr.qtype.getCode() != QType::AAAA) {
448 if (!pdns_iequals(tmp, rr.content)) {
449 cout<<"[Warning] Parsed and original record content are not equal: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"' (Content parsed as '"<<tmp<<"')"<<endl;
450 numwarnings++;
451 }
452 } else {
453 struct in6_addr tmpbuf;
454 if (inet_pton(AF_INET6, rr.content.c_str(), &tmpbuf) != 1 || rr.content.find('.') != string::npos) {
455 cout<<"[Warning] Following record is not a valid IPv6 address: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"'"<<endl;
456 numwarnings++;
457 }
458 }
459 }
460 catch(std::exception& e)
461 {
462 cout<<"[Error] Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
463 cout<<"[Error] Error was: "<<e.what()<<endl;
464 numerrors++;
465 continue;
466 }
467
468 if(!endsOn(rr.qname, zone)) {
469 cout<<"[Warning] Record '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"' is out-of-zone."<<endl;
470 numwarnings++;
471 continue;
472 }
473
474 content.str("");
475 content<<rr.qname<<" "<<rr.qtype.getName()<<" "<<rr.content;
476 if (records.count(toLower(content.str()))) {
477 cout<<"[Error] Duplicate record found in rrset: '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"'"<<endl;
478 numerrors++;
479 continue;
480 } else
481 records.insert(toLower(content.str()));
482
483 content.str("");
484 content<<rr.qname<<" "<<rr.qtype.getName();
485 if (rr.qtype.getCode() == QType::RRSIG) {
486 RRSIGRecordContent rrc(rr.content);
487 content<<" ("<<DNSRecordContent::NumberToType(rrc.d_type)<<")";
488 }
489 ret = ttl.insert(pair<string, unsigned int>(toLower(content.str()), rr.ttl));
490 if (ret.second == false && ret.first->second != rr.ttl) {
491 cout<<"[Error] TTL mismatch in rrset: '"<<rr.qname<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' ("<<ret.first->second<<" != "<<rr.ttl<<")"<<endl;
492 numerrors++;
493 continue;
494 }
495
496 if(pdns_iequals(rr.qname, zone)) {
497 if (rr.qtype.getCode() == QType::NS) {
498 hasNsAtApex=true;
499 } else if (rr.qtype.getCode() == QType::DS) {
500 cout<<"[Warning] DS at apex in zone '"<<zone<<"', should not be here."<<endl;
501 numwarnings++;
502 }
503 } else {
504 if (rr.qtype.getCode() == QType::SOA) {
505 cout<<"[Error] SOA record not at apex '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"'"<<endl;
506 numerrors++;
507 continue;
508 } else if (rr.qtype.getCode() == QType::DNSKEY) {
509 cout<<"[Warning] DNSKEY record not at apex '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"', should not be here."<<endl;
510 numwarnings++;
511 }
512 }
513
514 if (rr.qtype.getCode() == QType::CNAME) {
515 if (!cnames.count(toLower(rr.qname)))
516 cnames.insert(toLower(rr.qname));
517 else {
518 cout<<"[Error] Duplicate CNAME found at '"<<rr.qname<<"'"<<endl;
519 numerrors++;
520 continue;
521 }
522 } else {
523 if (rr.qtype.getCode() == QType::RRSIG) {
524 if(!presigned) {
525 cout<<"[Error] RRSIG found at '"<<rr.qname<<"' in non-presigned zone. These do not belong in the database."<<endl;
526 numerrors++;
527 continue;
528 }
529 } else
530 noncnames.insert(toLower(rr.qname));
531 }
532
533 if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
534 {
535 cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname<<"'. These do not belong in the database."<<endl;
536 numerrors++;
537 continue;
538 }
539
540 if(!presigned && rr.qtype.getCode() == QType::DNSKEY)
541 {
542 if(::arg().mustDo("direct-dnskey"))
543 {
544 if(rr.ttl != sd.default_ttl)
545 {
546 cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
547 numwarnings++;
548 }
549 }
550 else
551 {
552 cout<<"[Warning] DNSKEY at '"<<rr.qname<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl;
553 numwarnings++;
554 }
555 }
556
557 if(rr.qtype.getCode() == QType::URL || rr.qtype.getCode() == QType::MBOXFW) {
558 cout<<"[Error] The recordtype "<<rr.qtype.getName()<<" for record '"<<rr.qname<<"' is no longer supported."<<endl;
559 numerrors++;
560 continue;
561 }
562
563 if (rr.qname[rr.qname.size()-1] == '.') {
564 cout<<"[Error] Record '"<<rr.qname<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
565 numerrors++;
566 }
567
568 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) &&
569 rr.content[rr.content.size()-1] == '.') {
570 cout<<"[Warning] The record "<<rr.qname<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
571 numwarnings++;
572 }
573
574 if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
575 {
576 cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
577 numerrors++;
578 }
579 }
580
581 for(set<string>::const_iterator i = cnames.begin(); i != cnames.end(); i++) {
582 if (noncnames.find(*i) != noncnames.end()) {
583 cout<<"[Error] CNAME "<<*i<<" found, but other records with same label exist."<<endl;
584 numerrors++;
585 }
586 }
587
588 if(!hasNsAtApex) {
589 cout<<"[Error] No NS record at zone apex in zone '"<<zone<<"'"<<endl;
590 numerrors++;
591 }
592
593 cout<<"Checked "<<numrecords<<" records of '"<<zone<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
594 return numerrors;
595 }
596
597 int checkAllZones(DNSSECKeeper &dk)
598 {
599 UeberBackend B("default");
600 vector<DomainInfo> domainInfo;
601
602 B.getAllDomains(&domainInfo, true);
603 int errors=0;
604 BOOST_FOREACH(DomainInfo di, domainInfo) {
605 if (checkZone(dk, B, di.zone) > 0)
606 errors++;
607 }
608 cout<<"Checked "<<domainInfo.size()<<" zones, "<<errors<<" had errors."<<endl;
609 return 0;
610 }
611
612 int increaseSerial(const string& zone, DNSSECKeeper &dk)
613 {
614 UeberBackend B("default");
615 SOAData sd;
616 sd.db=(DNSBackend*)-1;
617 if(!B.getSOA(zone, sd)) {
618 cout<<"No SOA for zone '"<<zone<<"'"<<endl;
619 return -1;
620 }
621
622 string soaEditKind;
623 dk.getFromMeta(zone, "SOA-EDIT", soaEditKind);
624
625 sd.db->lookup(QType(QType::SOA), zone);
626 vector<DNSResourceRecord> rrs;
627 DNSResourceRecord rr;
628 while (sd.db->get(rr)) {
629 if (rr.qtype.getCode() == QType::SOA)
630 rrs.push_back(rr);
631 }
632
633 if (rrs.size() > 1) {
634 cerr<<rrs.size()<<" SOA records found for "<<zone<<"!"<<endl;
635 return -1;
636 }
637 if (rrs.size() < 1) {
638 cerr<<zone<<" not found!"<<endl;
639 }
640
641 if (soaEditKind.empty()) {
642 sd.serial++;
643 }
644 else if(pdns_iequals(soaEditKind,"INCREMENT-WEEKS")) {
645 sd.serial++;
646 }
647 else if(pdns_iequals(soaEditKind,"INCEPTION-INCREMENT")) {
648 uint32_t today_serial = localtime_format_YYYYMMDDSS(time(NULL), 1);
649
650 if (sd.serial < today_serial) {
651 sd.serial = today_serial;
652 }
653 else {
654 sd.serial++;
655 }
656 }
657 else {
658 sd.serial = calculateEditSOA(sd, soaEditKind) + 1;
659 }
660 rrs[0].content = serializeSOAData(sd);
661
662 sd.db->startTransaction("", -1);
663
664 if (! sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, rrs)) {
665 sd.db->abortTransaction();
666 cerr<<"Backend did not replace SOA record. Backend might not support this operation."<<endl;
667 return -1;
668 }
669
670 if (sd.db->doesDNSSEC()) {
671 NSEC3PARAMRecordContent ns3pr;
672 bool narrow;
673 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
674
675 if(haveNSEC3)
676 {
677 if(!narrow) {
678 string hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rrs[0].qname));
679 if(g_verbose)
680 cerr<<"'"<<rrs[0].qname<<"' -> '"<< hashed <<"'"<<endl;
681 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, rrs[0].qname, hashed, 1);
682 }
683 else {
684 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, rrs[0].qname, 1);
685 }
686 } else {
687 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, rrs[0].qname, 1);
688 }
689 }
690
691 sd.db->commitTransaction();
692
693 cout<<"SOA serial for zone "<<zone<<" set to "<<sd.serial<<endl;
694 return 0;
695 }
696
697 int deleteZone(const string &zone) {
698 UeberBackend B;
699 DomainInfo di;
700 if (! B.getDomainInfo(zone, di)) {
701 cerr<<"Domain '"<<zone<<"' not found!"<<endl;
702 return 1;
703 }
704
705 if(di.backend->deleteDomain(zone))
706 return 0;
707
708 cerr<<"Failed to delete domain '"+zone+"'"<<endl;;
709 return 1;
710 }
711
712 int listZone(const string &zone) {
713 UeberBackend B;
714 DomainInfo di;
715
716 if (! B.getDomainInfo(zone, di)) {
717 cerr<<"Domain '"<<zone<<"' not found!"<<endl;
718 return 1;
719 }
720 di.backend->list(zone, di.id);
721 DNSResourceRecord rr;
722 while(di.backend->get(rr)) {
723 if(rr.qtype.getCode()) {
724 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) && !rr.content.empty() && rr.content[rr.content.size()-1] != '.')
725 rr.content.append(1, '.');
726
727 cout<<rr.qname<<".\t"<<rr.ttl<<"\tIN\t"<<rr.qtype.getName()<<"\t"<<rr.content<<endl;
728 }
729 }
730 return 0;
731 }
732
733 int loadZone(string zone, const string& fname) {
734 UeberBackend B;
735 DomainInfo di;
736
737 if (B.getDomainInfo(zone, di)) {
738 cerr<<"Domain '"<<zone<<"' exists already, replacing contents"<<endl;
739 }
740 else {
741 cerr<<"Creating '"<<zone<<"'"<<endl;
742 B.createDomain(zone);
743
744 if(!B.getDomainInfo(zone, di)) {
745 cerr<<"Domain '"<<zone<<"' was not created!"<<endl;
746 return 1;
747 }
748 }
749 DNSBackend* db = di.backend;
750 ZoneParserTNG zpt(fname, zone);
751
752 DNSResourceRecord rr;
753 if(!db->startTransaction(zone, di.id)) {
754 cerr<<"Unable to start transaction for load of zone '"<<zone<<"'"<<endl;
755 return 1;
756 }
757 rr.domain_id=di.id;
758 while(zpt.get(rr)) {
759 if(!endsOn(stripDot(rr.qname), zone) && rr.qname!=zone) {
760 cerr<<"File contains record named '"<<rr.qname<<"' which is not part of zone '"<<zone<<"'"<<endl;
761 return 1;
762 }
763 rr.qname=stripDot(rr.qname);
764 db->feedRecord(rr);
765 }
766 db->commitTransaction();
767 return 0;
768 }
769
770 int createZone(const string &zone) {
771 UeberBackend B;
772 DomainInfo di;
773 if (B.getDomainInfo(zone, di)) {
774 cerr<<"Domain '"<<zone<<"' exists already"<<endl;
775 return 1;
776 }
777 cerr<<"Creating '"<<zone<<"'"<<endl;
778 B.createDomain(zone);
779
780 if(!B.getDomainInfo(zone, di)) {
781 cerr<<"Domain '"<<zone<<"' was not created!"<<endl;
782 return 1;
783 }
784 return 1;
785 }
786
787
788 int listAllZones(const string &type="") {
789
790 int kindFilter = -1;
791 if (type.size()) {
792 if (toUpper(type) == "MASTER")
793 kindFilter = 0;
794 else if (toUpper(type) == "SLAVE")
795 kindFilter = 1;
796 else if (toUpper(type) == "NATIVE")
797 kindFilter = 2;
798 else {
799 cerr<<"Syntax: pdnssec list-all-zones [master|slave|native]"<<endl;
800 return 1;
801 }
802 }
803
804 UeberBackend B("default");
805
806 vector<DomainInfo> domains;
807 B.getAllDomains(&domains);
808
809 int count = 0;
810 for (vector<DomainInfo>::const_iterator di=domains.begin(); di != domains.end(); di++) {
811 if (di->kind == kindFilter || kindFilter == -1) {
812 cout<<di->zone<<endl;
813 count++;
814 }
815 }
816
817 if (kindFilter != -1)
818 cout<<type<<" zonecount:"<<count<<endl;
819 else
820 cout<<"All zonecount:"<<count<<endl;
821 return 0;
822 }
823
824 void testAlgorithm(int algo)
825 {
826 DNSCryptoKeyEngine::testOne(algo);
827 }
828
829 void testAlgorithms()
830 {
831 DNSCryptoKeyEngine::testAll();
832 }
833
834 void testSpeed(DNSSECKeeper& dk, const string& zone, const string& remote, int cores)
835 {
836 DNSResourceRecord rr;
837 rr.qname="blah."+zone;
838 rr.qtype=QType::A;
839 rr.ttl=3600;
840 rr.auth=1;
841 rr.qclass = QClass::IN;
842 rr.d_place=DNSResourceRecord::ANSWER;
843
844 UeberBackend db("key-only");
845
846 if ( ! db.backends.size() )
847 {
848 throw runtime_error("No backends available for DNSSEC key storage");
849 }
850
851 ChunkedSigningPipe csp(zone, 1, remote, cores);
852
853 vector<DNSResourceRecord> signatures;
854 uint32_t rnd;
855 unsigned char* octets = (unsigned char*)&rnd;
856 char tmp[25];
857 DTime dt;
858 dt.set();
859 for(unsigned int n=0; n < 100000; ++n) {
860 rnd = random();
861 snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
862 octets[0], octets[1], octets[2], octets[3]);
863 rr.content=tmp;
864
865 snprintf(tmp, sizeof(tmp), "r-%u", rnd);
866 rr.qname=string(tmp)+"."+zone;
867
868 if(csp.submit(rr))
869 while(signatures = csp.getChunk(), !signatures.empty())
870 ;
871 }
872 cerr<<"Flushing the pipe, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
873 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiffNoReset()/1000000.0) << " sigs/s"<<endl;
874 while(signatures = csp.getChunk(true), !signatures.empty())
875 ;
876 cerr<<"Done, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
877 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s"<<endl;
878 }
879
880 void verifyCrypto(const string& zone)
881 {
882 ZoneParserTNG zpt(zone);
883 DNSResourceRecord rr;
884 DNSKEYRecordContent drc;
885 RRSIGRecordContent rrc;
886 DSRecordContent dsrc;
887 vector<shared_ptr<DNSRecordContent> > toSign;
888 string qname, apex;
889 dsrc.d_digesttype=0;
890 while(zpt.get(rr)) {
891 if(rr.qtype.getCode() == QType::DNSKEY) {
892 cerr<<"got DNSKEY!"<<endl;
893 apex=rr.qname;
894 drc = *dynamic_cast<DNSKEYRecordContent*>(DNSRecordContent::mastermake(QType::DNSKEY, 1, rr.content));
895 }
896 else if(rr.qtype.getCode() == QType::RRSIG) {
897 cerr<<"got RRSIG"<<endl;
898 rrc = *dynamic_cast<RRSIGRecordContent*>(DNSRecordContent::mastermake(QType::RRSIG, 1, rr.content));
899 }
900 else if(rr.qtype.getCode() == QType::DS) {
901 cerr<<"got DS"<<endl;
902 dsrc = *dynamic_cast<DSRecordContent*>(DNSRecordContent::mastermake(QType::DS, 1, rr.content));
903 }
904 else {
905 qname = rr.qname;
906 toSign.push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
907 }
908 }
909
910 string msg = getMessageForRRSET(qname, rrc, toSign);
911 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
912 if(dsrc.d_digesttype) {
913 cerr<<"Calculated DS: "<<apex<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
914 cerr<<"Original DS: "<<apex<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
915 }
916 #if 0
917 DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
918 "Algorithm: 12 (ECC-GOST)\n"
919 "GostAsn1: MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQg/9MiXtXKg9FDXDN/R9CmVhJDyuzRAIgh4tPwCu4NHIs=\n");
920 string resign=key->sign(hash);
921 cerr<<Base64Encode(resign)<<endl;
922 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(hash, resign)<<endl;
923 #endif
924
925 }
926 bool disableDNSSECOnZone(DNSSECKeeper& dk, const string& zone)
927 {
928 UeberBackend B("default");
929 DomainInfo di;
930
931 if (!B.getDomainInfo(zone, di)){
932 cerr << "No such zone in the database" << endl;
933 return false;
934 }
935
936 if(!dk.isSecuredZone(zone)) {
937 cerr<<"Zone is not secured"<<endl;
938 return false;
939 }
940 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
941
942 if(keyset.empty()) {
943 cerr << "No keys for zone '"<<zone<<"'."<<endl;
944 }
945 else {
946 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
947 dk.deactivateKey(zone, value.second.id);
948 dk.removeKey(zone, value.second.id);
949 }
950 }
951 dk.unsetNSEC3PARAM(zone);
952 dk.unsetPresigned(zone);
953 return true;
954 }
955 bool showZone(DNSSECKeeper& dk, const std::string& zone)
956 {
957 UeberBackend B("default");
958 DomainInfo di;
959 std::vector<std::string> meta;
960
961 if (!B.getDomainInfo(zone, di)){
962 cerr << "No such zone in the database" << endl;
963 return false;
964 }
965
966 if(!dk.isSecuredZone(zone)) {
967 cerr<<"Zone is not actively secured"<<endl;
968 }
969 NSEC3PARAMRecordContent ns3pr;
970 bool narrow;
971 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
972
973 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
974 if (B.getDomainMetadata(zone, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
975 cerr << "Zone has following allowed TSIG key(s): " << boost::join(meta, ",") << endl;
976 }
977
978 meta.clear();
979 if (B.getDomainMetadata(zone, "AXFR-MASTER-TSIG", meta) && meta.size() > 0) {
980 cerr << "Zone uses following TSIG key(s): " << boost::join(meta, ",") << endl;
981 }
982
983 cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
984
985 if(keyset.empty()) {
986 cerr << "No keys for zone '"<<zone<<"'."<<endl;
987 }
988 else {
989 if(!haveNSEC3)
990 cout<<"Zone has NSEC semantics"<<endl;
991 else
992 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
993
994 cout << "keys: "<<endl;
995 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
996 string algname;
997 algorithm2name(value.first.d_algorithm, algname);
998 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
999 cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< " ( " + algname + " ) "<<endl;
1000 if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || g_verbose)
1001 cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
1002 if(value.second.keyOrZone || g_verbose) {
1003 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
1004 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
1005 try {
1006 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
1007 cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
1008 }
1009 catch(...)
1010 {
1011 }
1012 try {
1013 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 4).getZoneRepresentation();
1014 cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
1015 }
1016 catch(...)
1017 {
1018 }
1019 cout<<endl;
1020 }
1021 }
1022 }
1023 return true;
1024 }
1025
1026 bool secureZone(DNSSECKeeper& dk, const std::string& zone)
1027 {
1028 // parse attribute
1029 vector<string> k_algos;
1030 vector<string> z_algos;
1031 int k_size;
1032 int z_size;
1033
1034 stringtok(k_algos, ::arg()["default-ksk-algorithms"], " ,");
1035 k_size = ::arg().asNum("default-ksk-size");
1036 stringtok(z_algos, ::arg()["default-zsk-algorithms"], " ,");
1037 z_size = ::arg().asNum("default-zsk-size");
1038
1039 if (k_size < 0) {
1040 throw runtime_error("KSK key size must be equal to or greater than 0");
1041 }
1042
1043 if (k_algos.size() < 1) {
1044 throw runtime_error("No algorithm(s) given for KSK");
1045 }
1046
1047 if (z_size < 0) {
1048 throw runtime_error("ZSK key size must be equal to or greater than 0");
1049 }
1050
1051 if (z_algos.size() < 1) {
1052 throw runtime_error("No algorithm(s) given for ZSK");
1053 }
1054
1055 if(dk.isSecuredZone(zone)) {
1056 cerr << "Zone '"<<zone<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
1057 return false;
1058 }
1059
1060 DomainInfo di;
1061 UeberBackend B("default");
1062 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
1063 cout<<"Can't find a zone called '"<<zone<<"'"<<endl;
1064 return false;
1065 }
1066
1067 if(di.kind == DomainInfo::Slave)
1068 {
1069 cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
1070 cout<<"pdnssec disable-dnssec "<<zone<<" right now!"<<endl;
1071 }
1072
1073 if (k_size)
1074 cout << "Securing zone with " << k_algos[0] << " algorithm with key size " << k_size << endl;
1075 else
1076 cout << "Securing zone with " << k_algos[0] << " algorithm with default key size" << endl;
1077
1078 // run secure-zone with first default algorith, then add keys
1079 if(!dk.secureZone(zone, shorthand2algorithm(k_algos[0]), k_size)) {
1080 cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC"<<endl;
1081 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
1082 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
1083 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
1084 return false;
1085 }
1086
1087 if(!dk.isSecuredZone(zone)) {
1088 cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl;
1089 cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl;
1090 cerr<<"If you run with the BIND backend, make sure you have configured"<<endl;
1091 cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl;
1092 cerr<<"'pdnssec create-bind-db /path/fname'!"<<endl;
1093 return false;
1094 }
1095
1096 DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
1097
1098 if(!zskset.empty()) {
1099 cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
1100 return false;
1101 }
1102
1103 for(vector<string>::iterator i = k_algos.begin()+1; i != k_algos.end(); i++)
1104 dk.addKey(zone, true, shorthand2algorithm(*i), k_size, true); // obvious errors will have been caught above
1105
1106 BOOST_FOREACH(string z_algo, z_algos)
1107 {
1108 int algo = shorthand2algorithm(z_algo);
1109 dk.addKey(zone, false, algo, z_size);
1110 }
1111
1112 // rectifyZone(dk, zone);
1113 // showZone(dk, zone);
1114 cout<<"Zone "<<zone<<" secured"<<endl;
1115 return true;
1116 }
1117
1118 void testSchema(DNSSECKeeper& dk, const std::string& zone)
1119 {
1120 cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
1121 cout<<"Please clean up after this."<<endl;
1122 cout<<endl;
1123 cout<<"Constructing UeberBackend"<<endl;
1124 UeberBackend B("default");
1125 cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
1126 DNSBackend *db = B.backends[0];
1127 cout<<"Creating slave domain "<<zone<<endl;
1128 db->createSlaveDomain("127.0.0.1", zone, "", "_testschema");
1129 cout<<"Slave domain created"<<endl;
1130
1131 DomainInfo di;
1132 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
1133 cout<<"Can't find domain we just created, aborting"<<endl;
1134 return;
1135 }
1136 db=di.backend;
1137 DNSResourceRecord rr, rrget;
1138 cout<<"Starting transaction to feed records"<<endl;
1139 db->startTransaction(zone, di.id);
1140
1141 rr.qtype=QType::SOA;
1142 rr.qname=zone;
1143 rr.ttl=86400;
1144 rr.domain_id=di.id;
1145 rr.auth=1;
1146 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1147 cout<<"Feeding SOA"<<endl;
1148 db->feedRecord(rr);
1149 rr.qtype=QType::TXT;
1150 // 300 As
1151 rr.content="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
1152 cout<<"Feeding overlong TXT"<<endl;
1153 db->feedRecord(rr);
1154 cout<<"Committing"<<endl;
1155 db->commitTransaction();
1156 cout<<"Querying TXT"<<endl;
1157 db->lookup(QType(QType::TXT), zone, NULL, di.id);
1158 if(db->get(rrget))
1159 {
1160 DNSResourceRecord rrthrowaway;
1161 if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
1162 {
1163 cout<<"Expected one record, got multiple, aborting"<<endl;
1164 return;
1165 }
1166 int size=rrget.content.size();
1167 if(size != 302)
1168 {
1169 cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
1170 return;
1171 }
1172 }
1173 cout<<"[+] content field is over 255 bytes"<<endl;
1174
1175 cout<<"Dropping all records, inserting SOA+2xA"<<endl;
1176 db->startTransaction(zone, di.id);
1177
1178 rr.qtype=QType::SOA;
1179 rr.qname=zone;
1180 rr.ttl=86400;
1181 rr.domain_id=di.id;
1182 rr.auth=1;
1183 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1184 cout<<"Feeding SOA"<<endl;
1185 db->feedRecord(rr);
1186
1187 rr.qtype=QType::A;
1188 rr.qname="_underscore."+zone;
1189 rr.content="127.0.0.1";
1190 db->feedRecord(rr);
1191
1192 rr.qname="bla."+zone;
1193 cout<<"Committing"<<endl;
1194 db->commitTransaction();
1195
1196 cout<<"Securing zone"<<endl;
1197 secureZone(dk, zone);
1198 cout<<"Rectifying zone"<<endl;
1199 rectifyZone(dk, zone);
1200 cout<<"Checking underscore ordering"<<endl;
1201 string before, after;
1202 db->getBeforeAndAfterNames(di.id, zone, "z."+zone, before, after);
1203 cout<<"got '"<<before<<"' < 'z."<<zone<<"' < '"<<after<<"'"<<endl;
1204 if(before != "_underscore."+zone)
1205 {
1206 cout<<"before is wrong, got '"<<before<<"', expected '_underscore."<<zone<<"', aborting"<<endl;
1207 return;
1208 }
1209 if(after != zone)
1210 {
1211 cout<<"after is wrong, got '"<<after<<"', expected '"<<zone<<"', aborting"<<endl;
1212 return;
1213 }
1214 cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
1215 cout<<endl;
1216 cout<<"End of tests, please remove "<<zone<<" from domains+records"<<endl;
1217 }
1218
1219 int main(int argc, char** argv)
1220 try
1221 {
1222 po::options_description desc("Allowed options");
1223 desc.add_options()
1224 ("help,h", "produce help message")
1225 ("verbose,v", "be verbose")
1226 ("force", "force an action")
1227 ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
1228 ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
1229 ("commands", po::value<vector<string> >());
1230
1231 po::positional_options_description p;
1232 p.add("commands", -1);
1233 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
1234 po::notify(g_vm);
1235
1236 vector<string> cmds;
1237
1238 if(g_vm.count("commands"))
1239 cmds = g_vm["commands"].as<vector<string> >();
1240
1241 g_verbose = g_vm.count("verbose");
1242
1243 if(cmds.empty() || g_vm.count("help")) {
1244 cerr<<"Usage: \npdnssec [options] <command> [params ..]\n"<<endl;
1245 cerr<<"Commands:"<<endl;
1246 cerr<<"activate-tsig-key ZONE NAME [master|slave]"<<endl;
1247 cerr<<" Enable TSIG key for a zone"<<endl;
1248 cerr<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE"<<endl;
1249 cerr<<"add-zone-key ZONE zsk|ksk [bits] [active|passive]"<<endl;
1250 cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
1251 cerr<<" Add a ZSK or KSK to zone and specify algo&bits"<<endl;
1252 cerr<<"bench-db [filename] Bench database backend with queries, one domain per line"<<endl;
1253 cerr<<"check-zone ZONE Check a zone for correctness"<<endl;
1254 cerr<<"check-all-zones Check all zones for correctness"<<endl;
1255 cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)"<<endl;
1256 cerr<<"create-zone ZONE Create empty zone ZONE"<<endl;
1257 cerr<<"deactivate-tsig-key ZONE NAME [master|slave]"<<endl;
1258 cerr<<" Disable TSIG key for a zone"<<endl;
1259 cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE"<<endl;
1260 cerr<<"delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)"<<endl;
1261 cerr<<"delete-zone ZONE Delete the zone"<<endl;
1262 cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE"<<endl;
1263 cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described"<<endl;
1264 cerr<<"export-zone-key ZONE KEY-ID Export to stdout the private key described"<<endl;
1265 cerr<<"generate-tsig-key NAME ALGORITHM Generate new TSIG key"<<endl;
1266 cerr<<"generate-zone-key zsk|ksk [algorithm] [bits]"<<endl;
1267 cerr<<" Generate a ZSK or KSK to stdout with specified algo&bits"<<endl;
1268 cerr<<"get-meta ZONE [kind kind ..] Get zone metadata. If no KIND given, lists all known"<<endl;
1269 cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
1270 #ifdef HAVE_P11KIT1
1271 cerr<<"hsm assign zone zsk|ksk module slot pin label"<<endl<<
1272 " Assign a hardware signing module to a ZONE"<<endl;
1273 cerr<<"hsm create-key zone [bits] Create a key using hardware signing module for ZONE (use assign first)"<<endl;
1274 cerr<<" bits defaults to 2048"<<endl;
1275 #endif
1276 cerr<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
1277 cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
1278 cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl;
1279 cerr<<" [active|passive][ksk|zsk] Defaults to KSK and active"<<endl;
1280 cerr<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl;
1281 cerr<<" replacing contents"<<endl;
1282 cerr<<"list-zone ZONE List zone contents"<<endl;
1283 cerr<<"list-all-zones [master|slave|native]"<<endl;
1284 cerr<<" List all zone names"<<endl;;
1285 cerr<<"list-tsig-keys List all TSIG keys"<<endl;
1286 cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)"<<endl;
1287 cerr<<"rectify-all-zones Rectify all zones."<<endl;
1288 cerr<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE"<<endl;
1289 cerr<<"secure-all-zones [increase-serial] Secure all zones without keys."<<endl;
1290 cerr<<"secure-zone ZONE [ZONE ..] Add KSK and two ZSKs"<<endl;
1291 cerr<<"set-nsec3 ZONE ['params' [narrow]] Enable NSEC3 with PARAMs. Optionally narrow"<<endl;
1292 cerr<<"set-presigned ZONE Use presigned RRSIGs from storage"<<endl;
1293 cerr<<"set-meta ZONE KIND [value value ..]"<<endl;
1294 cerr<<" Set zone metadata, optionally providing a value. Empty clears meta."<<endl;
1295 cerr<<"show-zone ZONE Show DNSSEC (public) key details about a zone"<<endl;
1296 cerr<<"unset-nsec3 ZONE Switch back to NSEC"<<endl;
1297 cerr<<"unset-presigned ZONE No longer use presigned RRSIGs"<<endl;
1298 cerr<<"test-schema ZONE Test DB schema - will create ZONE"<<endl;
1299 cerr<<desc<<endl;
1300 return 0;
1301 }
1302
1303 if (cmds[0] == "test-algorithm") {
1304 if(cmds.size() != 2) {
1305 cerr << "Syntax: pdnssec test-algorithm algonum"<<endl;
1306 return 0;
1307 }
1308 testAlgorithm(lexical_cast<int>(cmds[1]));
1309 return 0;
1310 }
1311
1312 if(cmds[0] == "test-algorithms") {
1313 testAlgorithms();
1314 return 0;
1315 }
1316
1317 loadMainConfig(g_vm["config-dir"].as<string>());
1318 reportAllTypes();
1319
1320 if(cmds[0] == "create-bind-db") {
1321 #ifdef HAVE_SQLITE3
1322 if(cmds.size() != 2) {
1323 cerr << "Syntax: pdnssec create-bind-db fname"<<endl;
1324 return 0;
1325 }
1326 try {
1327 SSQLite3 db(cmds[1], true); // create=ok
1328 vector<string> statements;
1329 stringtok(statements, sqlCreate, ";");
1330 BOOST_FOREACH(const string& statement, statements) {
1331 db.execute(statement);
1332 }
1333 }
1334 catch(SSqlException& se) {
1335 throw PDNSException("Error creating database in BIND backend: "+se.txtReason());
1336 }
1337 return 0;
1338 #else
1339 cerr<<"bind-dnssec-db requires building PowerDNS with SQLite3"<<endl;
1340 return 1;
1341 #endif
1342 }
1343
1344 DNSSECKeeper dk;
1345
1346 if (cmds[0] == "test-schema") {
1347 if(cmds.size() != 2) {
1348 cerr << "Syntax: pdnssec test-schema ZONE"<<endl;
1349 return 0;
1350 }
1351 testSchema(dk, cmds[1]);
1352 return 0;
1353 }
1354 if(cmds[0] == "rectify-zone") {
1355 if(cmds.size() < 2) {
1356 cerr << "Syntax: pdnssec rectify-zone ZONE [ZONE..]"<<endl;
1357 return 0;
1358 }
1359 unsigned int exitCode = 0;
1360 for(unsigned int n = 1; n < cmds.size(); ++n)
1361 if (!rectifyZone(dk, cmds[n])) exitCode = 1;
1362 return exitCode;
1363 }
1364 else if (cmds[0] == "rectify-all-zones") {
1365 rectifyAllZones(dk);
1366 }
1367 else if(cmds[0] == "check-zone") {
1368 if(cmds.size() != 2) {
1369 cerr << "Syntax: pdnssec check-zone ZONE"<<endl;
1370 return 0;
1371 }
1372 UeberBackend B("default");
1373 exit(checkZone(dk, B, cmds[1]));
1374 }
1375 else if(cmds[0] == "bench-db") {
1376 dbBench(cmds.size() > 1 ? cmds[1] : "");
1377 }
1378 else if (cmds[0] == "check-all-zones") {
1379 exit(checkAllZones(dk));
1380 }
1381 else if (cmds[0] == "list-all-zones") {
1382 if (cmds.size() > 2) {
1383 cerr << "Syntax: pdnssec list-all-zones [master|slave|native]"<<endl;
1384 return 0;
1385 }
1386 if (cmds.size() == 2)
1387 return listAllZones(cmds[1]);
1388 return listAllZones();
1389 }
1390 else if (cmds[0] == "test-zone") {
1391 cerr << "Did you mean check-zone?"<<endl;
1392 return 0;
1393 }
1394 else if (cmds[0] == "test-all-zones") {
1395 cerr << "Did you mean check-all-zones?"<<endl;
1396 return 0;
1397 }
1398 #if 0
1399 else if(cmds[0] == "signing-server" )
1400 {
1401 signingServer();
1402 }
1403 else if(cmds[0] == "signing-slave")
1404 {
1405 launchSigningService(0);
1406 }
1407 #endif
1408 else if(cmds[0] == "test-speed") {
1409 if(cmds.size() < 2) {
1410 cerr << "Syntax: pdnssec test-speed numcores [signing-server]"<<endl;
1411 return 0;
1412 }
1413 testSpeed(dk, cmds[1], (cmds.size() > 3) ? cmds[3] : "", atoi(cmds[2].c_str()));
1414 }
1415 else if(cmds[0] == "verify-crypto") {
1416 if(cmds.size() != 2) {
1417 cerr << "Syntax: pdnssec verify-crypto FILE"<<endl;
1418 return 0;
1419 }
1420 verifyCrypto(cmds[1]);
1421 }
1422
1423 else if(cmds[0] == "show-zone") {
1424 if(cmds.size() != 2) {
1425 cerr << "Syntax: pdnssec show-zone ZONE"<<endl;
1426 return 0;
1427 }
1428 const string& zone=cmds[1];
1429 if (!showZone(dk, zone)) return 1;
1430 }
1431 else if(cmds[0] == "disable-dnssec") {
1432 if(cmds.size() != 2) {
1433 cerr << "Syntax: pdnssec disable-dnssec ZONE"<<endl;
1434 return 0;
1435 }
1436 const string& zone=cmds[1];
1437 if(!disableDNSSECOnZone(dk, zone)) {
1438 cerr << "Cannot disable DNSSEC on " << zone << endl;
1439 return 1;
1440 }
1441 }
1442 else if(cmds[0] == "activate-zone-key") {
1443 if(cmds.size() != 3) {
1444 cerr << "Syntax: pdnssec activate-zone-key ZONE KEY-ID"<<endl;
1445 return 0;
1446 }
1447 const string& zone=cmds[1];
1448 unsigned int id=atoi(cmds[2].c_str());
1449 if(!id)
1450 {
1451 cerr<<"Invalid KEY-ID"<<endl;
1452 return 1;
1453 }
1454 if (!dk.activateKey(zone, id)) {
1455 cerr<<"Activation of key failed"<<endl;
1456 return 1;
1457 }
1458 return 0;
1459 }
1460 else if(cmds[0] == "deactivate-zone-key") {
1461 if(cmds.size() != 3) {
1462 cerr << "Syntax: pdnssec deactivate-zone-key ZONE KEY-ID"<<endl;
1463 return 0;
1464 }
1465 const string& zone=cmds[1];
1466 unsigned int id=atoi(cmds[2].c_str());
1467 if(!id)
1468 {
1469 cerr<<"Invalid KEY-ID"<<endl;
1470 return 1;
1471 }
1472 if (!dk.deactivateKey(zone, id)) {
1473 cerr<<"Deactivation of key failed"<<endl;
1474 return 1;
1475 }
1476 return 0;
1477 }
1478 else if(cmds[0] == "add-zone-key") {
1479 if(cmds.size() < 3 ) {
1480 cerr << "Syntax: pdnssec add-zone-key ZONE zsk|ksk [bits] [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
1481 return 0;
1482 }
1483 const string& zone=cmds[1];
1484
1485 UeberBackend B("default");
1486 DomainInfo di;
1487
1488 if (!B.getDomainInfo(zone, di)){
1489 cerr << "No such zone in the database" << endl;
1490 return 0;
1491 }
1492
1493 // need to get algorithm, bits & ksk or zsk from commandline
1494 bool keyOrZone=false;
1495 int tmp_algo=0;
1496 int bits=0;
1497 int algorithm=8;
1498 bool active=false;
1499 for(unsigned int n=2; n < cmds.size(); ++n) {
1500 if(pdns_iequals(cmds[n], "zsk"))
1501 keyOrZone = false;
1502 else if(pdns_iequals(cmds[n], "ksk"))
1503 keyOrZone = true;
1504 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1505 algorithm = tmp_algo;
1506 } else if(pdns_iequals(cmds[n], "active")) {
1507 active=true;
1508 } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) {
1509 active=false;
1510 } else if(atoi(cmds[n].c_str())) {
1511 bits = atoi(cmds[n].c_str());
1512 } else {
1513 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
1514 exit(EXIT_FAILURE);;
1515 }
1516 }
1517 if(!dk.addKey(zone, keyOrZone, algorithm, bits, active)) {
1518 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1519 exit(1);
1520 }
1521 else {
1522 cerr<<"Added a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<", active="<<active<<endl;
1523 if(bits)
1524 cerr<<"Requested specific key size of "<<bits<<" bits"<<endl;
1525 }
1526 }
1527 else if(cmds[0] == "remove-zone-key") {
1528 if(cmds.size() < 3) {
1529 cerr<<"Syntax: pdnssec remove-zone-key ZONE KEY-ID"<<endl;
1530 return 0;
1531 }
1532 const string& zone=cmds[1];
1533 unsigned int id=atoi(cmds[2].c_str());
1534 if (!dk.removeKey(zone, id)) {
1535 cerr<<"Cannot remove key " << id << " from " << zone <<endl;
1536 return 1;
1537 }
1538 return 0;
1539 }
1540 else if(cmds[0] == "delete-zone") {
1541 if(cmds.size() != 2) {
1542 cerr<<"Syntax: pdnssec delete-zone ZONE"<<endl;
1543 return 0;
1544 }
1545 exit(deleteZone(cmds[1]));
1546 }
1547 else if(cmds[0] == "create-zone") {
1548 if(cmds.size() != 2) {
1549 cerr<<"Syntax: pdnssec create-zone ZONE"<<endl;
1550 return 0;
1551 }
1552 exit(createZone(cmds[1]));
1553 }
1554 else if(cmds[0] == "list-zone") {
1555 if(cmds.size() != 2) {
1556 cerr<<"Syntax: pdnssec list-zone ZONE"<<endl;
1557 return 0;
1558 }
1559 if(cmds[1]==".")
1560 cmds[1].clear();
1561
1562 exit(listZone(cmds[1]));
1563 }
1564 else if(cmds[0] == "load-zone") {
1565 if(cmds.size() != 3) {
1566 cerr<<"Syntax: pdnssec load-zone ZONE FILENAME"<<endl;
1567 return 0;
1568 }
1569 if(cmds[1]==".")
1570 cmds[1].clear();
1571
1572 exit(loadZone(cmds[1], cmds[2]));
1573 }
1574 else if(cmds[0] == "secure-zone") {
1575 if(cmds.size() < 2) {
1576 cerr << "Syntax: pdnssec secure-zone ZONE"<<endl;
1577 return 0;
1578 }
1579 vector<string> mustRectify;
1580 dk.startTransaction();
1581 unsigned int zoneErrors=0;
1582 for(unsigned int n = 1; n < cmds.size(); ++n) {
1583 const string& zone=cmds[n];
1584 if(secureZone(dk, zone)) {
1585 mustRectify.push_back(zone);
1586 } else {
1587 zoneErrors++;
1588 }
1589 }
1590
1591 dk.commitTransaction();
1592 BOOST_FOREACH(string& zone, mustRectify)
1593 rectifyZone(dk, zone);
1594
1595 if (zoneErrors) {
1596 return 1;
1597 }
1598 return 0;
1599 }
1600 else if (cmds[0] == "secure-all-zones") {
1601 if (cmds.size() >= 2 && !pdns_iequals(cmds[1], "increase-serial")) {
1602 cerr << "Syntax: pdnssec secure-all-zones [increase-serial]"<<endl;
1603 return 0;
1604 }
1605
1606 UeberBackend B("default");
1607
1608 vector<DomainInfo> domainInfo;
1609 B.getAllDomains(&domainInfo);
1610
1611 unsigned int zonesSecured=0, zoneErrors=0;
1612 BOOST_FOREACH(DomainInfo di, domainInfo) {
1613 if(!dk.isSecuredZone(di.zone)) {
1614 cout<<"Securing "<<di.zone<<": ";
1615 if (secureZone(dk, di.zone)) {
1616 zonesSecured++;
1617 if (cmds.size() == 2) {
1618 if (!increaseSerial(di.zone, dk))
1619 continue;
1620 } else
1621 continue;
1622 }
1623 zoneErrors++;
1624 }
1625 }
1626
1627 cout<<"Secured: "<<zonesSecured<<" zones. Errors: "<<zoneErrors<<endl;
1628
1629 if (zoneErrors) {
1630 return 1;
1631 }
1632 return 0;
1633 }
1634 else if(cmds[0]=="set-nsec3") {
1635 if(cmds.size() < 2) {
1636 cerr<<"Syntax: pdnssec set-nsec3 ZONE 'params' [narrow]"<<endl;
1637 return 0;
1638 }
1639 string nsec3params = cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
1640 bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
1641 NSEC3PARAMRecordContent ns3pr(nsec3params);
1642
1643 string zone=cmds[1];
1644 if(!dk.isSecuredZone(zone)) {
1645 cerr<<"Zone '"<<zone<<"' is not secured, can't set NSEC3 parameters"<<endl;
1646 exit(EXIT_FAILURE);
1647 }
1648 dk.setNSEC3PARAM(zone, ns3pr, narrow);
1649
1650 if (!ns3pr.d_flags)
1651 cerr<<"NSEC3 set, please rectify-zone if your backend needs it"<<endl;
1652 else
1653 cerr<<"NSEC3 (opt-out) set, please rectify-zone if your backend needs it"<<endl;
1654 }
1655 else if(cmds[0]=="set-presigned") {
1656 if(cmds.size() < 2) {
1657 cerr<<"Syntax: pdnssec set-presigned ZONE"<<endl;
1658 return 0;
1659 }
1660 if (! dk.setPresigned(cmds[1])) {
1661 cerr << "Could not set presigned on for " << cmds[1] << endl;
1662 return 1;
1663 }
1664 return 0;
1665 }
1666 else if(cmds[0]=="unset-presigned") {
1667 if(cmds.size() < 2) {
1668 cerr<<"Syntax: pdnssec unset-presigned ZONE"<<endl;
1669 return 0;
1670 }
1671 if (! dk.unsetPresigned(cmds[1])) {
1672 cerr << "Could not unset presigned on for " << cmds[1] << endl;
1673 return 1;
1674 }
1675 return 0;
1676 }
1677 else if(cmds[0]=="hash-zone-record") {
1678 if(cmds.size() < 3) {
1679 cerr<<"Syntax: pdnssec hash-zone-record ZONE RNAME"<<endl;
1680 return 0;
1681 }
1682 string& zone=cmds[1];
1683 string& record=cmds[2];
1684 NSEC3PARAMRecordContent ns3pr;
1685 bool narrow;
1686 if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
1687 cerr<<"The '"<<zone<<"' zone does not use NSEC3"<<endl;
1688 return 0;
1689 }
1690 if(narrow) {
1691 cerr<<"The '"<<zone<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
1692 }
1693
1694 cout<<toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, record))<<endl;
1695 }
1696 else if(cmds[0]=="unset-nsec3") {
1697 if(cmds.size() < 2) {
1698 cerr<<"Syntax: pdnssec unset-nsec3 ZONE"<<endl;
1699 return 0;
1700 }
1701 if ( ! dk.unsetNSEC3PARAM(cmds[1])) {
1702 cerr<<"Cannot unset NSEC3 param for " << cmds[1] << endl;
1703 return 1;
1704 }
1705 return 0;
1706 }
1707 else if(cmds[0]=="export-zone-key") {
1708 if(cmds.size() < 3) {
1709 cerr<<"Syntax: pdnssec export-zone-key ZONE KEY-ID"<<endl;
1710 return 0;
1711 }
1712
1713 string zone=cmds[1];
1714 unsigned int id=atoi(cmds[2].c_str());
1715 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1716 cout << dpk.getKey()->convertToISC() <<endl;
1717 }
1718 else if(cmds[0]=="increase-serial") {
1719 if (cmds.size() < 2) {
1720 cerr<<"Syntax: pdnssec increase-serial ZONE"<<endl;
1721 return 0;
1722 }
1723 return increaseSerial(cmds[1], dk);
1724 }
1725 else if(cmds[0]=="import-zone-key-pem") {
1726 if(cmds.size() < 4) {
1727 cerr<<"Syntax: pdnssec import-zone-key-pem ZONE FILE algorithm [ksk|zsk]"<<endl;
1728 exit(1);
1729 }
1730 string zone=cmds[1];
1731 string fname=cmds[2];
1732 string line;
1733 ifstream ifs(fname.c_str());
1734 string tmp, interim, raw;
1735 while(getline(ifs, line)) {
1736 if(line[0]=='-')
1737 continue;
1738 trim(line);
1739 interim += line;
1740 }
1741 B64Decode(interim, raw);
1742 DNSSECPrivateKey dpk;
1743 DNSKEYRecordContent drc;
1744 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromPEMString(drc, raw));
1745 dpk.setKey(key);
1746
1747 dpk.d_algorithm = atoi(cmds[3].c_str());
1748
1749 if(dpk.d_algorithm == 7)
1750 dpk.d_algorithm = 5;
1751
1752 cerr<<(int)dpk.d_algorithm<<endl;
1753
1754 if(cmds.size() > 4) {
1755 if(pdns_iequals(cmds[4], "ZSK"))
1756 dpk.d_flags = 256;
1757 else if(pdns_iequals(cmds[4], "KSK"))
1758 dpk.d_flags = 257;
1759 else {
1760 cerr<<"Unknown key flag '"<<cmds[4]<<"'"<<endl;
1761 exit(1);
1762 }
1763 }
1764 else
1765 dpk.d_flags = 257; // ksk
1766
1767 if(!dk.addKey(zone, dpk)) {
1768 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1769 exit(1);
1770 }
1771
1772 }
1773 else if(cmds[0]=="import-zone-key") {
1774 if(cmds.size() < 3) {
1775 cerr<<"Syntax: pdnssec import-zone-key ZONE FILE [ksk|zsk] [active|passive]"<<endl;
1776 exit(1);
1777 }
1778 string zone=cmds[1];
1779 string fname=cmds[2];
1780 DNSSECPrivateKey dpk;
1781 DNSKEYRecordContent drc;
1782 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromISCFile(drc, fname.c_str()));
1783 dpk.setKey(key);
1784 dpk.d_algorithm = drc.d_algorithm;
1785
1786 if(dpk.d_algorithm == 7)
1787 dpk.d_algorithm = 5;
1788
1789 dpk.d_flags = 257;
1790 bool active=true;
1791
1792 for(unsigned int n = 3; n < cmds.size(); ++n) {
1793 if(pdns_iequals(cmds[n], "ZSK"))
1794 dpk.d_flags = 256;
1795 else if(pdns_iequals(cmds[n], "KSK"))
1796 dpk.d_flags = 257;
1797 else if(pdns_iequals(cmds[n], "active"))
1798 active = 1;
1799 else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive"))
1800 active = 0;
1801 else {
1802 cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
1803 exit(1);
1804 }
1805 }
1806 if(!dk.addKey(zone, dpk, active)) {
1807 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1808 exit(1);
1809 }
1810 }
1811 else if(cmds[0]=="export-zone-dnskey") {
1812 if(cmds.size() < 3) {
1813 cerr<<"Syntax: pdnssec export-zone-dnskey ZONE KEY-ID"<<endl;
1814 exit(1);
1815 }
1816
1817 string zone=cmds[1];
1818 unsigned int id=atoi(cmds[2].c_str());
1819 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1820 cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
1821 if(dpk.d_flags == 257) {
1822 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
1823 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
1824 }
1825 }
1826 else if(cmds[0] == "generate-zone-key") {
1827 if(cmds.size() < 2 ) {
1828 cerr << "Syntax: pdnssec generate-zone-key zsk|ksk [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384] [bits]"<<endl;
1829 return 0;
1830 }
1831 // need to get algorithm, bits & ksk or zsk from commandline
1832 bool keyOrZone=false;
1833 int tmp_algo=0;
1834 int bits=0;
1835 int algorithm=8;
1836 for(unsigned int n=1; n < cmds.size(); ++n) {
1837 if(pdns_iequals(cmds[n], "zsk"))
1838 keyOrZone = false;
1839 else if(pdns_iequals(cmds[n], "ksk"))
1840 keyOrZone = true;
1841 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1842 algorithm = tmp_algo;
1843 } else if(atoi(cmds[n].c_str()))
1844 bits = atoi(cmds[n].c_str());
1845 else {
1846 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
1847 return 0;
1848 }
1849 }
1850 cerr<<"Generating a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
1851 if(bits)
1852 cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
1853
1854 DNSSECPrivateKey dspk;
1855 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
1856 if(!bits) {
1857 if(algorithm <= 10)
1858 bits = keyOrZone ? 2048 : 1024;
1859 else {
1860 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // ECDSA, GOST, ED25519
1861 bits = 256;
1862 else if(algorithm == 14)
1863 bits = 384;
1864 else {
1865 throw runtime_error("Can't guess key size for algorithm "+lexical_cast<string>(algorithm));
1866 }
1867 }
1868 }
1869 dpk->create(bits);
1870 dspk.setKey(dpk);
1871 dspk.d_algorithm = algorithm;
1872 dspk.d_flags = keyOrZone ? 257 : 256;
1873
1874 // print key to stdout
1875 cout << "Flags: " << dspk.d_flags << endl <<
1876 dspk.getKey()->convertToISC() << endl;
1877 } else if (cmds[0]=="generate-tsig-key") {
1878 if (cmds.size() < 3) {
1879 cerr << "Syntax: " << cmds[0] << " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)" << endl;
1880 return 0;
1881 }
1882 string name = cmds[1];
1883 string algo = cmds[2];
1884 string key;
1885 char tmpkey[64];
1886
1887 size_t klen = 0;
1888 if (algo == "hmac-md5") {
1889 klen = 32;
1890 } else if (algo == "hmac-sha1") {
1891 klen = 32;
1892 } else if (algo == "hmac-sha224") {
1893 klen = 32;
1894 } else if (algo == "hmac-sha256") {
1895 klen = 64;
1896 } else if (algo == "hmac-sha384") {
1897 klen = 64;
1898 } else if (algo == "hmac-sha512") {
1899 klen = 64;
1900 } else {
1901 cerr << "Cannot generate key for " << algo << endl;
1902 return 1;
1903 }
1904
1905 cerr << "Generating new key with " << klen << " bytes (this can take a while)" << endl;
1906 seedRandom(::arg()["entropy-source"]);
1907 for(size_t i = 0; i < klen; i+=4) {
1908 *(unsigned int*)(tmpkey+i) = dns_random(0xffffffff);
1909 }
1910 key = Base64Encode(std::string(tmpkey, klen));
1911
1912 UeberBackend B("default");
1913 if (B.setTSIGKey(name, algo, key)) {
1914 cout << "Create new TSIG key " << name << " " << algo << " " << key << endl;
1915 } else {
1916 cout << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
1917 return 1;
1918 }
1919 return 0;
1920 } else if (cmds[0]=="import-tsig-key") {
1921 if (cmds.size() < 4) {
1922 cerr << "Syntax: " << cmds[0] << " name algorithm key" << endl;
1923 return 0;
1924 }
1925 string name = cmds[1];
1926 string algo = cmds[2];
1927 string key = cmds[3];
1928
1929 UeberBackend B("default");
1930 if (B.setTSIGKey(name, algo, key)) {
1931 cout << "Imported TSIG key " << name << " " << algo << endl;
1932 } else {
1933 cout << "Failure importing TSIG key " << name << " " << algo << endl;
1934 return 1;
1935 }
1936 return 0;
1937 } else if (cmds[0]=="delete-tsig-key") {
1938 if (cmds.size() < 2) {
1939 cerr << "Syntax: " << cmds[0] << " name" << endl;
1940 return 0;
1941 }
1942 string name = cmds[1];
1943
1944 UeberBackend B("default");
1945 if (B.deleteTSIGKey(name)) {
1946 cout << "Deleted TSIG key " << name << endl;
1947 } else {
1948 cout << "Failure deleting TSIG key " << name << endl;
1949 return 1;
1950 }
1951 return 0;
1952 } else if (cmds[0]=="list-tsig-keys") {
1953 std::vector<struct TSIGKey> keys;
1954 UeberBackend B("default");
1955 if (B.getTSIGKeys(keys)) {
1956 BOOST_FOREACH(const struct TSIGKey &key, keys) {
1957 cout << key.name << " " << key.algorithm << " " << key.key << endl;
1958 }
1959 }
1960 return 0;
1961 } else if (cmds[0]=="activate-tsig-key") {
1962 string metaKey;
1963 if (cmds.size() < 4) {
1964 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
1965 return 0;
1966 }
1967 string zname = cmds[1];
1968 string name = cmds[2];
1969 if (cmds[3] == "master")
1970 metaKey = "TSIG-ALLOW-AXFR";
1971 else if (cmds[3] == "slave")
1972 metaKey = "AXFR-MASTER-TSIG";
1973 else {
1974 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
1975 return 1;
1976 }
1977 UeberBackend B("default");
1978 std::vector<std::string> meta;
1979 if (!B.getDomainMetadata(zname, metaKey, meta)) {
1980 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
1981 return 1;
1982 }
1983 bool found = false;
1984 BOOST_FOREACH(std::string tmpname, meta) {
1985 if (tmpname == name) { found = true; break; }
1986 }
1987 if (!found) meta.push_back(name);
1988 if (B.setDomainMetadata(zname, metaKey, meta)) {
1989 cout << "Enabled TSIG key " << name << " for " << zname << endl;
1990 } else {
1991 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
1992 return 1;
1993 }
1994 return 0;
1995 } else if (cmds[0]=="deactivate-tsig-key") {
1996 string metaKey;
1997 if (cmds.size() < 4) {
1998 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
1999 return 0;
2000 }
2001 string zname = cmds[1];
2002 string name = cmds[2];
2003 if (cmds[3] == "master")
2004 metaKey = "TSIG-ALLOW-AXFR";
2005 else if (cmds[3] == "slave")
2006 metaKey = "AXFR-MASTER-TSIG";
2007 else {
2008 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
2009 return 1;
2010 }
2011
2012 UeberBackend B("default");
2013 std::vector<std::string> meta;
2014 if (!B.getDomainMetadata(zname, metaKey, meta)) {
2015 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
2016 return 1;
2017 }
2018 std::vector<std::string>::iterator iter = meta.begin();
2019 for(;iter != meta.end(); iter++) if (*iter == name) break;
2020 if (iter != meta.end()) meta.erase(iter);
2021 if (B.setDomainMetadata(zname, metaKey, meta)) {
2022 cout << "Disabled TSIG key " << name << " for " << zname << endl;
2023 } else {
2024 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
2025 return 1;
2026 }
2027 return 0;
2028 } else if (cmds[0]=="get-meta") {
2029 UeberBackend B("default");
2030 if (cmds.size() < 2) {
2031 cerr << "Syntax: " << cmds[0] << " zone [kind kind ..]" << endl;
2032 return 1;
2033 }
2034 string zone = cmds[1];
2035 vector<string> keys;
2036 DomainInfo di;
2037
2038 if (!B.getDomainInfo(zone, di)) {
2039 cerr << "Invalid zone '" << zone << "'" << endl;
2040 return 1;
2041 }
2042
2043 if (cmds.size() > 2) {
2044 keys.assign(cmds.begin() + 2, cmds.end());
2045 std::cout << "Metadata for '" << zone << "'" << endl;
2046 BOOST_FOREACH(const string kind, keys) {
2047 vector<string> meta;
2048 meta.clear();
2049 if (B.getDomainMetadata(zone, kind, meta)) {
2050 cout << kind << " = " << boost::join(meta, ", ") << endl;
2051 }
2052 }
2053 } else {
2054 std::map<std::string, std::vector<std::string> > meta;
2055 std::cout << "Metadata for '" << zone << "'" << endl;
2056 B.getAllDomainMetadata(zone, meta);
2057 for(std::map<std::string, std::vector<std::string> >::const_iterator each_meta = meta.begin(); each_meta != meta.end(); each_meta++) {
2058 cout << each_meta->first << " = " << boost::join(each_meta->second, ", ") << endl;
2059 }
2060 }
2061 return 0;
2062
2063 } else if (cmds[0]=="set-meta") {
2064 UeberBackend B("default");
2065 if (cmds.size() < 3) {
2066 cerr << "Syntax: " << cmds[0] << " zone kind [value value ..]" << endl;
2067 return 1;
2068 }
2069 string zone = cmds[1];
2070 string kind = cmds[2];
2071 vector<string> meta(cmds.begin() + 3, cmds.end());
2072
2073 if (!B.setDomainMetadata(zone, kind, meta)) {
2074 cerr << "Unable to set meta for '" << zone << "'" << endl;
2075 return 1;
2076 } else {
2077 cout << "Set '" << zone << "' meta " << kind << " = " << boost::join(meta, ", ") << endl;
2078 }
2079 } else if (cmds[0]=="hsm") {
2080 #ifdef HAVE_P11KIT1
2081 UeberBackend B("default");
2082 if (cmds[1] == "assign") {
2083 DNSCryptoKeyEngine::storvector_t storvect;
2084 DomainInfo di;
2085
2086 if (cmds.size() < 9) {
2087 std::cout << "Usage: pdnssec hsm assign zone algorithm ksk|zsk module slot pin label" << std::endl;
2088 return 1;
2089 }
2090
2091 string zone = cmds[2];
2092
2093 // verify zone
2094 if (!B.getDomainInfo(zone, di)) {
2095 cerr << "Unable to assign module to unknown zone '" << zone << "'" << std::endl;
2096 return 1;
2097 }
2098
2099 int algorithm = shorthand2algorithm(cmds[3]);
2100 int id;
2101 bool keyOrZone = (cmds[4] == "ksk" ? true : false);
2102 string module = cmds[5];
2103 string slot = cmds[6];
2104 string pin = cmds[7];
2105 string label = cmds[8];
2106
2107 std::ostringstream iscString;
2108 iscString << "Private-key-format: v1.2" << std::endl <<
2109 "Algorithm: " << algorithm << std::endl <<
2110 "Engine: " << module << std::endl <<
2111 "Slot: " << slot << std::endl <<
2112 "PIN: " << pin << std::endl <<
2113 "Label: " << label << std::endl;
2114
2115 DNSKEYRecordContent drc;
2116 DNSSECPrivateKey dpk;
2117 dpk.d_flags = (keyOrZone ? 257 : 256);
2118 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str())));
2119
2120 if (!(id = dk.addKey(zone, dpk))) {
2121 cerr << "Unable to assign module slot to zone" << std::endl;
2122 return 1;
2123 }
2124
2125 cerr << "Module " << module << " slot " << slot << " assigned to " << zone << " with key id " << id << endl;
2126 return 0;
2127 } else if (cmds[1] == "create-key") {
2128
2129 if (cmds.size() < 4) {
2130 cerr << "Usage: pdnssec hsm create-key zone key-id [bits]" << endl;
2131 return 1;
2132 }
2133 DomainInfo di;
2134 string zone = cmds[2];
2135 unsigned int id;
2136 int bits = 2048;
2137 // verify zone
2138 if (!B.getDomainInfo(zone, di)) {
2139 cerr << "Unable to create key for unknown zone '" << zone << "'" << std::endl;
2140 return 1;
2141 }
2142
2143 id = boost::lexical_cast<unsigned int>(cmds[3]);
2144 std::vector<DNSBackend::KeyData> keys;
2145 if (!B.getDomainKeys(zone, 0, keys)) {
2146 cerr << "No keys found for zone " << zone << std::endl;
2147 return 1;
2148 }
2149
2150 DNSCryptoKeyEngine *dke = NULL;
2151 // lookup correct key
2152 BOOST_FOREACH(DNSBackend::KeyData &kd, keys) {
2153 if (kd.id == id) {
2154 // found our key.
2155 DNSKEYRecordContent dkrc;
2156 dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content);
2157 }
2158 }
2159
2160 if (!dke) {
2161 cerr << "Could not find key with ID " << id << endl;
2162 return 1;
2163 }
2164 if (cmds.size() > 4) {
2165 bits = boost::lexical_cast<int>(cmds[4]);
2166 }
2167 if (bits < 1) {
2168 cerr << "Invalid bit size " << bits << "given, must be positive integer";
2169 return 1;
2170 }
2171 try {
2172 dke->create(bits);
2173 } catch (PDNSException& e) {
2174 cerr << e.reason << endl;
2175 return 1;
2176 }
2177
2178 cerr << "Key of size " << bits << " created" << std::endl;
2179 return 0;
2180 }
2181 #else
2182 cerr<<"PKCS#11 support not enabled"<<endl;
2183 return 1;
2184 #endif
2185 } else {
2186 cerr<<"Unknown command '"<<cmds[0] <<"'"<< endl;
2187 return 1;
2188 }
2189 return 0;
2190 }
2191 catch(PDNSException& ae) {
2192 cerr<<"Error: "<<ae.reason<<endl;
2193 return 1;
2194 }
2195 catch(std::exception& e) {
2196 cerr<<"Error: "<<e.what()<<endl;
2197 return 1;
2198 }
2199