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