]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/pdnsutil.cc
Merge pull request #3169 from zeha/createzonesoa
[thirdparty/pdns.git] / pdns / pdnsutil.cc
index 7b46c5d9ad9fdf4c61fc63c43e0fa84d7f1a7c33..02abec4a7ab96d92ee6e6fb2cf7930ee492d02a5 100644 (file)
@@ -21,6 +21,9 @@
 #ifdef HAVE_LIBSODIUM
 #include <sodium.h>
 #endif
+#ifdef HAVE_OPENSSL
+#include "opensslsigners.hh"
+#endif
 #ifdef HAVE_SQLITE3
 #include "ssqlite3.hh"
 #include "bind-dnssec.schema.sqlite3.sql.h"
@@ -136,9 +139,9 @@ void loadMainConfig(const std::string& configdir)
   string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
   cleanSlashes(configname);
 
-  ::arg().set("default-ksk-algorithms","Default KSK algorithms")="rsasha256";
+  ::arg().set("default-ksk-algorithms","Default KSK algorithms")="";
   ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
-  ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="rsasha256";
+  ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="ecdsa256";
   ::arg().set("default-zsk-size","Default ZSK size (0 means default)")="0";
   ::arg().set("default-soa-edit","Default SOA-EDIT value")="";
   ::arg().set("default-soa-edit-signed","Default SOA-EDIT value for signed zones")="";
@@ -420,7 +423,7 @@ int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
 
   if (haveNSEC3 && isSecure && zone.wirelength() > 222) {
     numerrors++;
-    cerr<<"[Error] zone '" << zone.toStringNoDot() << "' has NSEC3 semantics but is too long to have the hash prepended. Zone name is " << zone.wirelength() << " bytes long, whereas the maximum is 222 bytes." << endl;
+    cout<<"[Error] zone '" << zone.toStringNoDot() << "' has NSEC3 semantics but is too long to have the hash prepended. Zone name is " << zone.wirelength() << " bytes long, whereas the maximum is 222 bytes." << endl;
   }
 
   // Check for delegation in parent zone
@@ -434,7 +437,7 @@ int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
       while(B.get(rr))
         ns |= (rr.qtype == QType::NS);
       if (!ns) {
-        cerr<<"[Error] No delegation for zone '"<<zone.toString()<<"' in parent '"<<parent.toString()<<"'"<<endl;
+        cout<<"[Error] No delegation for zone '"<<zone.toString()<<"' in parent '"<<parent.toString()<<"'"<<endl;
         numerrors++;
       }
       break;
@@ -450,7 +453,7 @@ int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
   ostringstream content;
   pair<map<string, unsigned int>::iterator,bool> ret;
 
-  sd.db->list(zone, sd.domain_id, true);
+  sd.db->list(zone, sd.domain_id, false);
 
   while(sd.db->get(rr)) {
     if(!rr.qtype.getCode())
@@ -651,7 +654,7 @@ int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
 
   for(const auto &qname : checkglue) {
     if (!glue.count(qname)) {
-      cerr<<"[Warning] Missing glue for '"<<qname.toString()<<"' in zone '"<<zone.toString()<<"'"<<endl;
+      cout<<"[Warning] Missing glue for '"<<qname.toString()<<"' in zone '"<<zone.toString()<<"'"<<endl;
       numwarnings++;
     }
   }
@@ -687,7 +690,7 @@ int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
   UeberBackend B("default");
   SOAData sd;
   if(!B.getSOAUncached(zone, sd)) {
-    cout<<"No SOA for zone '"<<zone.toString()<<"'"<<endl;
+    cerr<<"No SOA for zone '"<<zone.toString()<<"'"<<endl;
     return -1;
   }
 
@@ -930,6 +933,24 @@ int createZone(const DNSName &zone) {
     cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
     return 1;
   }
+
+  DNSResourceRecord rr;
+  rr.qname = zone;
+  rr.auth = 1;
+  rr.ttl = ::arg().asNum("default-ttl");
+  rr.qtype = "SOA";
+  string soa = (boost::format("%s %s 1")
+    % ::arg()["default-soa-name"]
+    % (::arg().isEmpty("default-soa-mail") ? (DNSName("hostmaster.") + zone).toString() : ::arg()["default-soa-mail"])
+  ).str();
+  SOAData sd;
+  fillSOAData(soa, sd);  // fills out default values for us
+  rr.content = DNSRecordContent::mastermake(rr.qtype.getCode(), 1, serializeSOAData(sd))->getZoneRepresentation(true);
+  rr.domain_id = di.id;
+  di.backend->startTransaction(zone, di.id);
+  di.backend->feedRecord(rr);
+  di.backend->commitTransaction();
+
   return 1;
 }
 
