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