]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pdnssec.cc
detect duplicate cnames with pdnssec check-all-zones (Issue #545)
[thirdparty/pdns.git] / pdns / pdnssec.cc
CommitLineData
1d211b1b 1#include "dnsseckeeper.hh"
d3151289 2#include "dnssecinfra.hh"
1d211b1b 3#include "statbag.hh"
01fde57c 4#include "base32.hh"
ed3f8559 5#include "base64.hh"
1d211b1b
BH
6#include <boost/foreach.hpp>
7#include <boost/program_options.hpp>
20002664
BH
8#include "dnsbackend.hh"
9#include "ueberbackend.hh"
10#include "arguments.hh"
11#include "packetcache.hh"
aa65a832 12#include "zoneparser-tng.hh"
ea937fd4 13#include "signingpipe.hh"
c3221d6e 14#include <boost/scoped_ptr.hpp>
2717b8b3 15#include "bindbackend2.hh"
a56bc64d 16#include "dns_random.hh"
49449751 17
20002664
BH
18StatBag S;
19PacketCache PC;
1d211b1b 20
de43ec0f 21using boost::scoped_ptr;
1d211b1b
BH
22namespace po = boost::program_options;
23po::variables_map g_vm;
24
39a8b5c0 25string s_programname="pdns";
20002664 26
b3ce3dec
BH
27namespace {
28 bool g_verbose; // doesn't yet do anything though
49449751
BH
29}
30
20002664
BH
31ArgvMap &arg()
32{
33 static ArgvMap arg;
34 return arg;
35}
36
1d211b1b
BH
37string humanTime(time_t t)
38{
39 char ret[256];
40 struct tm tm;
41 localtime_r(&t, &tm);
42 strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d
43 return ret;
44}
45
e0ad7bb1
PD
46static void algorithm2name(uint8_t algo, string &name) {
47 switch(algo) {
48 case 0:
49 name = "Reserved"; return;
50 case 1:
51 name = "RSAMD5"; return;
52 case 2:
53 name = "DH"; return;
54 case 3:
55 name = "DSA"; return;
56 case 4:
57 name = "ECC"; return;
58 case 5:
59 name = "RSASHA1"; return;
60 case 6:
61 name = "DSA-NSEC3-SHA1"; return;
62 case 7:
63 name = "RSASHA1-NSEC3-SHA1"; return;
64 case 8:
65 name = "RSASHA256"; return;
66 case 9:
67 name = "Reserved"; return;
68 case 10:
69 name = "RSASHA512"; return;
70 case 11:
71 name = "Reserved"; return;
72 case 12:
73 name = "ECC-GOST"; return;
74 case 13:
75 name = "ECDSAP256SHA256"; return;
76 case 14:
77 name = "ECDSAP384SHA384"; return;
78 case 252:
79 name = "INDIRECT"; return;
80 case 253:
81 name = "PRIVATEDNS"; return;
82 case 254:
83 name = "PRIVATEOID"; return;
84 default:
85 name = "Unallocated/Reserved"; return;
86 }
87};
88
36758d25
PD
89static int shorthand2algorithm(const string &algorithm)
90{
91 if (!algorithm.compare("rsamd5")) return 1;
92 if (!algorithm.compare("dh")) return 2;
93 if (!algorithm.compare("dsa")) return 3;
94 if (!algorithm.compare("ecc")) return 4;
95 if (!algorithm.compare("rsasha1")) return 5;
96 if (!algorithm.compare("rsasha256")) return 8;
97 if (!algorithm.compare("rsasha512")) return 10;
98 if (!algorithm.compare("gost")) return 12;
99 if (!algorithm.compare("ecdsa256")) return 13;
100 if (!algorithm.compare("ecdsa384")) return 14;
101 if (!algorithm.compare("ed25519")) return 250;
102 return -1;
103}
104
7d9dcde0 105void loadMainConfig(const std::string& configdir)
20002664 106{
7d9dcde0 107 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
c6347f61 108 ::arg().set("pipebackend-abi-version","Version of the pipe backend ABI")="1";
f2e7d77b 109 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
20002664 110 ::arg().set("launch","Which backends to launch");
6dfa0aa0 111 ::arg().set("dnssec","if we should do dnssec")="true";
4f6cf113 112 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
20002664
BH
113 ::arg().setCmd("help","Provide a helpful message");
114 //::arg().laxParse(argc,argv);
115
116 if(::arg().mustDo("help")) {
117 cerr<<"syntax:"<<endl<<endl;
118 cerr<<::arg().helpstring(::arg()["help"])<<endl;
119 exit(99);
120 }
121
122 if(::arg()["config-name"]!="")
123 s_programname+="-"+::arg()["config-name"];
124
125 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
126 cleanSlashes(configname);
36758d25
PD
127
128 ::arg().set("default-ksk-algorithms","Default KSK algorithms")="rsasha256";
129 ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
130 ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="rsasha256";
131 ::arg().set("default-zsk-size","Default KSK size (0 means default)")="0";
b5baefaf 132 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
a7e0acd8 133 ::arg().set("module-dir","Default directory for modules")=LIBDIR;
a56bc64d
AT
134 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
135
9097239c 136 ::arg().setSwitch("experimental-direct-dnskey","EXPERIMENTAL: fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
966828ac 137 ::arg().laxFile(configname.c_str());
12a92688 138
20002664
BH
139 BackendMakers().launch(::arg()["launch"]); // vrooooom!
140 ::arg().laxFile(configname.c_str());
9abd98d3 141 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
20002664
BH
142
143 S.declare("qsize-q","Number of questions waiting for database attention");
144
145 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
146 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
147
148 S.declare("query-cache-hit","Number of hits on the query cache");
149 S.declare("query-cache-miss","Number of misses on the query cache");
150 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
151 ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no";
ec7f535c 152 ::arg().set("recursive-cache-ttl","Seconds to store packets for recursive queries in the PacketCache")="10";
20002664 153 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
ec7f535c
PD
154 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
155 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
da6a2926 156 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
20002664
BH
157 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
158 ::arg().set("soa-retry-default","Default SOA retry")="3600";
159 ::arg().set("soa-expire-default","Default SOA expire")="604800";
ec7f535c 160 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
abc1d928 161 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
12a92688 162
20002664
BH
163 UeberBackend::go();
164}
165
d6c16697 166// irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
d4904322 167// I think this has to do with interlocking transactions between B and DK, but unsure.
032e3906 168bool rectifyZone(DNSSECKeeper& dk, const std::string& zone)
20002664 169{
9bd211e1
KM
170 if(dk.isPresigned(zone)){
171 cerr<<"Rectify presigned zone '"<<zone<<"' is not allowed/necessary."<<endl;
172 return false;
173 }
174
ba86110a 175 UeberBackend B("default");
d6c16697 176 bool doTransaction=true; // but see above
20002664 177 SOAData sd;
1325e8a2 178 sd.db = (DNSBackend*)-1;
81b39e4b 179
ba86110a 180 if(!B.getSOA(zone, sd)) {
f28f2324 181 cerr<<"No SOA known for '"<<zone<<"', is such a zone in the database?"<<endl;
032e3906 182 return false;
20002664 183 }
81b39e4b 184 sd.db->list(zone, sd.domain_id);
81b39e4b 185
b5baefaf
PD
186 DNSResourceRecord rr;
187 set<string> qnames, nsset, dsnames, nonterm, insnonterm, delnonterm;
188 bool doent=true;
20002664
BH
189
190 while(sd.db->get(rr)) {
b5baefaf
PD
191 if (rr.qtype.getCode())
192 {
193 qnames.insert(rr.qname);
194 if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, zone))
195 nsset.insert(rr.qname);
196 if(rr.qtype.getCode() == QType::DS)
197 dsnames.insert(rr.qname);
198 }
199 else
200 if(doent)
201 delnonterm.insert(rr.qname);
81b39e4b 202 }
9abd98d3 203
c3c89361 204 NSEC3PARAMRecordContent ns3pr;
f28f2324 205 bool narrow;
3c873e66 206 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
ece45ffb
PD
207 if(sd.db->doesDNSSEC())
208 {
209 if(!haveNSEC3)
210 cerr<<"Adding NSEC ordering information "<<endl;
211 else if(!narrow)
212 cerr<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'"<<endl;
213 else
214 cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
215 }
216 else
217 cerr<<"Non DNSSEC zone, only adding empty non-terminals"<<endl;
9abd98d3 218
d6c16697
BH
219 if(doTransaction)
220 sd.db->startTransaction("", -1);
b5baefaf
PD
221
222 bool realrr=true;
223 string hashed;
224
225 uint32_t maxent = ::arg().asNum("max-ent-entries");
226
227 dononterm:;
81b39e4b
BH
228 BOOST_FOREACH(const string& qname, qnames)
229 {
f28f2324 230 bool auth=true;
b5baefaf 231 string shorter(qname);
27045410 232
b5baefaf
PD
233 if(realrr) {
234 do {
235 if(nsset.count(shorter)) {
236 auth=false;
237 break;
238 }
239 } while(chopOff(shorter));
b5baefaf 240 }
27045410
PD
241
242 if(haveNSEC3)
243 {
f28f2324 244 if(!narrow) {
1bad4190 245 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname));
4bf664d8
PD
246 if(g_verbose)
247 cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
c2df797e 248 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
f28f2324 249 }
c2df797e
PD
250 else
251 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
27045410
PD
252 }
253 else // NSEC
254 {
b8adb30d
KM
255 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, auth);
256 if (!realrr)
c2df797e 257 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
b5baefaf
PD
258 }
259
b8adb30d 260 if(realrr)
b5baefaf 261 {
b8adb30d
KM
262 if (dsnames.count(qname))
263 sd.db->setDNSSECAuthOnDsRecord(sd.domain_id, qname);
264 if (!auth || nsset.count(qname)) {
265 if(haveNSEC3 && ns3pr.d_flags)
266 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "NS");
267 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "A");
268 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "AAAA");
269 }
270
271 if(auth && doent)
b5baefaf 272 {
b8adb30d
KM
273 shorter=qname;
274 while(!pdns_iequals(shorter, zone) && chopOff(shorter))
b5baefaf 275 {
b8adb30d 276 if(!qnames.count(shorter) && !nonterm.count(shorter))
b5baefaf 277 {
b8adb30d
KM
278 if(!(maxent))
279 {
280 cerr<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
281 insnonterm.clear();
282 delnonterm.clear();
283 doent=false;
284 break;
285 }
286 nonterm.insert(shorter);
287 if (!delnonterm.count(shorter))
288 insnonterm.insert(shorter);
289 else
290 delnonterm.erase(shorter);
291 --maxent;
b5baefaf 292 }
b5baefaf 293 }
27045410 294 }
c3c89361 295 }
20002664 296 }
b5baefaf
PD
297
298 if(realrr)
299 {
300 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
301 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
302 {
303 sd.db->updateEmptyNonTerminals(sd.domain_id, zone, insnonterm, delnonterm, !doent);
304 }
305 if(doent)
306 {
307 realrr=false;
308 qnames=nonterm;
309 goto dononterm;
310 }
311 }
312
d6c16697
BH
313 if(doTransaction)
314 sd.db->commitTransaction();
032e3906
PD
315
316 return true;
20002664
BH
317}
318
1325e8a2
PD
319void rectifyAllZones(DNSSECKeeper &dk)
320{
ba86110a 321 UeberBackend B("default");
1325e8a2
PD
322 vector<DomainInfo> domainInfo;
323
ba86110a 324 B.getAllDomains(&domainInfo);
1325e8a2
PD
325 BOOST_FOREACH(DomainInfo di, domainInfo) {
326 cerr<<"Rectifying "<<di.zone<<": ";
327 rectifyZone(dk, di.zone);
328 }
329 cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
330}
331
ba86110a 332int checkZone(DNSSECKeeper &dk, UeberBackend &B, const std::string& zone)
5d2e58b0 333{
5d2e58b0 334 SOAData sd;
1325e8a2 335 sd.db=(DNSBackend*)-1;
ba86110a 336 if(!B.getSOA(zone, sd)) {
0ae6a6cc
BH
337 cout<<"No SOA for zone '"<<zone<<"'"<<endl;
338 return -1;
5d2e58b0 339 }
5d2e58b0
BH
340 sd.db->list(zone, sd.domain_id);
341 DNSResourceRecord rr;
8c949c52 342 uint64_t numrecords=0, numerrors=0, numwarnings=0;
5d2e58b0 343
61717a51
PD
344 set<string> cnames, noncnames;
345
035297ad 346 while(sd.db->get(rr)) {
b191a835
PD
347 if(!endsOn(rr.qname, zone)) {
348 cout<<"[Warning] The record "<<rr.qname<<" with type "<<rr.qtype.getName()<<" in zone "<<zone<<" is out-of-zone."<<endl;
349 numwarnings++;
350 continue;
351 }
352
0c13544c
PD
353 if(!rr.qtype.getCode())
354 continue;
ca57aa98 355
61717a51 356 if (rr.qtype.getCode() == QType::CNAME) {
ca57aa98
KM
357 if (!cnames.count(rr.qname))
358 cnames.insert(rr.qname);
359 else
360 {
361 cout<<"[Error] Duplicate CNAME found at '"<<rr.qname<<"'. These do not belong in the database."<<endl;
362 numerrors++;
363 continue;
364 }
61717a51
PD
365 }
366 else {
367 if (rr.qtype.getCode() != QType::RRSIG)
368 noncnames.insert(rr.qname);
369 }
370
0aabca97
PD
371 if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
372 {
373 cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname<<"'. These do not belong in the database."<<endl;
374 numerrors++;
375 continue;
376 }
377
12a92688
PD
378 if(rr.qtype.getCode() == QType::DNSKEY)
379 {
380 if(!dk.isPresigned(zone))
381 {
9097239c 382 if(::arg().mustDo("experimental-direct-dnskey"))
12a92688
PD
383 {
384 if(rr.ttl != sd.default_ttl)
385 {
386 cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
387 numwarnings++;
388 }
389 }
390 else
391 {
392 cout<<"[Error] DNSKEY in non-presigned zone will mostly be ignored and can cause problems."<<endl;
393 numerrors++;
394 }
395 }
396 }
397
0c13544c
PD
398 if(rr.qtype.getCode() == QType::SOA)
399 {
400 fillSOAData(rr.content, sd);
401 rr.content = serializeSOAData(sd);
402 }
403
01d2e77a 404 if(rr.qtype.getCode() == QType::URL || rr.qtype.getCode() == QType::MBOXFW) {
8c949c52 405 cout<<"[Error] The recordtype "<<rr.qtype.getName()<<" for record '"<<rr.qname<<"' is no longer supported."<<endl;
01d2e77a 406 numerrors++;
0ae6a6cc 407 continue;
01d2e77a
PD
408 }
409
8c949c52
PD
410 if (rr.qname[rr.qname.size()-1] == '.') {
411 cout<<"[Error] Record '"<<rr.qname<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
412 numerrors++;
413 }
8c949c52 414
0ae6a6cc 415 if(rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
035297ad 416 rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
01d2e77a 417
ef1c4bf0 418 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) &&
8c949c52
PD
419 rr.content[rr.content.size()-1] == '.') {
420 cout<<"[Warning] The record "<<rr.qname<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
421 numwarnings++;
422 }
423
5e42374c
BH
424 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
425 rr.content = "\""+rr.content+"\"";
426
27045410 427 if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
7ddd79a7 428 {
8c949c52 429 cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
5e42374c 430 numerrors++;
7ddd79a7 431 }
035297ad
BH
432 try {
433 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
434 string tmp=drc->serialize(rr.qname);
5d2e58b0 435 }
035297ad
BH
436 catch(std::exception& e)
437 {
8c949c52
PD
438 cout<<"[Error] Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
439 cout<<"[Error] Error was: "<<e.what()<<endl;
035297ad
BH
440 numerrors++;
441 }
442 numrecords++;
443 }
61717a51
PD
444
445 for(set<string>::const_iterator i = cnames.begin(); i != cnames.end(); i++) {
446 if (noncnames.find(*i) != noncnames.end()) {
447 cout<<"[Error] CNAME "<<*i<<" found, but other records with same label exist."<<endl;
448 numerrors++;
449 }
450 }
451
452
453
8c949c52 454 cout<<"Checked "<<numrecords<<" records of '"<<zone<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
0ae6a6cc 455 return numerrors;
5d2e58b0
BH
456}
457
12a92688 458int checkAllZones(DNSSECKeeper &dk)
1325e8a2 459{
ba86110a 460 UeberBackend B("default");
1325e8a2
PD
461 vector<DomainInfo> domainInfo;
462
ba86110a 463 B.getAllDomains(&domainInfo);
1325e8a2
PD
464 int errors=0;
465 BOOST_FOREACH(DomainInfo di, domainInfo) {
ba86110a 466 if (checkZone(dk, B, di.zone) > 0)
1325e8a2 467 errors++;
1325e8a2
PD
468 }
469 cout<<"Checked "<<domainInfo.size()<<" zones, "<<errors<<" had errors."<<endl;
470 return 0;
471}
472
04576eed
RA
473int increaseSerial(const string& zone, DNSSECKeeper &dk)
474{
475 UeberBackend B("default");
476 SOAData sd;
477 sd.db=(DNSBackend*)-1;
478 if(!B.getSOA(zone, sd)) {
479 cout<<"No SOA for zone '"<<zone<<"'"<<endl;
480 return -1;
481 }
482
483 string soaEditKind;
484 dk.getFromMeta(zone, "SOA-EDIT", soaEditKind);
485
486 sd.db->lookup(QType(QType::SOA), zone);
487 vector<DNSResourceRecord> rrs;
488 DNSResourceRecord rr;
489 while (sd.db->get(rr)) {
490 if (rr.qtype.getCode() == QType::SOA)
491 rrs.push_back(rr);
492 }
493
494 if (rrs.size() > 1) {
495 cerr<<rrs.size()<<" SOA records found for "<<zone<<"!"<<endl;
496 return -1;
497 }
498 if (rrs.size() < 1) {
499 cerr<<zone<<" not found!"<<endl;
500 }
501
63347c6c 502 if (soaEditKind.empty()) {
04576eed 503 sd.serial++;
63347c6c
SB
504 }
505 else if(pdns_iequals(soaEditKind,"INCREMENT-WEEKS")) {
506 sd.serial++;
507 }
508 else if(pdns_iequals(soaEditKind,"INCEPTION-INCREMENT")) {
509 uint32_t today_serial = localtime_format_YYYYMMDDSS(time(NULL), 1);
510
511 if (sd.serial < today_serial) {
512 sd.serial = today_serial;
513 }
514 else {
515 sd.serial++;
516 }
517 }
518 else {
9f44333a 519 sd.serial = calculateEditSOA(sd, soaEditKind) + 1;
63347c6c 520 }
04576eed
RA
521 rrs[0].content = serializeSOAData(sd);
522
523 if (! sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, rrs)) {
524 cerr<<"Backend did not replace SOA record. Backend might not support this operation."<<endl;
525 return -1;
526 }
527 cout<<"SOA serial for zone "<<zone<<" set to "<<sd.serial<<endl;
528 return 0;
529}
530
cbb0025b
PD
531void testAlgorithm(int algo)
532{
533 DNSCryptoKeyEngine::testOne(algo);
534}
1325e8a2 535
189bb9d2
BH
536void testAlgorithms()
537{
538 DNSCryptoKeyEngine::testAll();
539}
540
49449751 541void testSpeed(DNSSECKeeper& dk, const string& zone, const string& remote, int cores)
ea937fd4
BH
542{
543 DNSResourceRecord rr;
544 rr.qname="blah."+zone;
545 rr.qtype=QType::A;
546 rr.ttl=3600;
547 rr.auth=1;
548 rr.qclass = 1;
549 rr.d_place=DNSResourceRecord::ANSWER;
550 rr.priority=0;
551
49449751 552 UeberBackend db("key-only");
ea937fd4 553
f0c4b9d5
PD
554 if ( ! db.backends.size() )
555 {
556 throw runtime_error("No backends available for DNSSEC key storage");
557 }
558
49449751 559 ChunkedSigningPipe csp(zone, 1, remote, cores);
ea937fd4
BH
560
561 vector<DNSResourceRecord> signatures;
562 uint32_t rnd;
563 unsigned char* octets = (unsigned char*)&rnd;
564 char tmp[25];
565 DTime dt;
566 dt.set();
567 for(unsigned int n=0; n < 100000; ++n) {
568 rnd = random();
569 snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
570 octets[0], octets[1], octets[2], octets[3]);
571 rr.content=tmp;
572
573 snprintf(tmp, sizeof(tmp), "r-%u", rnd);
574 rr.qname=string(tmp)+"."+zone;
575
576 if(csp.submit(rr))
577 while(signatures = csp.getChunk(), !signatures.empty())
578 ;
579 }
580 cerr<<"Flushing the pipe, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
581 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiffNoReset()/1000000.0) << " sigs/s\n";
582 while(signatures = csp.getChunk(true), !signatures.empty())
583 ;
584 cerr<<"Done, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
585 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s\n";
586}
587
aa65a832
BH
588void verifyCrypto(const string& zone)
589{
590 ZoneParserTNG zpt(zone);
591 DNSResourceRecord rr;
592 DNSKEYRecordContent drc;
593 RRSIGRecordContent rrc;
c3e26094 594 DSRecordContent dsrc;
aa65a832 595 vector<shared_ptr<DNSRecordContent> > toSign;
c3e26094
BH
596 string qname, apex;
597 dsrc.d_digesttype=0;
aa65a832
BH
598 while(zpt.get(rr)) {
599 if(rr.qtype.getCode() == QType::DNSKEY) {
600 cerr<<"got DNSKEY!"<<endl;
c3e26094 601 apex=rr.qname;
aa65a832
BH
602 drc = *dynamic_cast<DNSKEYRecordContent*>(DNSRecordContent::mastermake(QType::DNSKEY, 1, rr.content));
603 }
604 else if(rr.qtype.getCode() == QType::RRSIG) {
605 cerr<<"got RRSIG"<<endl;
606 rrc = *dynamic_cast<RRSIGRecordContent*>(DNSRecordContent::mastermake(QType::RRSIG, 1, rr.content));
607 }
c3e26094
BH
608 else if(rr.qtype.getCode() == QType::DS) {
609 cerr<<"got DS"<<endl;
610 dsrc = *dynamic_cast<DSRecordContent*>(DNSRecordContent::mastermake(QType::DS, 1, rr.content));
611 }
aa65a832
BH
612 else {
613 qname = rr.qname;
aa65a832
BH
614 toSign.push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
615 }
616 }
c3e26094 617
f309dacd 618 string msg = getMessageForRRSET(qname, rrc, toSign);
49449751 619 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
c3e26094
BH
620 if(dsrc.d_digesttype) {
621 cerr<<"Calculated DS: "<<apex<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
622 cerr<<"Original DS: "<<apex<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
623 }
295812a1 624#if 0
8d9f38f2 625 DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
295812a1
BH
626 "Algorithm: 12 (ECC-GOST)\n"
627 "GostAsn1: MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQg/9MiXtXKg9FDXDN/R9CmVhJDyuzRAIgh4tPwCu4NHIs=\n");
628 string resign=key->sign(hash);
629 cerr<<Base64Encode(resign)<<endl;
8d9f38f2 630 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(hash, resign)<<endl;
295812a1
BH
631#endif
632
aa65a832 633}
032e3906 634bool disableDNSSECOnZone(DNSSECKeeper& dk, const string& zone)
5935cede 635{
032e3906
PD
636 UeberBackend B("default");
637 DomainInfo di;
638
639 if (!B.getDomainInfo(zone, di)){
640 cerr << "No such zone in the database" << endl;
641 return false;
642 }
643
5935cede
BH
644 if(!dk.isSecuredZone(zone)) {
645 cerr<<"Zone is not secured\n";
032e3906 646 return false;
5935cede
BH
647 }
648 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
aa65a832 649
5935cede
BH
650 if(keyset.empty()) {
651 cerr << "No keys for zone '"<<zone<<"'."<<endl;
652 }
653 else {
654 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
655 dk.deactivateKey(zone, value.second.id);
37fd8771 656 dk.removeKey(zone, value.second.id);
5935cede
BH
657 }
658 }
659 dk.unsetNSEC3PARAM(zone);
660 dk.unsetPresigned(zone);
032e3906 661 return true;
5935cede 662}
032e3906 663bool showZone(DNSSECKeeper& dk, const std::string& zone)
ade1b1e9 664{
032e3906
PD
665 UeberBackend B("default");
666 DomainInfo di;
4831df57 667 std::vector<std::string> meta;
032e3906
PD
668
669 if (!B.getDomainInfo(zone, di)){
670 cerr << "No such zone in the database" << endl;
671 return false;
672 }
673
d3e7090c 674 if(!dk.isSecuredZone(zone)) {
d4a4176d 675 cerr<<"Zone is not actively secured\n";
d3e7090c 676 }
ade1b1e9
BH
677 NSEC3PARAMRecordContent ns3pr;
678 bool narrow;
3c873e66 679 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
ade1b1e9 680
d4a4176d 681 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
4831df57
AT
682 if (B.getDomainMetadata(zone, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
683 cerr << "Zone has following allowed TSIG key(s): " << boost::join(meta, ",") << endl;
684 }
685
a56bc64d 686 meta.clear();
4831df57
AT
687 if (B.getDomainMetadata(zone, "AXFR-MASTER-TSIG", meta) && meta.size() > 0) {
688 cerr << "Zone uses following TSIG key(s): " << boost::join(meta, ",") << endl;
689 }
d3e7090c 690
d4a4176d 691 cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned\n";
ade1b1e9
BH
692
693 if(keyset.empty()) {
694 cerr << "No keys for zone '"<<zone<<"'."<<endl;
695 }
696 else {
d4a4176d
BH
697 if(!haveNSEC3)
698 cout<<"Zone has NSEC semantics"<<endl;
699 else
700 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
701
ade1b1e9
BH
702 cout << "keys: "<<endl;
703 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
e0ad7bb1
PD
704 string algname;
705 algorithm2name(value.first.d_algorithm, algname);
ade1b1e9 706 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
ca2eb011
KM
707 cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< " ( " + algname + " ) "<<endl;
708 if(value.second.keyOrZone || ::arg().mustDo("experimental-direct-dnskey"))
709 cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
ade1b1e9 710 if(value.second.keyOrZone) {
e0ad7bb1
PD
711 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
712 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
608d776f
BH
713 try {
714 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
e0ad7bb1
PD
715 cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
716 }
717 catch(...)
718 {
719 }
720 try {
721 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 4).getZoneRepresentation();
722 cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
608d776f
BH
723 }
724 catch(...)
725 {
726 }
727 cout<<endl;
ade1b1e9
BH
728 }
729 }
730 }
032e3906 731 return true;
ade1b1e9 732}
5d2e58b0 733
d6c16697 734bool secureZone(DNSSECKeeper& dk, const std::string& zone)
ddac7145 735{
36758d25
PD
736 // parse attribute
737 vector<string> k_algos;
738 vector<string> z_algos;
739 int k_size;
740 int z_size;
741
742 stringtok(k_algos, ::arg()["default-ksk-algorithms"], " ,");
743 k_size = ::arg().asNum("default-ksk-size");
744 stringtok(z_algos, ::arg()["default-zsk-algorithms"], " ,");
745 z_size = ::arg().asNum("default-zsk-size");
746
747 if (k_size < 0) {
748 throw runtime_error("KSK key size must be equal to or greater than 0");
749 }
750
751 if (k_algos.size() < 1) {
752 throw runtime_error("No algorithm(s) given for KSK");
753 }
754
755 if (z_size < 0) {
756 throw runtime_error("ZSK key size must be equal to or greater than 0");
757 }
758
759 if (z_algos.size() < 1) {
760 throw runtime_error("No algorithm(s) given for ZSK");
761 }
762
f60f4bcd
BH
763 if(dk.isSecuredZone(zone)) {
764 cerr << "Zone '"<<zone<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
765 return false;
766 }
767
2402c558
BH
768 DomainInfo di;
769 UeberBackend B("default");
770 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
771 cout<<"Can't find a zone called '"<<zone<<"'"<<endl;
772 return false;
773 }
774
3bf07122
PD
775 if(di.kind == DomainInfo::Slave)
776 {
777 cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
778 cout<<"pdnssec disable-dnssec "<<zone<<" right now!"<<endl;
779 }
780
36758d25
PD
781 if (k_size)
782 cout << "Securing zone with " << k_algos[0] << " algorithm with key size " << k_size << endl;
783 else
784 cout << "Securing zone with " << k_algos[0] << " algorithm with default key size" << endl;
785
786 // run secure-zone with first default algorith, then add keys
787 if(!dk.secureZone(zone, shorthand2algorithm(k_algos[0]), k_size)) {
f60f4bcd
BH
788 cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC\n";
789 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled.\n";
21fb9631
BH
790 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or\n";
791 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!\n";
f60f4bcd
BH
792 return false;
793 }
794
795 if(!dk.isSecuredZone(zone)) {
796 cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set \n";
0c84faf7 797 cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first.\n";
21fb9631 798 cerr<<"If you run with the BIND backend, make sure you have configured\n";
b925384e 799 cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and\n";
800 cerr<<"'pdnssec create-bind-db /path/fname'!\n";
f60f4bcd
BH
801 return false;
802 }
803
804 DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
805
806 if(!zskset.empty()) {
807 cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
808 return false;
809 }
36758d25
PD
810
811 for(vector<string>::iterator i = k_algos.begin()+1; i != k_algos.end(); i++)
07bf35d1 812 dk.addKey(zone, true, shorthand2algorithm(*i), k_size, true); // obvious errors will have been caught above
36758d25
PD
813
814 BOOST_FOREACH(string z_algo, z_algos)
815 {
816 int algo = shorthand2algorithm(z_algo);
817 dk.addKey(zone, false, algo, z_size);
36758d25
PD
818 }
819
f60f4bcd
BH
820 // rectifyZone(dk, zone);
821 // showZone(dk, zone);
822 cout<<"Zone "<<zone<<" secured"<<endl;
d6c16697 823 return true;
ddac7145
BH
824}
825
46d2e0d6
PD
826void testSchema(DNSSECKeeper& dk, const std::string& zone)
827{
828 cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
829 cout<<"Please clean up after this."<<endl;
830 cout<<endl;
831 cout<<"Constructing UeberBackend"<<endl;
2402c558 832 UeberBackend B("default");
46d2e0d6 833 cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
2402c558 834 DNSBackend *db = B.backends[0];
46d2e0d6
PD
835 cout<<"Creating slave domain "<<zone<<endl;
836 db->createSlaveDomain("127.0.0.1", zone, "_testschema");
837 cout<<"Slave domain created"<<endl;
838
839 DomainInfo di;
2402c558 840 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
46d2e0d6
PD
841 cout<<"Can't find domain we just created, aborting"<<endl;
842 return;
843 }
844 db=di.backend;
845 DNSResourceRecord rr, rrget;
846 cout<<"Starting transaction to feed records"<<endl;
847 db->startTransaction(zone, di.id);
848
849 rr.qtype=QType::SOA;
850 rr.qname=zone;
851 rr.ttl=86400;
852 rr.domain_id=di.id;
853 rr.auth=1;
854 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
855 cout<<"Feeding SOA"<<endl;
856 db->feedRecord(rr);
857 rr.qtype=QType::TXT;
858 // 300 As
859 rr.content="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
860 cout<<"Feeding overlong TXT"<<endl;
861 db->feedRecord(rr);
862 cout<<"Committing"<<endl;
863 db->commitTransaction();
864 cout<<"Querying TXT"<<endl;
865 db->lookup(QType(QType::TXT), zone, NULL, di.id);
866 if(db->get(rrget))
867 {
868 DNSResourceRecord rrthrowaway;
869 if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
870 {
871 cout<<"Expected one record, got multiple, aborting"<<endl;
872 return;
873 }
874 int size=rrget.content.size();
875 if(size != 302)
876 {
877 cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
878 return;
879 }
880 }
881 cout<<"[+] content field is over 255 bytes"<<endl;
882
883 cout<<"Dropping all records, inserting SOA+2xA"<<endl;
884 db->startTransaction(zone, di.id);
885
886 rr.qtype=QType::SOA;
887 rr.qname=zone;
888 rr.ttl=86400;
889 rr.domain_id=di.id;
890 rr.auth=1;
891 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
892 cout<<"Feeding SOA"<<endl;
893 db->feedRecord(rr);
894
895 rr.qtype=QType::A;
896 rr.qname="_underscore."+zone;
897 rr.content="127.0.0.1";
898 db->feedRecord(rr);
899
900 rr.qname="bla."+zone;
901 cout<<"Committing"<<endl;
902 db->commitTransaction();
903
904 cout<<"Securing zone"<<endl;
905 secureZone(dk, zone);
906 cout<<"Rectifying zone"<<endl;
907 rectifyZone(dk, zone);
908 cout<<"Checking underscore ordering"<<endl;
909 string before, after;
910 db->getBeforeAndAfterNames(di.id, zone, "z."+zone, before, after);
911 cout<<"got '"<<before<<"' < 'z."<<zone<<"' < '"<<after<<"'"<<endl;
912 if(before != "_underscore."+zone)
913 {
914 cout<<"before is wrong, got '"<<before<<"', expected '_underscore."<<zone<<"', aborting"<<endl;
915 return;
916 }
917 if(after != zone)
918 {
919 cout<<"after is wrong, got '"<<after<<"', expected '"<<zone<<"', aborting"<<endl;
920 return;
921 }
922 cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
923 cout<<endl;
924 cout<<"End of tests, please remove "<<zone<<" from domains+records"<<endl;
925}
926
1d211b1b 927int main(int argc, char** argv)
20002664 928try
2fa33bce 929{
1d211b1b
BH
930 po::options_description desc("Allowed options");
931 desc.add_options()
932 ("help,h", "produce help message")
b3ce3dec 933 ("verbose,v", "be verbose")
1d211b1b 934 ("force", "force an action")
aa952078 935 ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
7d9dcde0 936 ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
1d211b1b
BH
937 ("commands", po::value<vector<string> >());
938
939 po::positional_options_description p;
940 p.add("commands", -1);
941 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
942 po::notify(g_vm);
943
944 vector<string> cmds;
945
946 if(g_vm.count("commands"))
947 cmds = g_vm["commands"].as<vector<string> >();
948
b3ce3dec
BH
949 g_verbose = g_vm.count("verbose");
950
1d211b1b 951 if(cmds.empty() || g_vm.count("help")) {
e1489e74
PD
952 cerr<<"Usage: \npdnssec [options] <command> [params ..]\n\n";
953 cerr<<"Commands:\n";
151b561f 954 cerr<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE\n";
4af49b80 955 cerr<<"add-zone-key ZONE zsk|ksk [bits] [active|passive]\n";
151b561f
PD
956 cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]\n";
957 cerr<<" Add a ZSK or KSK to zone and specify algo&bits\n";
958 cerr<<"check-zone ZONE Check a zone for correctness\n";
959 cerr<<"check-all-zones Check all zones for correctness\n";
960 cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)\n";
961 cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE\n";
962 cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE\n";
963 cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described\n";
964 cerr<<"export-zone-key ZONE KEY-ID Export to stdout the private key described\n";
7e5b2860 965 cerr<<"generate-zone-key zsk|ksk [algorithm] [bits]\n";
49baee5f 966 cerr<<" Generate a ZSK or KSK to stdout with specified algo&bits\n";
151b561f 967 cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE\n";
4b88a3cd 968 cerr<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT\n";
151b561f 969 cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK\n";
4af49b80 970 cerr<<" [active|passive][ksk|zsk] Defaults to KSK and active\n";
151b561f
PD
971 cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)\n";
972 cerr<<"rectify-all-zones Rectify all zones.\n";
973 cerr<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE\n";
974 cerr<<"secure-zone ZONE [ZONE ..] Add KSK and two ZSKs\n";
975 cerr<<"set-nsec3 ZONE ['params' [narrow]] Enable NSEC3 with PARAMs. Optionally narrow\n";
976 cerr<<"set-presigned ZONE Use presigned RRSIGs from storage\n";
977 cerr<<"show-zone ZONE Show DNSSEC (public) key details about a zone\n";
978 cerr<<"unset-nsec3 ZONE Switch back to NSEC\n";
979 cerr<<"unset-presigned ZONE No longer use presigned RRSIGs\n";
6f872b78 980 cerr<<"test-schema ZONE Test DB schema - will create ZONE\n";
7e5b2860 981 cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key\n";
4831df57 982 cerr<<"create-tsig-key NAME ALGORITHM Generate new TSIG key\n";
7e5b2860
AT
983 cerr<<"list-tsig-keys List all TSIG keys\n";
984 cerr<<"delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)\n";
4831df57
AT
985 cerr<<"enable-tsig-key ZONE NAME [master|slave]\n";
986 cerr<<" Enable TSIG key for a zone\n";
987 cerr<<"disable-tsig-key ZONE NAME [master|slave]\n";
988 cerr<<" Disable TSIG key for a zone\n";
1d211b1b
BH
989 cerr<<desc<<endl;
990 return 0;
991 }
a7e0acd8 992
cbb0025b 993 if (cmds[0] == "test-algorithm") {
2551cc18
PD
994 if(cmds.size() != 2) {
995 cerr << "Syntax: pdnssec test-algorithm algonum"<<endl;
996 return 0;
997 }
cbb0025b
PD
998 testAlgorithm(lexical_cast<int>(cmds[1]));
999 return 0;
1000 }
1001
ea937fd4
BH
1002 if(cmds[0] == "test-algorithms") {
1003 testAlgorithms();
1004 return 0;
1005 }
1006
7d9dcde0 1007 loadMainConfig(g_vm["config-dir"].as<string>());
6dfa0aa0 1008 reportAllTypes();
b925384e 1009
2717b8b3 1010 if(cmds[0] == "create-bind-db") {
fbe72b7a
BH
1011 if(cmds.size() != 2) {
1012 cerr << "Syntax: pdnssec create-bind-db fname"<<endl;
1013 return 0;
1014 }
080f5d57
PD
1015 try {
1016 Bind2Backend::createDNSSECDB(cmds[1]);
1017 }
3f81d239 1018 catch (PDNSException& ae) {
080f5d57
PD
1019 cerr<<"Error: "<<ae.reason<<endl;
1020 return 1;
1021 }
1022 return 0;
2717b8b3 1023 }
fbe72b7a
BH
1024
1025 DNSSECKeeper dk;
1026
46d2e0d6
PD
1027 if (cmds[0] == "test-schema") {
1028 if(cmds.size() != 2) {
1029 cerr << "Syntax: pdnssec test-schema ZONE"<<endl;
1030 return 0;
1031 }
1032 testSchema(dk, cmds[1]);
1033 return 0;
1034 }
fbe72b7a 1035 if(cmds[0] == "rectify-zone") {
d4904322
BH
1036 if(cmds.size() < 2) {
1037 cerr << "Syntax: pdnssec rectify-zone ZONE [ZONE..]"<<endl;
81b39e4b
BH
1038 return 0;
1039 }
032e3906 1040 unsigned int exitCode = 0;
d4904322 1041 for(unsigned int n = 1; n < cmds.size(); ++n)
032e3906
PD
1042 if (!rectifyZone(dk, cmds[n])) exitCode = 1;
1043 return exitCode;
20002664 1044 }
1325e8a2
PD
1045 else if (cmds[0] == "rectify-all-zones") {
1046 rectifyAllZones(dk);
1047 }
9abd98d3 1048 else if(cmds[0] == "check-zone") {
5d2e58b0 1049 if(cmds.size() != 2) {
37fd8771 1050 cerr << "Syntax: pdnssec check-zone ZONE"<<endl;
5d2e58b0
BH
1051 return 0;
1052 }
ba86110a
PD
1053 UeberBackend B("default");
1054 exit(checkZone(dk, B, cmds[1]));
5d2e58b0 1055 }
1325e8a2 1056 else if (cmds[0] == "check-all-zones") {
12a92688 1057 exit(checkAllZones(dk));
1325e8a2 1058 }
81e9ba89
PD
1059 else if (cmds[0] == "test-zone") {
1060 cerr << "Did you mean check-zone?"<<endl;
1061 return 0;
1062 }
1063 else if (cmds[0] == "test-all-zones") {
1064 cerr << "Did you mean check-all-zones?"<<endl;
1065 return 0;
1066 }
49449751
BH
1067#if 0
1068 else if(cmds[0] == "signing-server" )
1069 {
1070 signingServer();
1071 }
1072 else if(cmds[0] == "signing-slave")
1073 {
1074 launchSigningService(0);
1075 }
1076#endif
ea937fd4 1077 else if(cmds[0] == "test-speed") {
37fd8771
BH
1078 if(cmds.size() < 2) {
1079 cerr << "Syntax: pdnssec test-speed numcores [signing-server]"<<endl;
ea937fd4
BH
1080 return 0;
1081 }
49449751 1082 testSpeed(dk, cmds[1], (cmds.size() > 3) ? cmds[3] : "", atoi(cmds[2].c_str()));
189bb9d2 1083 }
aa65a832
BH
1084 else if(cmds[0] == "verify-crypto") {
1085 if(cmds.size() != 2) {
37fd8771 1086 cerr << "Syntax: pdnssec verify-crypto FILE"<<endl;
aa65a832
BH
1087 return 0;
1088 }
1089 verifyCrypto(cmds[1]);
1090 }
da11ed0e
BH
1091
1092 else if(cmds[0] == "show-zone") {
a0472099 1093 if(cmds.size() != 2) {
37fd8771 1094 cerr << "Syntax: pdnssec show-zone ZONE"<<endl;
a0472099
BH
1095 return 0;
1096 }
1d211b1b 1097 const string& zone=cmds[1];
032e3906 1098 if (!showZone(dk, zone)) return 1;
1d211b1b 1099 }
5935cede
BH
1100 else if(cmds[0] == "disable-dnssec") {
1101 if(cmds.size() != 2) {
37fd8771 1102 cerr << "Syntax: pdnssec disable-dnssec ZONE"<<endl;
5935cede
BH
1103 return 0;
1104 }
1105 const string& zone=cmds[1];
032e3906
PD
1106 if(!disableDNSSECOnZone(dk, zone))
1107 return 1;
5935cede 1108 }
bed962b5 1109 else if(cmds[0] == "activate-zone-key") {
37fd8771
BH
1110 if(cmds.size() != 3) {
1111 cerr << "Syntax: pdnssec activate-zone-key ZONE KEY-ID"<<endl;
1112 return 0;
1113 }
bed962b5
BH
1114 const string& zone=cmds[1];
1115 unsigned int id=atoi(cmds[2].c_str());
77b909cc
PD
1116 if(!id)
1117 {
1118 cerr<<"Invalid KEY-ID"<<endl;
1119 return 1;
1120 }
a84a8203
PD
1121 if (!dk.activateKey(zone, id)) {
1122 cerr<<"Activation of key failed"<<endl;
1123 return 1;
1124 }
1125 return 0;
bed962b5
BH
1126 }
1127 else if(cmds[0] == "deactivate-zone-key") {
37fd8771
BH
1128 if(cmds.size() != 3) {
1129 cerr << "Syntax: pdnssec deactivate-zone-key ZONE KEY-ID"<<endl;
1130 return 0;
1131 }
bed962b5
BH
1132 const string& zone=cmds[1];
1133 unsigned int id=atoi(cmds[2].c_str());
77b909cc
PD
1134 if(!id)
1135 {
1136 cerr<<"Invalid KEY-ID"<<endl;
1137 return 1;
1138 }
a84a8203
PD
1139 if (!dk.deactivateKey(zone, id)) {
1140 cerr<<"Deactivation of key failed"<<endl;
1141 return 1;
1142 }
1143 return 0;
bed962b5
BH
1144 }
1145 else if(cmds[0] == "add-zone-key") {
37fd8771 1146 if(cmds.size() < 3 ) {
52690ba5 1147 cerr << "Syntax: pdnssec add-zone-key ZONE zsk|ksk [bits] [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
37fd8771
BH
1148 return 0;
1149 }
bed962b5 1150 const string& zone=cmds[1];
032e3906
PD
1151
1152 UeberBackend B("default");
1153 DomainInfo di;
1154
1155 if (!B.getDomainInfo(zone, di)){
1156 cerr << "No such zone in the database" << endl;
1157 return 0;
1158 }
1159
36c394e5
BH
1160 // need to get algorithm, bits & ksk or zsk from commandline
1161 bool keyOrZone=false;
36758d25 1162 int tmp_algo=0;
36c394e5 1163 int bits=0;
0ba8e0f1 1164 int algorithm=8;
4af49b80 1165 bool active=false;
36c394e5
BH
1166 for(unsigned int n=2; n < cmds.size(); ++n) {
1167 if(pdns_iequals(cmds[n], "zsk"))
1168 keyOrZone = false;
1169 else if(pdns_iequals(cmds[n], "ksk"))
1170 keyOrZone = true;
36758d25
PD
1171 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1172 algorithm = tmp_algo;
4af49b80 1173 } else if(pdns_iequals(cmds[n], "active")) {
1174 active=true;
1175 } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) {
1176 active=false;
1177 } else if(atoi(cmds[n].c_str())) {
36c394e5 1178 bits = atoi(cmds[n].c_str());
4af49b80 1179 } else {
a254438f 1180 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
07bf35d1 1181 exit(EXIT_FAILURE);;
36c394e5
BH
1182 }
1183 }
07bf35d1 1184 if(!dk.addKey(zone, keyOrZone, algorithm, bits, active)) {
1185 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1186 exit(1);
1187 }
1188 else {
1189 cerr<<"Added a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<", active="<<active<<endl;
1190 if(bits)
1191 cerr<<"Requested specific key size of "<<bits<<" bits"<<endl;
1192 }
bed962b5
BH
1193 }
1194 else if(cmds[0] == "remove-zone-key") {
471304cc 1195 if(cmds.size() < 3) {
48cb6e2a 1196 cerr<<"Syntax: pdnssec remove-zone-key ZONE KEY-ID"<<endl;
471304cc
BH
1197 return 0;
1198 }
bed962b5
BH
1199 const string& zone=cmds[1];
1200 unsigned int id=atoi(cmds[2].c_str());
a84a8203
PD
1201 if (!dk.removeKey(zone, id)) {
1202 return 1;
1203 }
1204 return 0;
bed962b5
BH
1205 }
1206
c3c89361 1207 else if(cmds[0] == "secure-zone") {
ddac7145 1208 if(cmds.size() < 2) {
37fd8771 1209 cerr << "Syntax: pdnssec secure-zone ZONE"<<endl;
a9175ad6
BH
1210 return 0;
1211 }
d6c16697 1212 vector<string> mustRectify;
ddac7145 1213 dk.startTransaction();
1325e8a2 1214 unsigned int zoneErrors=0;
ddac7145 1215 for(unsigned int n = 1; n < cmds.size(); ++n) {
f60f4bcd
BH
1216 const string& zone=cmds[n];
1217 if(secureZone(dk, zone)) {
d6c16697 1218 mustRectify.push_back(zone);
1325e8a2
PD
1219 } else {
1220 zoneErrors++;
d6c16697 1221 }
f60f4bcd 1222 }
1d211b1b 1223
ddac7145 1224 dk.commitTransaction();
d6c16697
BH
1225 BOOST_FOREACH(string& zone, mustRectify)
1226 rectifyZone(dk, zone);
1325e8a2
PD
1227
1228 if (zoneErrors) {
1229 return 1;
1230 }
1231 return 0;
1d211b1b 1232 }
da11ed0e 1233 else if(cmds[0]=="set-nsec3") {
37fd8771
BH
1234 if(cmds.size() < 2) {
1235 cerr<<"Syntax: pdnssec set-nsec3 ZONE 'params' [narrow]"<<endl;
1236 return 0;
1237 }
b8adb30d 1238 string nsec3params = cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
22c5aa60 1239 bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
da11ed0e 1240 NSEC3PARAMRecordContent ns3pr(nsec3params);
775acd9e 1241
1242 string zone=cmds[1];
1243 if(!dk.isSecuredZone(zone)) {
1244 cerr<<"Zone '"<<zone<<"' is not secured, can't set NSEC3 parameters"<<endl;
1245 exit(EXIT_FAILURE);
1246 }
1247 dk.setNSEC3PARAM(zone, ns3pr, narrow);
1248
b8adb30d
KM
1249 if (!ns3pr.d_flags)
1250 cerr<<"NSEC3 set, please rectify-zone if your backend needs it"<<endl;
1251 else
1252 cerr<<"NSEC3 (opt-out) set, please rectify-zone if your backend needs it"<<endl;
da11ed0e 1253 }
d3e7090c 1254 else if(cmds[0]=="set-presigned") {
5935cede 1255 if(cmds.size() < 2) {
37fd8771
BH
1256 cerr<<"Syntax: pdnssec set-presigned ZONE"<<endl;
1257 return 0;
5935cede 1258 }
a84a8203
PD
1259 if (! dk.setPresigned(cmds[1])) {
1260 return 1;
1261 }
1262 return 0;
d3e7090c
BH
1263 }
1264 else if(cmds[0]=="unset-presigned") {
37fd8771
BH
1265 if(cmds.size() < 2) {
1266 cerr<<"Syntax: pdnssec unset-presigned ZONE"<<endl;
f60f4bcd 1267 return 0;
37fd8771 1268 }
a84a8203
PD
1269 if (! dk.unsetPresigned(cmds[1])) {
1270 return 1;
1271 }
1272 return 0;
d3e7090c 1273 }
65c87942
BH
1274 else if(cmds[0]=="hash-zone-record") {
1275 if(cmds.size() < 3) {
37fd8771 1276 cerr<<"Syntax: pdnssec hash-zone-record ZONE RNAME"<<endl;
65c87942
BH
1277 return 0;
1278 }
1279 string& zone=cmds[1];
1280 string& record=cmds[2];
1281 NSEC3PARAMRecordContent ns3pr;
1282 bool narrow;
1283 if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
1284 cerr<<"The '"<<zone<<"' zone does not use NSEC3"<<endl;
1285 return 0;
1286 }
5e42374c 1287 if(narrow) {
65c87942
BH
1288 cerr<<"The '"<<zone<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
1289 }
1290
1bad4190 1291 cout<<toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, record))<<endl;
65c87942 1292 }
da11ed0e 1293 else if(cmds[0]=="unset-nsec3") {
37fd8771
BH
1294 if(cmds.size() < 2) {
1295 cerr<<"Syntax: pdnssec unset-nsec3 ZONE"<<endl;
a84a8203 1296 return 0;
37fd8771 1297 }
a84a8203
PD
1298 if ( ! dk.unsetNSEC3PARAM(cmds[1])) {
1299 return 1;
1300 }
1301 return 0;
da11ed0e
BH
1302 }
1303 else if(cmds[0]=="export-zone-key") {
7ddd79a7 1304 if(cmds.size() < 3) {
37fd8771 1305 cerr<<"Syntax: pdnssec export-zone-key ZONE KEY-ID"<<endl;
a84a8203 1306 return 0;
7ddd79a7
BH
1307 }
1308
da11ed0e
BH
1309 string zone=cmds[1];
1310 unsigned int id=atoi(cmds[2].c_str());
1311 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
189bb9d2 1312 cout << dpk.getKey()->convertToISC() <<endl;
4496f66f 1313 }
04576eed
RA
1314 else if(cmds[0]=="increase-serial") {
1315 if (cmds.size() < 2) {
1316 cerr<<"Syntax: pdnssec increase-serial ZONE"<<endl;
1317 return 0;
1318 }
1319 return increaseSerial(cmds[1], dk);
1320 }
ed3f8559
BH
1321 else if(cmds[0]=="import-zone-key-pem") {
1322 if(cmds.size() < 4) {
4af49b80 1323 cerr<<"Syntax: pdnssec import-zone-key-pem ZONE FILE algorithm [ksk|zsk]"<<endl;
ed3f8559
BH
1324 exit(1);
1325 }
1326 string zone=cmds[1];
1327 string fname=cmds[2];
1328 string line;
1329 ifstream ifs(fname.c_str());
1330 string tmp, interim, raw;
1331 while(getline(ifs, line)) {
1332 if(line[0]=='-')
1333 continue;
1334 trim(line);
1335 interim += line;
1336 }
1337 B64Decode(interim, raw);
1338 DNSSECPrivateKey dpk;
699e6e37 1339 DNSKEYRecordContent drc;
8d9f38f2 1340 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromPEMString(drc, raw));
699e6e37 1341 dpk.setKey(key);
ed3f8559
BH
1342
1343 dpk.d_algorithm = atoi(cmds[3].c_str());
1344
1345 if(dpk.d_algorithm == 7)
1346 dpk.d_algorithm = 5;
1347
1348 cerr<<(int)dpk.d_algorithm<<endl;
1349
1350 if(cmds.size() > 4) {
1351 if(pdns_iequals(cmds[4], "ZSK"))
1352 dpk.d_flags = 256;
1353 else if(pdns_iequals(cmds[4], "KSK"))
1354 dpk.d_flags = 257;
1355 else {
1356 cerr<<"Unknown key flag '"<<cmds[4]<<"'\n";
1357 exit(1);
1358 }
1359 }
1360 else
1361 dpk.d_flags = 257; // ksk
1362
07bf35d1 1363 if(!dk.addKey(zone, dpk)) {
1364 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1365 exit(1);
1366 }
ed3f8559
BH
1367
1368 }
976b6541 1369 else if(cmds[0]=="import-zone-key") {
d1eacbeb 1370 if(cmds.size() < 3) {
4af49b80 1371 cerr<<"Syntax: pdnssec import-zone-key ZONE FILE [ksk|zsk] [active|passive]"<<endl;
4496f66f
BH
1372 exit(1);
1373 }
976b6541
BH
1374 string zone=cmds[1];
1375 string fname=cmds[2];
1376 DNSSECPrivateKey dpk;
699e6e37 1377 DNSKEYRecordContent drc;
8d9f38f2 1378 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromISCFile(drc, fname.c_str()));
699e6e37 1379 dpk.setKey(key);
7ddd79a7
BH
1380 dpk.d_algorithm = drc.d_algorithm;
1381
1382 if(dpk.d_algorithm == 7)
1383 dpk.d_algorithm = 5;
aa952078 1384
4af49b80 1385 dpk.d_flags = 257;
4cec6ac5 1386 bool active=true;
4af49b80 1387
1388 for(unsigned int n = 3; n < cmds.size(); ++n) {
1389 if(pdns_iequals(cmds[n], "ZSK"))
1390 dpk.d_flags = 256;
1391 else if(pdns_iequals(cmds[n], "KSK"))
1392 dpk.d_flags = 257;
1393 else if(pdns_iequals(cmds[n], "active"))
1394 active = 1;
1395 else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive"))
1396 active = 0;
1397 else {
1398 cerr<<"Unknown key flag '"<<cmds[n]<<"'\n";
1399 exit(1);
1400 }
aa952078 1401 }
07bf35d1 1402 if(!dk.addKey(zone, dpk, active)) {
1403 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1404 exit(1);
1405 }
976b6541 1406 }
da11ed0e 1407 else if(cmds[0]=="export-zone-dnskey") {
7ddd79a7 1408 if(cmds.size() < 3) {
37fd8771 1409 cerr<<"Syntax: pdnssec export-zone-dnskey ZONE KEY-ID"<<endl;
7ddd79a7
BH
1410 exit(1);
1411 }
1412
da11ed0e
BH
1413 string zone=cmds[1];
1414 unsigned int id=atoi(cmds[2].c_str());
1415 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1416 cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
f8b25763
BH
1417 if(dpk.d_flags == 257) {
1418 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
1419 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
1420 }
da11ed0e 1421 }
950bdddf
PD
1422 else if(cmds[0] == "generate-zone-key") {
1423 if(cmds.size() < 2 ) {
7e5b2860 1424 cerr << "Syntax: pdnssec generate-zone-key zsk|ksk [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384] [bits]"<<endl;
950bdddf
PD
1425 return 0;
1426 }
1427 // need to get algorithm, bits & ksk or zsk from commandline
1428 bool keyOrZone=false;
1429 int tmp_algo=0;
1430 int bits=0;
1431 int algorithm=8;
1432 for(unsigned int n=1; n < cmds.size(); ++n) {
1433 if(pdns_iequals(cmds[n], "zsk"))
1434 keyOrZone = false;
1435 else if(pdns_iequals(cmds[n], "ksk"))
1436 keyOrZone = true;
1437 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1438 algorithm = tmp_algo;
1439 } else if(atoi(cmds[n].c_str()))
1440 bits = atoi(cmds[n].c_str());
1441 else {
1442 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
1443 return 0;
1444 }
1445 }
1446 cerr<<"Generating a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
1447 if(bits)
1448 cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
1449
1450 DNSSECPrivateKey dspk;
1451 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
1452 if(!bits) {
1453 if(algorithm <= 10)
1454 bits = keyOrZone ? 2048 : 1024;
1455 else {
1456 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // ECDSA, GOST, ED25519
1457 bits = 256;
1458 else if(algorithm == 14)
1459 bits = 384;
1460 else {
1461 throw runtime_error("Can't guess key size for algoritm "+lexical_cast<string>(algorithm));
1462 }
1463 }
1464 }
1465 dpk->create(bits);
1466 dspk.setKey(dpk);
1467 dspk.d_algorithm = algorithm;
1468 dspk.d_flags = keyOrZone ? 257 : 256;
1469
1470 // print key to stdout
1471 cout << "Flags: " << dspk.d_flags << endl <<
1472 dspk.getKey()->convertToISC() << endl;
4831df57 1473 } else if (cmds[0]=="create-tsig-key") {
6f872b78
AT
1474 if (cmds.size() < 3) {
1475 cerr << "Syntax: " << cmds[0] << " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)" << endl;
1476 return 0;
1477 }
1478 string name = cmds[1];
1479 string algo = cmds[2];
1480 string key;
1481 char tmpkey[64];
1482
d885cea7 1483 size_t klen = 0;
6f872b78
AT
1484 if (algo == "hmac-md5") {
1485 klen = 32;
1486 } else if (algo == "hmac-sha1") {
1487 klen = 32;
1488 } else if (algo == "hmac-sha224") {
1489 klen = 32;
1490 } else if (algo == "hmac-sha256") {
1491 klen = 64;
1492 } else if (algo == "hmac-sha384") {
1493 klen = 64;
1494 } else if (algo == "hmac-sha512") {
1495 klen = 64;
d885cea7
AT
1496 } else {
1497 cerr << "Cannot generate key for " << algo << endl;
1498 return 1;
6f872b78
AT
1499 }
1500
7e5b2860 1501 cerr << "Generating new key with " << klen << " bytes (this can take a while)" << endl;
a56bc64d
AT
1502 seedRandom(::arg()["entropy-source"]);
1503 for(size_t i = 0; i < klen; i+=4) {
1504 *(unsigned int*)(tmpkey+i) = dns_random(0xffffffff);
1505 }
6f872b78
AT
1506 key = Base64Encode(std::string(tmpkey, klen));
1507
1508 UeberBackend B("default");
1509 if (B.setTSIGKey(name, algo, key)) {
1510 cout << "Create new TSIG key " << name << " " << algo << " " << key << endl;
1511 } else {
1512 cout << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
1513 return 1;
1514 }
1515 return 0;
1516 } else if (cmds[0]=="import-tsig-key") {
1517 if (cmds.size() < 4) {
1518 cerr << "Syntax: " << cmds[0] << " name algorithm key" << endl;
1519 return 0;
1520 }
1521 string name = cmds[1];
1522 string algo = cmds[2];
1523 string key = cmds[3];
1524
1525 UeberBackend B("default");
1526 if (B.setTSIGKey(name, algo, key)) {
1527 cout << "Imported TSIG key " << name << " " << algo << endl;
1528 } else {
1529 cout << "Failure importing TSIG key " << name << " " << algo << endl;
1530 return 1;
1531 }
1532 return 0;
1533 } else if (cmds[0]=="delete-tsig-key") {
1534 if (cmds.size() < 2) {
1535 cerr << "Syntax: " << cmds[0] << " name" << endl;
1536 return 0;
1537 }
1538 string name = cmds[1];
1539 string algo = cmds[2];
1540
1541 UeberBackend B("default");
1542 if (B.deleteTSIGKey(name)) {
1543 cout << "Deleted TSIG key " << name << endl;
1544 } else {
1545 cout << "Failure deleting TSIG key " << name << endl;
1546 return 1;
1547 }
1548 return 0;
1549 } else if (cmds[0]=="list-tsig-keys") {
1550 std::vector<struct TSIGKey> keys;
1551 UeberBackend B("default");
1552 if (B.getTSIGKeys(keys)) {
1553 BOOST_FOREACH(const struct TSIGKey &key, keys) {
1554 cout << key.name << " " << key.algorithm << " " << key.key << endl;
1555 }
1556 }
1557 return 0;
1558 } else if (cmds[0]=="enable-tsig-key") {
4831df57
AT
1559 string metaKey;
1560 if (cmds.size() < 4) {
1561 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
6f872b78
AT
1562 return 0;
1563 }
1564 string zname = cmds[1];
1565 string name = cmds[2];
4831df57
AT
1566 if (cmds[3] == "master")
1567 metaKey = "TSIG-ALLOW-AXFR";
1568 else if (cmds[3] == "slave")
1569 metaKey = "AXFR-MASTER-TSIG";
1570 else {
1571 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
1572 return 1;
1573 }
6f872b78
AT
1574 UeberBackend B("default");
1575 std::vector<std::string> meta;
4831df57 1576 if (!B.getDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
1577 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
1578 return 1;
1579 }
1580 bool found = false;
1581 BOOST_FOREACH(std::string tmpname, meta) {
1582 if (tmpname == name) { found = true; break; }
1583 }
1584 if (!found) meta.push_back(name);
4831df57 1585 if (B.setDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
1586 cout << "Enabled TSIG key " << name << " for " << zname << endl;
1587 } else {
1588 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
1589 return 1;
1590 }
1591 return 0;
1592 } else if (cmds[0]=="disable-tsig-key") {
4831df57
AT
1593 string metaKey;
1594 if (cmds.size() < 4) {
1595 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
6f872b78
AT
1596 return 0;
1597 }
1598 string zname = cmds[1];
1599 string name = cmds[2];
4831df57
AT
1600 if (cmds[3] == "master")
1601 metaKey = "TSIG-ALLOW-AXFR";
1602 else if (cmds[3] == "slave")
1603 metaKey = "AXFR-MASTER-TSIG";
1604 else {
1605 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
1606 return 1;
1607 }
6f872b78
AT
1608
1609 UeberBackend B("default");
1610 std::vector<std::string> meta;
4831df57 1611 if (!B.getDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
1612 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
1613 return 1;
1614 }
1615 std::vector<std::string>::iterator iter = meta.begin();
1616 for(;iter != meta.end(); iter++) if (*iter == name) break;
1617 if (iter != meta.end()) meta.erase(iter);
4831df57 1618 if (B.setDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
1619 cout << "Disabled TSIG key " << name << " for " << zname << endl;
1620 } else {
1621 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
1622 return 1;
1623 }
1624 return 0;
950bdddf 1625 }
1d211b1b 1626 else {
6f872b78 1627 cerr<<"Unknown command '"<<cmds[0] << endl;
1d211b1b
BH
1628 return 1;
1629 }
1630 return 0;
1631}
3f81d239 1632catch(PDNSException& ae) {
20002664
BH
1633 cerr<<"Error: "<<ae.reason<<endl;
1634}
14133ba3
BH
1635catch(std::exception& e) {
1636 cerr<<"Error: "<<e.what()<<endl;
1637}
46d2e0d6 1638