@@ -1129,9 +1150,59 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone)
      cerr << "Zone uses following TSIG key(s): " << boost::join(meta, ",") << endl;
   }
   
-  cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
+  if (dk.isPresigned(zone)) {
+    cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
+    // get us some keys
+    vector<DNSKEYRecordContent> keys;
+    DNSResourceRecord rr;
 
-  if(keyset.empty())  {
+    B.lookup(QType(QType::DNSKEY), DNSName(zone));
+    while(B.get(rr)) {
+      if (rr.qtype != QType::DNSKEY) continue;
+      keys.push_back(*dynamic_cast<DNSKEYRecordContent*>(DNSKEYRecordContent::make(rr.getZoneRepresentation())));
+    }
+
+    if(keys.empty()) {
+      cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
+      return true;
+    }
+
+    if(!haveNSEC3)
+      cout<<"Zone has NSEC semantics"<<endl;
+    else
+      cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
+    cout << "keys: "<<endl;
+    sort(keys.begin(),keys.end());
+    reverse(keys.begin(),keys.end());
+    bool shown=false;
+    for(const auto& key : keys) {
+      string algname;
+      algorithm2name(key.d_algorithm,algname);
+      int bits;
+      if (key.d_key[0] == 0)
+        bits = *(uint16_t*)(key.d_key.c_str()+1);
+      else
+        bits = *(uint8_t*)key.d_key.c_str();
+      bits = (key.d_key.size() - (bits+1))*8;
+      cout << (key.d_flags == 257 ? "KSK" : "ZSK") << ", tag = " << key.getTag() << ", algo = "<<(int)key.d_algorithm << ", bits = " << bits << endl;
+      cout << "DNSKEY = " <<zone.toString()<<" IN DNSKEY "<< key.getZoneRepresentation() << "; ( " + algname + " ) " <<endl;
+      if (shown) continue;
+      shown=true;
+      cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
+      cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
+      try {
+        cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 3).getZoneRepresentation() << " ; ( GOST R 34.11-94 digest )" << endl;
+      }
+      catch(...)
+      {}
+      try {
+        cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 4).getZoneRepresentation() << " ; ( SHA-384 digest )" << endl;
+      }
+      catch(...)
+      {}
+    }
+  }
+  else if(keyset.empty())  {
     cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
   }
   else {  
@@ -1149,10 +1220,10 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone)
         continue;
       }
       cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
-      cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< " ( " + algname + " ) "<<endl;
-      if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || g_verbose)
+      cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\t"<<((int)value.second.active == 1 ? "  A" : "Ina")<<"ctive ( " + algname + " ) "<<endl;
+      if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || 1)
         cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone.toString()<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( "  + algname + " )" << endl;
