]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pdnssec.cc
ignore NSEC/NSEC3 records while loading bind zonefiles
[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"
49449751 16
20002664
BH
17StatBag S;
18PacketCache PC;
1d211b1b 19
de43ec0f 20using boost::scoped_ptr;
1d211b1b
BH
21namespace po = boost::program_options;
22po::variables_map g_vm;
23
39a8b5c0 24string s_programname="pdns";
20002664 25
b3ce3dec
BH
26namespace {
27 bool g_verbose; // doesn't yet do anything though
49449751
BH
28}
29
20002664
BH
30ArgvMap &arg()
31{
32 static ArgvMap arg;
33 return arg;
34}
35
1d211b1b
BH
36string humanTime(time_t t)
37{
38 char ret[256];
39 struct tm tm;
40 localtime_r(&t, &tm);
41 strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d
42 return ret;
43}
44
7d9dcde0 45void loadMainConfig(const std::string& configdir)
20002664 46{
7d9dcde0 47 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
c6347f61 48 ::arg().set("pipebackend-abi-version","Version of the pipe backend ABI")="1";
f2e7d77b 49 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
20002664 50 ::arg().set("launch","Which backends to launch");
6dfa0aa0 51 ::arg().set("dnssec","if we should do dnssec")="true";
4f6cf113 52 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
20002664
BH
53 ::arg().setCmd("help","Provide a helpful message");
54 //::arg().laxParse(argc,argv);
55
56 if(::arg().mustDo("help")) {
57 cerr<<"syntax:"<<endl<<endl;
58 cerr<<::arg().helpstring(::arg()["help"])<<endl;
59 exit(99);
60 }
61
62 if(::arg()["config-name"]!="")
63 s_programname+="-"+::arg()["config-name"];
64
65 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
66 cleanSlashes(configname);
20002664
BH
67
68 ::arg().laxFile(configname.c_str());
b5baefaf 69 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
a7e0acd8 70 ::arg().set("module-dir","Default directory for modules")=LIBDIR;
20002664
BH
71 BackendMakers().launch(::arg()["launch"]); // vrooooom!
72 ::arg().laxFile(configname.c_str());
9abd98d3 73 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
20002664
BH
74
75 S.declare("qsize-q","Number of questions waiting for database attention");
76
77 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
78 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
79
80 S.declare("query-cache-hit","Number of hits on the query cache");
81 S.declare("query-cache-miss","Number of misses on the query cache");
82 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
83 ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no";
ec7f535c 84 ::arg().set("recursive-cache-ttl","Seconds to store packets for recursive queries in the PacketCache")="10";
20002664 85 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
ec7f535c
PD
86 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
87 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
da6a2926 88 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
20002664
BH
89 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
90 ::arg().set("soa-retry-default","Default SOA retry")="3600";
91 ::arg().set("soa-expire-default","Default SOA expire")="604800";
ec7f535c 92 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
abc1d928 93 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
a7e0acd8 94
20002664
BH
95 UeberBackend::go();
96}
97
d6c16697 98// irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
d4904322 99// I think this has to do with interlocking transactions between B and DK, but unsure.
f28f2324 100void rectifyZone(DNSSECKeeper& dk, const std::string& zone)
20002664 101{
c3221d6e 102 scoped_ptr<UeberBackend> B(new UeberBackend("default"));
d6c16697 103 bool doTransaction=true; // but see above
20002664 104 SOAData sd;
1325e8a2 105 sd.db = (DNSBackend*)-1;
81b39e4b
BH
106
107 if(!B->getSOA(zone, sd)) {
f28f2324 108 cerr<<"No SOA known for '"<<zone<<"', is such a zone in the database?"<<endl;
01fde57c 109 return;
20002664 110 }
81b39e4b 111 sd.db->list(zone, sd.domain_id);
81b39e4b 112
b5baefaf
PD
113 DNSResourceRecord rr;
114 set<string> qnames, nsset, dsnames, nonterm, insnonterm, delnonterm;
115 bool doent=true;
20002664
BH
116
117 while(sd.db->get(rr)) {
b5baefaf
PD
118 if (rr.qtype.getCode())
119 {
120 qnames.insert(rr.qname);
121 if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, zone))
122 nsset.insert(rr.qname);
123 if(rr.qtype.getCode() == QType::DS)
124 dsnames.insert(rr.qname);
125 }
126 else
127 if(doent)
128 delnonterm.insert(rr.qname);
81b39e4b 129 }
9abd98d3 130
c3c89361 131 NSEC3PARAMRecordContent ns3pr;
f28f2324 132 bool narrow;
3c873e66 133 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
ece45ffb
PD
134 if(sd.db->doesDNSSEC())
135 {
136 if(!haveNSEC3)
137 cerr<<"Adding NSEC ordering information "<<endl;
138 else if(!narrow)
139 cerr<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'"<<endl;
140 else
141 cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
142 }
143 else
144 cerr<<"Non DNSSEC zone, only adding empty non-terminals"<<endl;
9abd98d3 145
d6c16697
BH
146 if(doTransaction)
147 sd.db->startTransaction("", -1);
b5baefaf
PD
148
149 bool realrr=true;
150 string hashed;
151
152 uint32_t maxent = ::arg().asNum("max-ent-entries");
153
154 dononterm:;
81b39e4b
BH
155 BOOST_FOREACH(const string& qname, qnames)
156 {
f28f2324 157 bool auth=true;
b5baefaf 158 string shorter(qname);
27045410 159
b5baefaf
PD
160 if(realrr) {
161 do {
162 if(nsset.count(shorter)) {
163 auth=false;
164 break;
165 }
166 } while(chopOff(shorter));
b5baefaf 167 }
27045410
PD
168
169 if(haveNSEC3)
170 {
f28f2324
BH
171 if(!narrow) {
172 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
4bf664d8
PD
173 if(g_verbose)
174 cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
c2df797e 175 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
f28f2324 176 }
c2df797e
PD
177 else
178 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
179 if(realrr)
27045410 180 {
c2df797e
PD
181 if (dsnames.count(qname))
182 sd.db->setDNSSECAuthOnDsRecord(sd.domain_id, qname);
183 if (!auth || nsset.count(qname)) {
184 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "NS");
185 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "A");
186 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "AAAA");
187 }
27045410
PD
188 }
189 }
190 else // NSEC
191 {
b5baefaf 192 if(realrr)
27045410 193 {
b5baefaf 194 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, auth);
c2df797e
PD
195 if (dsnames.count(qname))
196 sd.db->setDNSSECAuthOnDsRecord(sd.domain_id, qname);
197 if (!auth || nsset.count(qname)) {
b5baefaf
PD
198 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "A");
199 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "AAAA");
200 }
201 }
202 else
203 {
c2df797e 204 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
b5baefaf
PD
205 }
206 }
207
208 if(auth && realrr && doent)
209 {
210 shorter=qname;
211 while(!pdns_iequals(shorter, zone) && chopOff(shorter))
212 {
213 if(!qnames.count(shorter) && !nonterm.count(shorter))
214 {
215 if(!(maxent))
216 {
217 cerr<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
218 insnonterm.clear();
219 delnonterm.clear();
220 doent=false;
221 break;
222 }
223 nonterm.insert(shorter);
224 if (!delnonterm.count(shorter))
225 insnonterm.insert(shorter);
226 else
227 delnonterm.erase(shorter);
228 --maxent;
229 }
27045410 230 }
c3c89361 231 }
20002664 232 }
b5baefaf
PD
233
234 if(realrr)
235 {
236 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
237 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
238 {
239 sd.db->updateEmptyNonTerminals(sd.domain_id, zone, insnonterm, delnonterm, !doent);
240 }
241 if(doent)
242 {
243 realrr=false;
244 qnames=nonterm;
245 goto dononterm;
246 }
247 }
248
d6c16697
BH
249 if(doTransaction)
250 sd.db->commitTransaction();
20002664
BH
251}
252
1325e8a2
PD
253void rectifyAllZones(DNSSECKeeper &dk)
254{
255 scoped_ptr<UeberBackend> B(new UeberBackend("default"));
256 vector<DomainInfo> domainInfo;
257
258 B->getAllDomains(&domainInfo);
259 BOOST_FOREACH(DomainInfo di, domainInfo) {
260 cerr<<"Rectifying "<<di.zone<<": ";
261 rectifyZone(dk, di.zone);
262 }
263 cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
264}
265
9d335015 266int checkZone(UeberBackend *B, const std::string& zone)
5d2e58b0 267{
5d2e58b0 268 SOAData sd;
1325e8a2 269 sd.db=(DNSBackend*)-1;
5d2e58b0 270 if(!B->getSOA(zone, sd)) {
0ae6a6cc
BH
271 cout<<"No SOA for zone '"<<zone<<"'"<<endl;
272 return -1;
5d2e58b0 273 }
5d2e58b0
BH
274 sd.db->list(zone, sd.domain_id);
275 DNSResourceRecord rr;
8c949c52 276 uint64_t numrecords=0, numerrors=0, numwarnings=0;
5d2e58b0 277
035297ad 278 while(sd.db->get(rr)) {
b191a835
PD
279 if(!endsOn(rr.qname, zone)) {
280 cout<<"[Warning] The record "<<rr.qname<<" with type "<<rr.qtype.getName()<<" in zone "<<zone<<" is out-of-zone."<<endl;
281 numwarnings++;
282 continue;
283 }
284
0c13544c
PD
285 if(!rr.qtype.getCode())
286 continue;
287
288 if(rr.qtype.getCode() == QType::SOA)
289 {
290 fillSOAData(rr.content, sd);
291 rr.content = serializeSOAData(sd);
292 }
293
01d2e77a 294 if(rr.qtype.getCode() == QType::URL || rr.qtype.getCode() == QType::MBOXFW) {
8c949c52 295 cout<<"[Error] The recordtype "<<rr.qtype.getName()<<" for record '"<<rr.qname<<"' is no longer supported."<<endl;
01d2e77a 296 numerrors++;
0ae6a6cc 297 continue;
01d2e77a
PD
298 }
299
8c949c52
PD
300 if (rr.qname[rr.qname.size()-1] == '.') {
301 cout<<"[Error] Record '"<<rr.qname<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
302 numerrors++;
303 }
304
305
0ae6a6cc 306 if(rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
035297ad 307 rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
01d2e77a 308
8c949c52
PD
309 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX) &&
310 rr.content[rr.content.size()-1] == '.') {
311 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;
312 numwarnings++;
313 }
314
5e42374c
BH
315 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
316 rr.content = "\""+rr.content+"\"";
317
27045410 318 if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
7ddd79a7 319 {
8c949c52 320 cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
5e42374c 321 numerrors++;
7ddd79a7 322 }
035297ad
BH
323 try {
324 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
325 string tmp=drc->serialize(rr.qname);
5d2e58b0 326 }
035297ad
BH
327 catch(std::exception& e)
328 {
8c949c52
PD
329 cout<<"[Error] Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
330 cout<<"[Error] Error was: "<<e.what()<<endl;
035297ad
BH
331 numerrors++;
332 }
333 numrecords++;
334 }
8c949c52 335 cout<<"Checked "<<numrecords<<" records of '"<<zone<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
0ae6a6cc 336 return numerrors;
5d2e58b0
BH
337}
338
9d335015 339int checkAllZones()
1325e8a2
PD
340{
341 scoped_ptr<UeberBackend> B(new UeberBackend("default"));
342 vector<DomainInfo> domainInfo;
343
344 B->getAllDomains(&domainInfo);
345 int errors=0;
346 BOOST_FOREACH(DomainInfo di, domainInfo) {
9d335015 347 if (checkZone(B.get(), di.zone) > 0)
1325e8a2 348 errors++;
1325e8a2
PD
349 }
350 cout<<"Checked "<<domainInfo.size()<<" zones, "<<errors<<" had errors."<<endl;
351 return 0;
352}
353
cbb0025b
PD
354void testAlgorithm(int algo)
355{
356 DNSCryptoKeyEngine::testOne(algo);
357}
1325e8a2 358
189bb9d2
BH
359void testAlgorithms()
360{
361 DNSCryptoKeyEngine::testAll();
362}
363
49449751 364void testSpeed(DNSSECKeeper& dk, const string& zone, const string& remote, int cores)
ea937fd4
BH
365{
366 DNSResourceRecord rr;
367 rr.qname="blah."+zone;
368 rr.qtype=QType::A;
369 rr.ttl=3600;
370 rr.auth=1;
371 rr.qclass = 1;
372 rr.d_place=DNSResourceRecord::ANSWER;
373 rr.priority=0;
374
49449751 375 UeberBackend db("key-only");
ea937fd4 376
f0c4b9d5
PD
377 if ( ! db.backends.size() )
378 {
379 throw runtime_error("No backends available for DNSSEC key storage");
380 }
381
49449751 382 ChunkedSigningPipe csp(zone, 1, remote, cores);
ea937fd4
BH
383
384 vector<DNSResourceRecord> signatures;
385 uint32_t rnd;
386 unsigned char* octets = (unsigned char*)&rnd;
387 char tmp[25];
388 DTime dt;
389 dt.set();
390 for(unsigned int n=0; n < 100000; ++n) {
391 rnd = random();
392 snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
393 octets[0], octets[1], octets[2], octets[3]);
394 rr.content=tmp;
395
396 snprintf(tmp, sizeof(tmp), "r-%u", rnd);
397 rr.qname=string(tmp)+"."+zone;
398
399 if(csp.submit(rr))
400 while(signatures = csp.getChunk(), !signatures.empty())
401 ;
402 }
403 cerr<<"Flushing the pipe, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
404 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiffNoReset()/1000000.0) << " sigs/s\n";
405 while(signatures = csp.getChunk(true), !signatures.empty())
406 ;
407 cerr<<"Done, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
408 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s\n";
409}
410
aa65a832
BH
411void verifyCrypto(const string& zone)
412{
413 ZoneParserTNG zpt(zone);
414 DNSResourceRecord rr;
415 DNSKEYRecordContent drc;
416 RRSIGRecordContent rrc;
c3e26094 417 DSRecordContent dsrc;
aa65a832 418 vector<shared_ptr<DNSRecordContent> > toSign;
c3e26094
BH
419 string qname, apex;
420 dsrc.d_digesttype=0;
aa65a832
BH
421 while(zpt.get(rr)) {
422 if(rr.qtype.getCode() == QType::DNSKEY) {
423 cerr<<"got DNSKEY!"<<endl;
c3e26094 424 apex=rr.qname;
aa65a832
BH
425 drc = *dynamic_cast<DNSKEYRecordContent*>(DNSRecordContent::mastermake(QType::DNSKEY, 1, rr.content));
426 }
427 else if(rr.qtype.getCode() == QType::RRSIG) {
428 cerr<<"got RRSIG"<<endl;
429 rrc = *dynamic_cast<RRSIGRecordContent*>(DNSRecordContent::mastermake(QType::RRSIG, 1, rr.content));
430 }
c3e26094
BH
431 else if(rr.qtype.getCode() == QType::DS) {
432 cerr<<"got DS"<<endl;
433 dsrc = *dynamic_cast<DSRecordContent*>(DNSRecordContent::mastermake(QType::DS, 1, rr.content));
434 }
aa65a832
BH
435 else {
436 qname = rr.qname;
aa65a832
BH
437 toSign.push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
438 }
439 }
c3e26094 440
f309dacd 441 string msg = getMessageForRRSET(qname, rrc, toSign);
49449751 442 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
c3e26094
BH
443 if(dsrc.d_digesttype) {
444 cerr<<"Calculated DS: "<<apex<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
445 cerr<<"Original DS: "<<apex<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
446 }
295812a1 447#if 0
8d9f38f2 448 DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
295812a1
BH
449 "Algorithm: 12 (ECC-GOST)\n"
450 "GostAsn1: MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQg/9MiXtXKg9FDXDN/R9CmVhJDyuzRAIgh4tPwCu4NHIs=\n");
451 string resign=key->sign(hash);
452 cerr<<Base64Encode(resign)<<endl;
8d9f38f2 453 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(hash, resign)<<endl;
295812a1
BH
454#endif
455
aa65a832 456}
5935cede
BH
457void disableDNSSECOnZone(DNSSECKeeper& dk, const string& zone)
458{
459 if(!dk.isSecuredZone(zone)) {
460 cerr<<"Zone is not secured\n";
461 return;
462 }
463 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
aa65a832 464
5935cede
BH
465 if(keyset.empty()) {
466 cerr << "No keys for zone '"<<zone<<"'."<<endl;
467 }
468 else {
469 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
470 dk.deactivateKey(zone, value.second.id);
37fd8771 471 dk.removeKey(zone, value.second.id);
5935cede
BH
472 }
473 }
474 dk.unsetNSEC3PARAM(zone);
475 dk.unsetPresigned(zone);
476}
ade1b1e9
BH
477void showZone(DNSSECKeeper& dk, const std::string& zone)
478{
d3e7090c 479 if(!dk.isSecuredZone(zone)) {
d4a4176d 480 cerr<<"Zone is not actively secured\n";
d3e7090c 481 }
ade1b1e9
BH
482 NSEC3PARAMRecordContent ns3pr;
483 bool narrow;
3c873e66 484 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
ade1b1e9 485
d4a4176d 486 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
ade1b1e9 487
d3e7090c 488
d4a4176d 489 cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned\n";
ade1b1e9
BH
490
491 if(keyset.empty()) {
492 cerr << "No keys for zone '"<<zone<<"'."<<endl;
493 }
494 else {
d4a4176d
BH
495 if(!haveNSEC3)
496 cout<<"Zone has NSEC semantics"<<endl;
497 else
498 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
499
ade1b1e9
BH
500 cout << "keys: "<<endl;
501 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
502 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
aa65a832 503 cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< endl;
ade1b1e9
BH
504 if(value.second.keyOrZone) {
505 cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
506 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
224778b0 507 cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl;
608d776f
BH
508 try {
509 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
510 cout<<"DS = "<<zone<<" IN DS "<< output << endl;
511 }
512 catch(...)
513 {
514 }
515 cout<<endl;
ade1b1e9
BH
516 }
517 }
518 }
519}
5d2e58b0 520
d6c16697 521bool secureZone(DNSSECKeeper& dk, const std::string& zone)
ddac7145 522{
f60f4bcd
BH
523 if(dk.isSecuredZone(zone)) {
524 cerr << "Zone '"<<zone<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
525 return false;
526 }
527
2402c558
BH
528 DomainInfo di;
529 UeberBackend B("default");
530 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
531 cout<<"Can't find a zone called '"<<zone<<"'"<<endl;
532 return false;
533 }
534
3bf07122
PD
535 if(di.kind == DomainInfo::Slave)
536 {
537 cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
538 cout<<"pdnssec disable-dnssec "<<zone<<" right now!"<<endl;
539 }
540
f60f4bcd
BH
541 if(!dk.secureZone(zone, 8)) {
542 cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC\n";
543 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled.\n";
21fb9631
BH
544 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or\n";
545 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!\n";
f60f4bcd
BH
546 return false;
547 }
548
549 if(!dk.isSecuredZone(zone)) {
550 cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set \n";
0c84faf7 551 cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first.\n";
21fb9631
BH
552 cerr<<"If you run with the BIND backend, make sure you have configured\n";
553 cerr<<"it to use DNSSEC with 'bind-dnssec-db' and 'pdnssec create-bind-db'!\n";
f60f4bcd
BH
554 return false;
555 }
556
557 DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
558
559 if(!zskset.empty()) {
560 cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
561 return false;
562 }
563
564 dk.addKey(zone, false, 8);
565 dk.addKey(zone, false, 8, 0, false); // not active
566 // rectifyZone(dk, zone);
567 // showZone(dk, zone);
568 cout<<"Zone "<<zone<<" secured"<<endl;
d6c16697 569 return true;
ddac7145
BH
570}
571
46d2e0d6
PD
572void testSchema(DNSSECKeeper& dk, const std::string& zone)
573{
574 cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
575 cout<<"Please clean up after this."<<endl;
576 cout<<endl;
577 cout<<"Constructing UeberBackend"<<endl;
2402c558 578 UeberBackend B("default");
46d2e0d6 579 cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
2402c558 580 DNSBackend *db = B.backends[0];
46d2e0d6
PD
581 cout<<"Creating slave domain "<<zone<<endl;
582 db->createSlaveDomain("127.0.0.1", zone, "_testschema");
583 cout<<"Slave domain created"<<endl;
584
585 DomainInfo di;
2402c558 586 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
46d2e0d6
PD
587 cout<<"Can't find domain we just created, aborting"<<endl;
588 return;
589 }
590 db=di.backend;
591 DNSResourceRecord rr, rrget;
592 cout<<"Starting transaction to feed records"<<endl;
593 db->startTransaction(zone, di.id);
594
595 rr.qtype=QType::SOA;
596 rr.qname=zone;
597 rr.ttl=86400;
598 rr.domain_id=di.id;
599 rr.auth=1;
600 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
601 cout<<"Feeding SOA"<<endl;
602 db->feedRecord(rr);
603 rr.qtype=QType::TXT;
604 // 300 As
605 rr.content="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
606 cout<<"Feeding overlong TXT"<<endl;
607 db->feedRecord(rr);
608 cout<<"Committing"<<endl;
609 db->commitTransaction();
610 cout<<"Querying TXT"<<endl;
611 db->lookup(QType(QType::TXT), zone, NULL, di.id);
612 if(db->get(rrget))
613 {
614 DNSResourceRecord rrthrowaway;
615 if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
616 {
617 cout<<"Expected one record, got multiple, aborting"<<endl;
618 return;
619 }
620 int size=rrget.content.size();
621 if(size != 302)
622 {
623 cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
624 return;
625 }
626 }
627 cout<<"[+] content field is over 255 bytes"<<endl;
628
629 cout<<"Dropping all records, inserting SOA+2xA"<<endl;
630 db->startTransaction(zone, di.id);
631
632 rr.qtype=QType::SOA;
633 rr.qname=zone;
634 rr.ttl=86400;
635 rr.domain_id=di.id;
636 rr.auth=1;
637 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
638 cout<<"Feeding SOA"<<endl;
639 db->feedRecord(rr);
640
641 rr.qtype=QType::A;
642 rr.qname="_underscore."+zone;
643 rr.content="127.0.0.1";
644 db->feedRecord(rr);
645
646 rr.qname="bla."+zone;
647 cout<<"Committing"<<endl;
648 db->commitTransaction();
649
650 cout<<"Securing zone"<<endl;
651 secureZone(dk, zone);
652 cout<<"Rectifying zone"<<endl;
653 rectifyZone(dk, zone);
654 cout<<"Checking underscore ordering"<<endl;
655 string before, after;
656 db->getBeforeAndAfterNames(di.id, zone, "z."+zone, before, after);
657 cout<<"got '"<<before<<"' < 'z."<<zone<<"' < '"<<after<<"'"<<endl;
658 if(before != "_underscore."+zone)
659 {
660 cout<<"before is wrong, got '"<<before<<"', expected '_underscore."<<zone<<"', aborting"<<endl;
661 return;
662 }
663 if(after != zone)
664 {
665 cout<<"after is wrong, got '"<<after<<"', expected '"<<zone<<"', aborting"<<endl;
666 return;
667 }
668 cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
669 cout<<endl;
670 cout<<"End of tests, please remove "<<zone<<" from domains+records"<<endl;
671}
672
1d211b1b 673int main(int argc, char** argv)
20002664 674try
2fa33bce 675{
1d211b1b
BH
676 po::options_description desc("Allowed options");
677 desc.add_options()
678 ("help,h", "produce help message")
b3ce3dec 679 ("verbose,v", "be verbose")
1d211b1b 680 ("force", "force an action")
aa952078 681 ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
7d9dcde0 682 ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
1d211b1b
BH
683 ("commands", po::value<vector<string> >());
684
685 po::positional_options_description p;
686 p.add("commands", -1);
687 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
688 po::notify(g_vm);
689
690 vector<string> cmds;
691
692 if(g_vm.count("commands"))
693 cmds = g_vm["commands"].as<vector<string> >();
694
b3ce3dec
BH
695 g_verbose = g_vm.count("verbose");
696
1d211b1b 697 if(cmds.empty() || g_vm.count("help")) {
e1489e74
PD
698 cerr<<"Usage: \npdnssec [options] <command> [params ..]\n\n";
699 cerr<<"Commands:\n";
8a1f08e6 700 cerr<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE\n";
52690ba5 701 cerr<<"add-zone-key ZONE zsk|ksk [bits]\n";
e1489e74 702 cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]\n";
3aa7e8c1
PD
703 cerr<<" Add a ZSK or KSK to zone and specify algo&bits\n";
704 cerr<<"check-zone ZONE Check a zone for correctness\n";
705 cerr<<"check-all-zones Check all zones for correctness\n";
706 cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)\n";
707 cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE\n";
708 cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE\n";
709 cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described\n";
710 cerr<<"export-zone-key ZONE KEY-ID Export to stdout the private key described\n";
711 cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE\n";
712 cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK\n";
713 cerr<<" [ksk|zsk] Defaults to KSK\n";
714 cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)\n";
715 cerr<<"rectify-all-zones Rectify all zones.\n";
716 cerr<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE\n";
52690ba5 717 cerr<<"secure-zone ZONE [ZONE ..] Add KSK and two ZSKs\n";
3aa7e8c1
PD
718 cerr<<"set-nsec3 ZONE ['params' [narrow]] Enable NSEC3 with PARAMs. Optionally narrow\n";
719 cerr<<"set-presigned ZONE Use presigned RRSIGs from storage\n";
720 cerr<<"show-zone ZONE Show DNSSEC (public) key details about a zone\n";
721 cerr<<"unset-nsec3 ZONE Switch back to NSEC\n";
46d2e0d6
PD
722 cerr<<"unset-presigned ZONE No longer use presigned RRSIGs\n";
723 cerr<<"test-schema ZONE Test DB schema - will create ZONE\n\n";
7d9dcde0 724 cerr<<"Options:"<<endl;
1d211b1b
BH
725 cerr<<desc<<endl;
726 return 0;
727 }
a7e0acd8 728
cbb0025b 729 if (cmds[0] == "test-algorithm") {
2551cc18
PD
730 if(cmds.size() != 2) {
731 cerr << "Syntax: pdnssec test-algorithm algonum"<<endl;
732 return 0;
733 }
cbb0025b
PD
734 testAlgorithm(lexical_cast<int>(cmds[1]));
735 return 0;
736 }
737
ea937fd4
BH
738 if(cmds[0] == "test-algorithms") {
739 testAlgorithms();
740 return 0;
741 }
742
7d9dcde0 743 loadMainConfig(g_vm["config-dir"].as<string>());
6dfa0aa0 744 reportAllTypes();
fbe72b7a
BH
745
746
2717b8b3 747 if(cmds[0] == "create-bind-db") {
fbe72b7a
BH
748 if(cmds.size() != 2) {
749 cerr << "Syntax: pdnssec create-bind-db fname"<<endl;
750 return 0;
751 }
080f5d57
PD
752 try {
753 Bind2Backend::createDNSSECDB(cmds[1]);
754 }
755 catch (AhuException& ae) {
756 cerr<<"Error: "<<ae.reason<<endl;
757 return 1;
758 }
759 return 0;
2717b8b3 760 }
fbe72b7a
BH
761
762 DNSSECKeeper dk;
763
46d2e0d6
PD
764 if (cmds[0] == "test-schema") {
765 if(cmds.size() != 2) {
766 cerr << "Syntax: pdnssec test-schema ZONE"<<endl;
767 return 0;
768 }
769 testSchema(dk, cmds[1]);
770 return 0;
771 }
fbe72b7a 772 if(cmds[0] == "rectify-zone") {
d4904322
BH
773 if(cmds.size() < 2) {
774 cerr << "Syntax: pdnssec rectify-zone ZONE [ZONE..]"<<endl;
81b39e4b
BH
775 return 0;
776 }
d4904322
BH
777 for(unsigned int n = 1; n < cmds.size(); ++n)
778 rectifyZone(dk, cmds[n]);
20002664 779 }
1325e8a2
PD
780 else if (cmds[0] == "rectify-all-zones") {
781 rectifyAllZones(dk);
782 }
9abd98d3 783 else if(cmds[0] == "check-zone") {
5d2e58b0 784 if(cmds.size() != 2) {
37fd8771 785 cerr << "Syntax: pdnssec check-zone ZONE"<<endl;
5d2e58b0
BH
786 return 0;
787 }
9d335015
PD
788 scoped_ptr<UeberBackend> B(new UeberBackend("default"));
789 exit(checkZone(B.get(), cmds[1]));
5d2e58b0 790 }
1325e8a2 791 else if (cmds[0] == "check-all-zones") {
9d335015 792 exit(checkAllZones());
1325e8a2 793 }
81e9ba89
PD
794 else if (cmds[0] == "test-zone") {
795 cerr << "Did you mean check-zone?"<<endl;
796 return 0;
797 }
798 else if (cmds[0] == "test-all-zones") {
799 cerr << "Did you mean check-all-zones?"<<endl;
800 return 0;
801 }
49449751
BH
802#if 0
803 else if(cmds[0] == "signing-server" )
804 {
805 signingServer();
806 }
807 else if(cmds[0] == "signing-slave")
808 {
809 launchSigningService(0);
810 }
811#endif
ea937fd4 812 else if(cmds[0] == "test-speed") {
37fd8771
BH
813 if(cmds.size() < 2) {
814 cerr << "Syntax: pdnssec test-speed numcores [signing-server]"<<endl;
ea937fd4
BH
815 return 0;
816 }
49449751 817 testSpeed(dk, cmds[1], (cmds.size() > 3) ? cmds[3] : "", atoi(cmds[2].c_str()));
189bb9d2 818 }
aa65a832
BH
819 else if(cmds[0] == "verify-crypto") {
820 if(cmds.size() != 2) {
37fd8771 821 cerr << "Syntax: pdnssec verify-crypto FILE"<<endl;
aa65a832
BH
822 return 0;
823 }
824 verifyCrypto(cmds[1]);
825 }
da11ed0e
BH
826
827 else if(cmds[0] == "show-zone") {
a0472099 828 if(cmds.size() != 2) {
37fd8771 829 cerr << "Syntax: pdnssec show-zone ZONE"<<endl;
a0472099
BH
830 return 0;
831 }
1d211b1b 832 const string& zone=cmds[1];
ade1b1e9 833 showZone(dk, zone);
1d211b1b 834 }
5935cede
BH
835 else if(cmds[0] == "disable-dnssec") {
836 if(cmds.size() != 2) {
37fd8771 837 cerr << "Syntax: pdnssec disable-dnssec ZONE"<<endl;
5935cede
BH
838 return 0;
839 }
840 const string& zone=cmds[1];
841 disableDNSSECOnZone(dk, zone);
842 }
bed962b5 843 else if(cmds[0] == "activate-zone-key") {
37fd8771
BH
844 if(cmds.size() != 3) {
845 cerr << "Syntax: pdnssec activate-zone-key ZONE KEY-ID"<<endl;
846 return 0;
847 }
bed962b5
BH
848 const string& zone=cmds[1];
849 unsigned int id=atoi(cmds[2].c_str());
77b909cc
PD
850 if(!id)
851 {
852 cerr<<"Invalid KEY-ID"<<endl;
853 return 1;
854 }
a84a8203
PD
855 if (!dk.activateKey(zone, id)) {
856 cerr<<"Activation of key failed"<<endl;
857 return 1;
858 }
859 return 0;
bed962b5
BH
860 }
861 else if(cmds[0] == "deactivate-zone-key") {
37fd8771
BH
862 if(cmds.size() != 3) {
863 cerr << "Syntax: pdnssec deactivate-zone-key ZONE KEY-ID"<<endl;
864 return 0;
865 }
bed962b5
BH
866 const string& zone=cmds[1];
867 unsigned int id=atoi(cmds[2].c_str());
77b909cc
PD
868 if(!id)
869 {
870 cerr<<"Invalid KEY-ID"<<endl;
871 return 1;
872 }
a84a8203
PD
873 if (!dk.deactivateKey(zone, id)) {
874 cerr<<"Deactivation of key failed"<<endl;
875 return 1;
876 }
877 return 0;
bed962b5
BH
878 }
879 else if(cmds[0] == "add-zone-key") {
37fd8771 880 if(cmds.size() < 3 ) {
52690ba5 881 cerr << "Syntax: pdnssec add-zone-key ZONE zsk|ksk [bits] [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
37fd8771
BH
882 return 0;
883 }
bed962b5 884 const string& zone=cmds[1];
36c394e5
BH
885 // need to get algorithm, bits & ksk or zsk from commandline
886 bool keyOrZone=false;
887 int bits=0;
0ba8e0f1 888 int algorithm=8;
36c394e5
BH
889 for(unsigned int n=2; n < cmds.size(); ++n) {
890 if(pdns_iequals(cmds[n], "zsk"))
891 keyOrZone = false;
892 else if(pdns_iequals(cmds[n], "ksk"))
893 keyOrZone = true;
a254438f
BH
894 else if(pdns_iequals(cmds[n], "rsasha1"))
895 algorithm=5;
896 else if(pdns_iequals(cmds[n], "rsasha256"))
897 algorithm=8;
f7982887 898 else if(pdns_iequals(cmds[n], "rsasha512"))
022e5e0b 899 algorithm=10;
f7982887
BH
900 else if(pdns_iequals(cmds[n], "gost"))
901 algorithm=12;
022e5e0b
BH
902 else if(pdns_iequals(cmds[n], "ecdsa256"))
903 algorithm=13;
904 else if(pdns_iequals(cmds[n], "ecdsa384"))
905 algorithm=14;
59d84dc2
BH
906 else if(pdns_iequals(cmds[n], "ed25519"))
907 algorithm=250;
36c394e5
BH
908 else if(atoi(cmds[n].c_str()))
909 bits = atoi(cmds[n].c_str());
910 else {
a254438f 911 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
8296003a 912 return 0;
36c394e5
BH
913 }
914 }
a254438f 915 cerr<<"Adding a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
36c394e5
BH
916 if(bits)
917 cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
37fd8771 918 dk.addKey(zone, keyOrZone, algorithm, bits, false);
bed962b5
BH
919 }
920 else if(cmds[0] == "remove-zone-key") {
471304cc 921 if(cmds.size() < 3) {
48cb6e2a 922 cerr<<"Syntax: pdnssec remove-zone-key ZONE KEY-ID"<<endl;
471304cc
BH
923 return 0;
924 }
bed962b5
BH
925 const string& zone=cmds[1];
926 unsigned int id=atoi(cmds[2].c_str());
a84a8203
PD
927 if (!dk.removeKey(zone, id)) {
928 return 1;
929 }
930 return 0;
bed962b5
BH
931 }
932
c3c89361 933 else if(cmds[0] == "secure-zone") {
ddac7145 934 if(cmds.size() < 2) {
37fd8771 935 cerr << "Syntax: pdnssec secure-zone ZONE"<<endl;
a9175ad6
BH
936 return 0;
937 }
d6c16697 938 vector<string> mustRectify;
ddac7145 939 dk.startTransaction();
1325e8a2 940 unsigned int zoneErrors=0;
ddac7145 941 for(unsigned int n = 1; n < cmds.size(); ++n) {
f60f4bcd
BH
942 const string& zone=cmds[n];
943 if(secureZone(dk, zone)) {
d6c16697 944 mustRectify.push_back(zone);
1325e8a2
PD
945 } else {
946 zoneErrors++;
d6c16697 947 }
f60f4bcd 948 }
1d211b1b 949
ddac7145 950 dk.commitTransaction();
d6c16697
BH
951 BOOST_FOREACH(string& zone, mustRectify)
952 rectifyZone(dk, zone);
1325e8a2
PD
953
954 if (zoneErrors) {
955 return 1;
956 }
957 return 0;
1d211b1b 958 }
da11ed0e 959 else if(cmds[0]=="set-nsec3") {
37fd8771
BH
960 if(cmds.size() < 2) {
961 cerr<<"Syntax: pdnssec set-nsec3 ZONE 'params' [narrow]"<<endl;
962 return 0;
963 }
b9dba5c1 964 string nsec3params = cmds.size() > 2 ? cmds[2] : "1 1 1 ab";
22c5aa60 965 bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
da11ed0e 966 NSEC3PARAMRecordContent ns3pr(nsec3params);
b9dba5c1
BH
967 if(!ns3pr.d_flags) {
968 cerr<<"PowerDNS only implements opt-out zones, please set the second parameter to '1' (example, '1 1 1 ab')"<<endl;
969 return 0;
970 }
971
22c5aa60 972 dk.setNSEC3PARAM(cmds[1], ns3pr, narrow);
5fce0f3d 973 cerr<<"NSEC3 set, please rectify-zone if your backend needs it"<<endl;
da11ed0e 974 }
d3e7090c 975 else if(cmds[0]=="set-presigned") {
5935cede 976 if(cmds.size() < 2) {
37fd8771
BH
977 cerr<<"Syntax: pdnssec set-presigned ZONE"<<endl;
978 return 0;
5935cede 979 }
a84a8203
PD
980 if (! dk.setPresigned(cmds[1])) {
981 return 1;
982 }
983 return 0;
d3e7090c
BH
984 }
985 else if(cmds[0]=="unset-presigned") {
37fd8771
BH
986 if(cmds.size() < 2) {
987 cerr<<"Syntax: pdnssec unset-presigned ZONE"<<endl;
f60f4bcd 988 return 0;
37fd8771 989 }
a84a8203
PD
990 if (! dk.unsetPresigned(cmds[1])) {
991 return 1;
992 }
993 return 0;
d3e7090c 994 }
65c87942
BH
995 else if(cmds[0]=="hash-zone-record") {
996 if(cmds.size() < 3) {
37fd8771 997 cerr<<"Syntax: pdnssec hash-zone-record ZONE RNAME"<<endl;
65c87942
BH
998 return 0;
999 }
1000 string& zone=cmds[1];
1001 string& record=cmds[2];
1002 NSEC3PARAMRecordContent ns3pr;
1003 bool narrow;
1004 if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
1005 cerr<<"The '"<<zone<<"' zone does not use NSEC3"<<endl;
1006 return 0;
1007 }
5e42374c 1008 if(narrow) {
65c87942
BH
1009 cerr<<"The '"<<zone<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
1010 }
1011
1012 cout<<toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, record)))<<endl;
1013 }
da11ed0e 1014 else if(cmds[0]=="unset-nsec3") {
37fd8771
BH
1015 if(cmds.size() < 2) {
1016 cerr<<"Syntax: pdnssec unset-nsec3 ZONE"<<endl;
a84a8203 1017 return 0;
37fd8771 1018 }
a84a8203
PD
1019 if ( ! dk.unsetNSEC3PARAM(cmds[1])) {
1020 return 1;
1021 }
1022 return 0;
da11ed0e
BH
1023 }
1024 else if(cmds[0]=="export-zone-key") {
7ddd79a7 1025 if(cmds.size() < 3) {
37fd8771 1026 cerr<<"Syntax: pdnssec export-zone-key ZONE KEY-ID"<<endl;
a84a8203 1027 return 0;
7ddd79a7
BH
1028 }
1029
da11ed0e
BH
1030 string zone=cmds[1];
1031 unsigned int id=atoi(cmds[2].c_str());
1032 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
189bb9d2 1033 cout << dpk.getKey()->convertToISC() <<endl;
4496f66f 1034 }
ed3f8559
BH
1035 else if(cmds[0]=="import-zone-key-pem") {
1036 if(cmds.size() < 4) {
37fd8771 1037 cerr<<"Syntax: pdnssec import-zone-key ZONE FILE algorithm [zsk|ksk]"<<endl;
ed3f8559
BH
1038 exit(1);
1039 }
1040 string zone=cmds[1];
1041 string fname=cmds[2];
1042 string line;
1043 ifstream ifs(fname.c_str());
1044 string tmp, interim, raw;
1045 while(getline(ifs, line)) {
1046 if(line[0]=='-')
1047 continue;
1048 trim(line);
1049 interim += line;
1050 }
1051 B64Decode(interim, raw);
1052 DNSSECPrivateKey dpk;
699e6e37 1053 DNSKEYRecordContent drc;
8d9f38f2 1054 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromPEMString(drc, raw));
699e6e37 1055 dpk.setKey(key);
ed3f8559
BH
1056
1057 dpk.d_algorithm = atoi(cmds[3].c_str());
1058
1059 if(dpk.d_algorithm == 7)
1060 dpk.d_algorithm = 5;
1061
1062 cerr<<(int)dpk.d_algorithm<<endl;
1063
1064 if(cmds.size() > 4) {
1065 if(pdns_iequals(cmds[4], "ZSK"))
1066 dpk.d_flags = 256;
1067 else if(pdns_iequals(cmds[4], "KSK"))
1068 dpk.d_flags = 257;
1069 else {
1070 cerr<<"Unknown key flag '"<<cmds[4]<<"'\n";
1071 exit(1);
1072 }
1073 }
1074 else
1075 dpk.d_flags = 257; // ksk
1076
1077 dk.addKey(zone, dpk);
1078
1079 }
976b6541 1080 else if(cmds[0]=="import-zone-key") {
37fd8771
BH
1081 if(cmds.size() < 4) {
1082 cerr<<"Syntax: pdnssec import-zone-key ZONE FILE [zsk|ksk]"<<endl;
4496f66f
BH
1083 exit(1);
1084 }
976b6541
BH
1085 string zone=cmds[1];
1086 string fname=cmds[2];
1087 DNSSECPrivateKey dpk;
699e6e37 1088 DNSKEYRecordContent drc;
8d9f38f2 1089 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromISCFile(drc, fname.c_str()));
699e6e37 1090 dpk.setKey(key);
7ddd79a7
BH
1091 dpk.d_algorithm = drc.d_algorithm;
1092
1093 if(dpk.d_algorithm == 7)
1094 dpk.d_algorithm = 5;
1095
1096 cerr<<(int)dpk.d_algorithm<<endl;
aa952078
BH
1097
1098 if(cmds.size() > 3) {
1099 if(pdns_iequals(cmds[3], "ZSK"))
1100 dpk.d_flags = 256;
1101 else if(pdns_iequals(cmds[3], "KSK"))
1102 dpk.d_flags = 257;
1103 else {
1104 cerr<<"Unknown key flag '"<<cmds[3]<<"'\n";
1105 exit(1);
1106 }
1107 }
1108 else
1109 dpk.d_flags = 257;
1110
7ddd79a7 1111 dk.addKey(zone, dpk);
976b6541 1112 }
da11ed0e 1113 else if(cmds[0]=="export-zone-dnskey") {
7ddd79a7 1114 if(cmds.size() < 3) {
37fd8771 1115 cerr<<"Syntax: pdnssec export-zone-dnskey ZONE KEY-ID"<<endl;
7ddd79a7
BH
1116 exit(1);
1117 }
1118
da11ed0e
BH
1119 string zone=cmds[1];
1120 unsigned int id=atoi(cmds[2].c_str());
1121 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1122 cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
f8b25763
BH
1123 if(dpk.d_flags == 257) {
1124 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
1125 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
1126 }
da11ed0e 1127 }
1d211b1b
BH
1128 else {
1129 cerr<<"Unknown command '"<<cmds[0]<<"'\n";
1130 return 1;
1131 }
1132 return 0;
1133}
20002664
BH
1134catch(AhuException& ae) {
1135 cerr<<"Error: "<<ae.reason<<endl;
1136}
14133ba3
BH
1137catch(std::exception& e) {
1138 cerr<<"Error: "<<e.what()<<endl;
1139}
46d2e0d6 1140