]> granicus.if.org Git - pdns/commitdiff
this implies that there will be more dnsseckeepers - database backed for example
authorBert Hubert <bert.hubert@netherlabs.nl>
Mon, 27 Dec 2010 15:57:14 +0000 (15:57 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Mon, 27 Dec 2010 15:57:14 +0000 (15:57 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1758 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/fsdnsseckeeper.cc [new file with mode: 0644]

diff --git a/pdns/fsdnsseckeeper.cc b/pdns/fsdnsseckeeper.cc
new file mode 100644 (file)
index 0000000..2a86f3b
--- /dev/null
@@ -0,0 +1,356 @@
+#include "dnsseckeeper.hh"
+#include "dnssecinfra.hh"
+#include "statbag.hh"
+#include <iostream>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <polarssl/havege.h>
+#include <polarssl/base64.h>
+#include <boost/foreach.hpp>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fstream>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/std/vector.hpp> // for 'operator+=()'
+#include <boost/assign/list_inserter.hpp>
+using namespace boost::assign;
+namespace fs = boost::filesystem;
+
+using namespace std;
+using namespace boost;
+
+void RSAContext::create(unsigned int bits)
+{
+  havege_state hs;
+  havege_init( &hs );
+  
+  rsa_init(&d_context, RSA_PKCS_V15, 0, havege_rand, &hs ); // FIXME this leaks memory
+  int ret=rsa_gen_key(&d_context, bits, 65537);
+  if(ret < 0) 
+    throw runtime_error("Key generation failed");
+}
+
+std::string RSAContext::convertToISC()
+{
+  string ret;
+  typedef vector<pair<string, mpi*> > outputs_t;
+  outputs_t outputs;
+  push_back(outputs)("Modulus", &d_context.N)("PublicExponent",&d_context.E)
+    ("Modulus", &d_context.N)
+    ("PublicExponent",&d_context.E)
+    ("PrivateExponent",&d_context.D)
+    ("Prime1",&d_context.P)
+    ("Prime2",&d_context.Q)
+    ("Exponent1",&d_context.DP)
+    ("Exponent2",&d_context.DQ)
+    ("Coefficient",&d_context.QP);
+
+  ret = "Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\n";
+
+  BOOST_FOREACH(outputs_t::value_type value, outputs) {
+    ret += value.first;
+    ret += ": ";
+    unsigned char tmp[mpi_size(value.second)];
+    mpi_write_binary(value.second, tmp, sizeof(tmp));
+    unsigned char base64tmp[sizeof(tmp)*2];
+    int dlen=sizeof(base64tmp);
+    base64_encode(base64tmp, &dlen, tmp, sizeof(tmp));
+    ret.append((const char*)base64tmp, dlen);
+    ret.append(1, '\n');
+  }
+  return ret;
+}
+
+bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk)
+{
+  keyset_t keys = getKeys(zone, true);
+  // need to get an *active* one!
+  if(dpk && !keys.empty()) {
+    *dpk = keys.begin()->first;
+  }
+  return !keys.empty();
+  
+  #if 0
+  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/keys/" ) );
+
+  if ( !fs::exists( full_path ) )
+    return false;
+
+  fs::directory_iterator end_iter;
+  for ( fs::directory_iterator dir_itr( full_path );
+       dir_itr != end_iter;
+       ++dir_itr )
+  {
+    //    cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
+    if(ends_with(dir_itr->leaf(),".isc")) {
+      //      cerr<<"Hit!"<<endl;
+
+      if(dpk) {
+        getRSAKeyFromISC(&dpk->d_key.getContext(), dir_itr->path().file_string().c_str());
+       
+        if(getNSEC3PARAM(zone)) {
+          dpk->d_algorithm = 7;
+        }
+        else {
+          dpk->d_algorithm = 5;
+        }
+      
+      }
+      return true;
+    }
+  }
+
+  return false;
+  #endif
+}
+
+unsigned int DNSSECKeeper::getNextKeyIDFromDir(const std::string& dirname)
+{
+  fs::path full_path = fs::system_complete( fs::path(dirname));
+
+  if ( !fs::exists( full_path ) )
+    unixDie("Unable to get next free key id from '"+dirname+"'");
+
+  fs::directory_iterator end_iter;
+  unsigned int maxID=0;
+  for ( fs::directory_iterator dir_itr( full_path );
+       dir_itr != end_iter;
+       ++dir_itr )
+  {
+         if(ends_with(dir_itr->leaf(),".isc")) {
+                 maxID = max(maxID, (unsigned int)atoi(dir_itr->leaf().c_str()));
+         }
+  }
+  return maxID+1;
+}
+
+std::string DNSSECKeeper::getKeyFilenameById(const std::string& dirname, unsigned int id)
+{
+  fs::path full_path = fs::system_complete( fs::path(dirname));
+
+  if ( !fs::exists( full_path ) )
+    unixDie("Unable to get free key id from '"+dirname+"'");
+
+  fs::directory_iterator end_iter;
+  pair<string, string> parts;
+  for ( fs::directory_iterator dir_itr( full_path );
+    dir_itr != end_iter;
+    ++dir_itr )
+  {
+    parts = splitField(dir_itr->leaf(), '-');
+         if(atoi(parts.first.c_str()) == (signed int)id) 
+      return dirname+"/"+dir_itr->leaf();
+  }
+  throw runtime_error("Could not get filename for key id '"+lexical_cast<string>(id)+"'");
+}
+
+
+void DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, bool active)
+{
+  DNSSECPrivateKey dpk;
+  dpk.d_key.create(1024); // for testing, 1024
+
+  string isc = dpk.d_key.convertToISC();
+  DNSKEYRecordContent drc = dpk.getDNSKEY();
+  drc.d_flags = 256; // KSK
+  drc.d_algorithm = algorithm; 
+  string iscName=d_dirname+"/"+name+"/keys/";
+  unsigned int id = getNextKeyIDFromDir(iscName);
+  time_t inception=time(0);
+
+  struct tm ts;
+  gmtime_r(&inception, &ts);
+
+  iscName += (boost::format("%06d-%04d%02d%02d%02d%02d.%u") % id
+             % (1900+ts.tm_year) % (ts.tm_mon + 1)
+             % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag()).str();
+
+  iscName += keyOrZone ? ".ksk" : ".zsk";
+  iscName += active ? ".active" : ".passive";
+  
+  {  
+    ofstream iscFile((iscName+".isc").c_str());
+    iscFile << isc;
+  }
+
+  {  
+    ofstream dnskeyFile((iscName+".dnskey").c_str());
+    dnskeyFile << toCanonic("", name) << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
+  }
+
+}
+
+
+static bool zskCompareByID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
+{
+  return a.second.id < b.second.id;
+}
+
+void DNSSECKeeper::removeKey(const std::string& zname, unsigned int id)
+{
+  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
+  abort();
+}
+
+void DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id)
+{
+  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
+  string fname = getKeyFilenameById(d_dirname+"/keys/", id);
+  string newname = boost::replace_last_copy(fname, ".active", ".passive");
+  if(rename(fname.c_str(), newname.c_str()) < 0)
+    unixDie("renaming file to deactivate key, from: '"+fname+"' to '"+newname+"'");
+}
+
+void DNSSECKeeper::activateKey(const std::string& zname, unsigned int id)
+{
+  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
+  abort();
+}
+
+bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p)
+{
+  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zname + "/nsec3param" ) );
+  ifstream ifs(full_path.external_directory_string().c_str());
+  // cerr<<"called for nsec3param..."<<endl;
+  if(!ifs)
+    return false;
+    
+  if(ns3p) {
+    string descr;
+    getline(ifs, descr);
+    NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, descr));
+    if(!tmp) {
+      cerr<<"Could not parse "<< full_path.external_directory_string() <<endl;
+      cerr<<"descr: '"<<descr<<"'\n";
+    }
+    *ns3p = *tmp;
+    delete tmp;
+    
+    cerr<<"hmm salt: "<<makeHexDump(ns3p->d_salt)<<endl;
+  }
+  return true;
+}
+
+void DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent* ns3p)
+{
+  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zname + "/nsec3param" ) );
+  if(ns3p) {
+    string descr = ns3p->getZoneRepresentation();
+    
+    
+    ofstream of(full_path.external_directory_string().c_str());
+    of << descr;
+  }
+  else {
+    unlink(full_path.external_directory_string().c_str());
+  }
+}
+
+
+DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone)
+{
+  keyset_t keyset;
+
+  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/keys/" ) );
+
+  if ( !fs::exists( full_path ) )
+    return keyset;
+
+  fs::directory_iterator end_iter;
+  for ( fs::directory_iterator dir_itr( full_path );
+       dir_itr != end_iter;
+       ++dir_itr )
+  {
+    //cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
+    if(ends_with(dir_itr->leaf(),".isc")) {
+      DNSSECPrivateKey dpk;
+      getRSAKeyFromISC(&dpk.d_key.getContext(), dir_itr->path().file_string().c_str());
+
+      if(getNSEC3PARAM(zone)) {
+        dpk.d_algorithm = 7;
+      }
+      else {
+        dpk.d_algorithm = 5;
+      }
+      
+      struct tm ts1, ts2;
+      
+      memset(&ts1, 0, sizeof(ts1));
+      memset(&ts2, 0, sizeof(ts2));
+      
+      unsigned int id;
+      sscanf(dir_itr->leaf().c_str(), "%06u-%04d%02d%02d%02d%02d",
+        &id,
+        &ts1.tm_year, 
+        &ts1.tm_mon, &ts1.tm_mday, &ts1.tm_hour, &ts1.tm_min);
+            
+
+      ts1.tm_year -= 1900;
+      
+      ts1.tm_mon--;
+      
+      KeyMetaData kmd;
+      
+      kmd.id = id;
+      kmd.fname = dir_itr->leaf();
+      kmd.active = kmd.fname.find(".active") != string::npos;
+      kmd.keyOrZone = kmd.fname.find(".ksk") != string::npos;
+      if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone)
+        keyset.push_back(make_pair(dpk, kmd));
+    }
+    sort(keyset.begin(), keyset.end(), zskCompareByID);
+  }
+
+  return keyset;
+}
+
+DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY()
+{
+  return makeDNSKEYFromRSAKey(&d_key.getContext(), d_algorithm);
+}
+
+
+void DNSSECKeeper::secureZone(const std::string& name, int algorithm)
+{
+  mkdir((d_dirname+"/"+name).c_str(), 0700);
+  if(mkdir((d_dirname+"/"+name+"/keys").c_str(), 0700) < 0)
+    unixDie("Making directory for keys in '"+d_dirname+"'");
+
+
+  // now add the KSK
+
+  addKey(name, true, algorithm);
+#if 0
+
+  DNSSECPrivateKey dpk;
+  dpk.d_key.create(2048); // for testing, 1024
+
+  string isc = dpk.d_key.convertToISC();
+  DNSKEYRecordContent drc = dpk.getDNSKEY();
+  drc.d_flags = 257; // ZSK (?? for a KSK?)
+  drc.d_algorithm = algorithm;  
+  string iscName=d_dirname+"/"+name+"/keys/";
+
+  time_t now=time(0);
+  struct tm ts;
+  gmtime_r(&now, &ts);
+  unsigned int id=1;
+  iscName += (boost::format("%06d-%04d%02d%02d%02d%02d.%u.%s.%s") % id
+             % (1900+ts.tm_year) % (ts.tm_mon + 1)
+             % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag() % "ksk" % "active").str();
+
+
+  {  
+    ofstream iscFile((iscName+".isc").c_str());
+    iscFile << isc;
+  }
+
+  {  
+    ofstream dnskeyFile((iscName+".dnskey").c_str());
+    dnskeyFile << toCanonic("", name) << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
+  }
+#endif
+}
+