-      if(value.second.keyOrZone || g_verbose) {
+      if(value.second.keyOrZone || 1) {
         cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
         cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
         try {
@@ -1193,18 +1264,14 @@ bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
      throw runtime_error("KSK key size must be equal to or greater than 0");
   }
 
-  if (k_algos.size() < 1) {
-     throw runtime_error("No algorithm(s) given for KSK");
+  if (k_algos.size() < 1 && z_algos.size() < 1) {
+     throw runtime_error("Zero algorithms given for KSK+ZSK in total");
   }
 
   if (z_size < 0) {
      throw runtime_error("ZSK key size must be equal to or greater than 0");
   }
 
-  if (z_algos.size() < 1) {
-     throw runtime_error("No algorithm(s) given for ZSK");
-  }
-
   if(dk.isSecuredZone(zone)) {
     cerr << "Zone '"<<zone.toString()<<"' already secure, remove keys with pdnsutil remove-zone-key if needed"<<endl;
     return false;
@@ -1213,38 +1280,21 @@ bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
   DomainInfo di;
   UeberBackend B("default");
   if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
-    cout<<"Can't find a zone called '"<<zone.toString()<<"'"<<endl;
+    cerr<<"Can't find a zone called '"<<zone.toString()<<"'"<<endl;
     return false;
   }
 
   if(di.kind == DomainInfo::Slave)
   {
-    cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
-    cout<<"pdnsutil disable-dnssec "<<zone.toString()<<" right now!"<<endl;
+    cerr<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
+    cerr<<"pdnsutil disable-dnssec "<<zone.toString()<<" right now!"<<endl;
   }
 
   if (k_size)
-    cout << "Securing zone with " << k_algos[0] << " algorithm with key size " << k_size << endl;
+    cout << "Securing zone with key size " << k_size << endl;
   else
-    cout << "Securing zone with " << k_algos[0] << " algorithm with default key size" << endl;
-
-  // run secure-zone with first default algorith, then add keys
-  if(!dk.addKey(zone, true, shorthand2algorithm(k_algos[0]), k_size)) {
-    cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
-    cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
-    cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
-    cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
-    return false;
-  }
+    cout << "Securing zone with default key size" << endl;
 
-  if(!dk.isSecuredZone(zone)) {
-    cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl;
-    cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl;
-    cerr<<"If you run with the BIND backend, make sure you have configured"<<endl;
-    cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl;
-    cerr<<"'pdnsutil create-bind-db /path/fname'!"<<endl;
-    return false;
-  }
 
   DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
 
@@ -1252,14 +1302,43 @@ bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
     cerr<<"There were ZSKs already for zone '"<<zone.toString()<<"', no need to add more"<<endl;
     return false;
   }
-  
-  for(vector<string>::iterator i = k_algos.begin()+1; i != k_algos.end(); i++)
-    dk.addKey(zone, true, shorthand2algorithm(*i), k_size, true); // obvious errors will have been caught above
 
-  for(string z_algo :  z_algos)
+  for(auto &k_algo: k_algos) {
+    cout << "Adding KSK with algorithm " << k_algo << endl;
+
+    int algo = shorthand2algorithm(k_algo);
+
+    if(!dk.addKey(zone, true, algo, k_size, true)) {
+      cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
+      cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
+      cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
+      cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
+      return false;
+    }
+  }
+
+  for(auto &z_algo :  z_algos)
   {
+    cout << "Adding ZSK with algorithm " << z_algo << endl;
+
     int algo = shorthand2algorithm(z_algo);
-    dk.addKey(zone, false, algo, z_size);
+
+    if(!dk.addKey(zone, false, algo, z_size, true)) {
+      cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
+      cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
+      cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
+      cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
+      return false;
+    }
+  }
+
+  if(!dk.isSecuredZone(zone)) {
+    cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl;
+    cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl;
+    cerr<<"If you run with the BIND backend, make sure you have configured"<<endl;
+    cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl;
+    cerr<<"'pdnsutil create-bind-db /path/fname'!"<<endl;
+    return false;
   }
 
   // rectifyZone(dk, zone);
@@ -1399,7 +1478,7 @@ try
     cerr<<"activate-tsig-key ZONE NAME {master|slave}"<<endl;
     cerr<<"                                   Enable TSIG key for a zone"<<endl;
     cerr<<"activate-zone-key ZONE KEY-ID      Activate the key with key id KEY-ID in ZONE"<<endl;
-    cerr<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|passive]"<<endl;
+    cerr<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive]"<<endl;
     cerr<<"             [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384";
 #ifdef HAVE_LIBSODIUM
     cerr<<"|experimental-ed25519";
@@ -1436,7 +1515,7 @@ try
     cerr<<"increase-serial ZONE               Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
     cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
     cerr<<"import-zone-key ZONE FILE          Import from a file a private key, ZSK or KSK"<<endl;
-    cerr<<"       [active|passive] [ksk|zsk]  Defaults to KSK and active"<<endl;
+    cerr<<"       [active|inactive] [ksk|zsk]  Defaults to KSK and active"<<endl;
     cerr<<"load-zone ZONE FILE                Load ZONE from FILE, possibly creating zone or atomically"<<endl;
     cerr<<"                                   replacing contents"<<endl;
     cerr<<"list-keys [ZONE]                   List DNSSEC keys for ZONE. When ZONE is unset or \"all\", display all keys for all zones"<<endl;
@@ -1466,6 +1545,10 @@ try
     return 0;
   }
 
+loadMainConfig(g_vm["config-dir"].as<string>());
+
+seedRandom(::arg()["entropy-source"]);
+
 #ifdef HAVE_LIBSODIUM
   if (sodium_init() == -1) {
     cerr<<"Unable to initialize sodium crypto library"<<endl;
@@ -1473,6 +1556,10 @@ try
   }
 #endif
 
