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