+#ifdef HAVE_OPENSSL
+  openssl_seed();
+#endif
+
   if (cmds[0] == "test-algorithm") {
     if(cmds.size() != 2) {
       cerr << "Syntax: pdnsutil test-algorithm algonum"<<endl;
@@ -1489,7 +1576,6 @@ try
     return 1;
   }
 
-  loadMainConfig(g_vm["config-dir"].as<string>());
   reportAllTypes();
 
   if(cmds[0] == "create-bind-db") {
@@ -1552,7 +1638,7 @@ try
     dbBench(cmds.size() > 1 ? cmds[1] : "");
   }
   else if (cmds[0] == "check-all-zones") {
-    bool exitOnError = (cmds[1] == "exit-on-error");
+    bool exitOnError = ((cmds.size() >= 2 ? cmds[1] : "") == "exit-on-error");
     exit(checkAllZones(dk, exitOnError));
   }
   else if (cmds[0] == "list-all-zones") {
@@ -1596,7 +1682,6 @@ try
     }
     verifyCrypto(cmds[1]);
   }
-
   else if(cmds[0] == "show-zone") {
     if(cmds.size() != 2) {
       cerr << "Syntax: pdnsutil show-zone ZONE"<<endl;
@@ -1681,7 +1766,7 @@ try
         algorithm = tmp_algo;
       } else if(pdns_iequals(cmds[n], "active")) {
         active=true;
-      } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) {
+      } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) { // 'passive' eventually needs to be removed
         active=false;
       } else if(pdns_stou(cmds[n])) {
         bits = pdns_stou(cmds[n]);
@@ -2015,7 +2100,7 @@ try
   }
   else if(cmds[0]=="import-zone-key") {
     if(cmds.size() < 3) {
-      cerr<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|passive]"<<endl;
+      cerr<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|inactive]"<<endl;
       exit(1);
     }
     string zone=cmds[1];
@@ -2039,7 +2124,7 @@ try
         dpk.d_flags = 257;
       else if(pdns_iequals(cmds[n], "active"))
         active = 1;
-      else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive"))
+      else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive")) // passive eventually needs to be removed
         active = 0;
       else { 
         cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
@@ -2146,7 +2231,6 @@ try
      }
 
      cerr << "Generating new key with " << klen << " bytes (this can take a while)" << endl;
-     seedRandom(::arg()["entropy-source"]);
      for(size_t i = 0; i < klen; i+=4) {
         *(unsigned int*)(tmpkey+i) = dns_random(0xffffffff);
      }
@@ -2156,7 +2240,7 @@ try
      if (B.setTSIGKey(name, DNSName(algo), key)) { // you are feeling bored, put up DNSName(algo) up earlier
        cout << "Create new TSIG key " << name << " " << algo << " " << key << endl;
      } else {
-       cout << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
+       cerr << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
        return 1;
      }
      return 0;
@@ -2173,7 +2257,7 @@ try
      if (B.setTSIGKey(name, DNSName(algo), key)) {
        cout << "Imported TSIG key " << name << " " << algo << endl;
      } else {
-       cout << "Failure importing TSIG key " << name << " " << algo << endl;
+       cerr << "Failure importing TSIG key " << name << " " << algo << endl;
        return 1;
      }
      return 0;
@@ -2188,7 +2272,7 @@ try
      if (B.deleteTSIGKey(name)) {
        cout << "Deleted TSIG key " << name << endl;
      } else {
-       cout << "Failure deleting TSIG key " << name << endl;
+       cerr << "Failure deleting TSIG key " << name << endl;
        return 1;
      }
      return 0;
@@ -2220,7 +2304,7 @@ try
      UeberBackend B("default");
      std::vector<std::string> meta; 
      if (!B.getDomainMetadata(zname, metaKey, meta)) {
-       cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
+       cerr << "Failure enabling TSIG key " << name << " for " << zname << endl;
        return 1;
      }
      bool found = false;
@@ -2231,7 +2315,7 @@ try
      if (B.setDomainMetadata(zname, metaKey, meta)) {
        cout << "Enabled TSIG key " << name << " for " << zname << endl;
      } else {
-       cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
+       cerr << "Failure enabling TSIG key " << name << " for " << zname << endl;
        return 1;
      }
      return 0;
@@ -2255,7 +2339,7 @@ try
      UeberBackend B("default");
      std::vector<std::string> meta;
      if (!B.getDomainMetadata(zname, metaKey, meta)) {
-       cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
+       cerr << "Failure disabling TSIG key " << name << " for " << zname << endl;
        return 1;
      }
      std::vector<std::string>::iterator iter = meta.begin();
@@ -2264,7 +2348,7 @@ try
      if (B.setDomainMetadata(zname, metaKey, meta)) {
        cout << "Disabled TSIG key " << name << " for " << zname << endl;
      } else {
-       cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
+       cerr << "Failure disabling TSIG key " << name << " for " << zname << endl;
        return 1;
      }
      return 0;