root/trunk/pdns/pdns/fsdnsseckeeper.cc @ 1758

Revision 1758, 9.6 KB (checked in by ahu, 2 years ago)

this implies that there will be more dnsseckeepers - database backed for example

Line 
1#include "dnsseckeeper.hh"
2#include "dnssecinfra.hh"
3#include "statbag.hh"
4#include <iostream>
5#include <boost/filesystem/operations.hpp>
6#include <boost/filesystem/path.hpp>
7#include <polarssl/havege.h>
8#include <polarssl/base64.h>
9#include <boost/foreach.hpp>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <fstream>
13#include <boost/algorithm/string.hpp>
14#include <boost/format.hpp>
15#include <boost/assign/std/vector.hpp> // for 'operator+=()'
16#include <boost/assign/list_inserter.hpp>
17using namespace boost::assign;
18namespace fs = boost::filesystem;
19
20using namespace std;
21using namespace boost;
22
23void RSAContext::create(unsigned int bits)
24{
25  havege_state hs;
26  havege_init( &hs );
27 
28  rsa_init(&d_context, RSA_PKCS_V15, 0, havege_rand, &hs ); // FIXME this leaks memory
29  int ret=rsa_gen_key(&d_context, bits, 65537);
30  if(ret < 0) 
31    throw runtime_error("Key generation failed");
32}
33
34std::string RSAContext::convertToISC()
35{
36  string ret;
37  typedef vector<pair<string, mpi*> > outputs_t;
38  outputs_t outputs;
39  push_back(outputs)("Modulus", &d_context.N)("PublicExponent",&d_context.E)
40    ("Modulus", &d_context.N)
41    ("PublicExponent",&d_context.E)
42    ("PrivateExponent",&d_context.D)
43    ("Prime1",&d_context.P)
44    ("Prime2",&d_context.Q)
45    ("Exponent1",&d_context.DP)
46    ("Exponent2",&d_context.DQ)
47    ("Coefficient",&d_context.QP);
48
49  ret = "Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\n";
50
51  BOOST_FOREACH(outputs_t::value_type value, outputs) {
52    ret += value.first;
53    ret += ": ";
54    unsigned char tmp[mpi_size(value.second)];
55    mpi_write_binary(value.second, tmp, sizeof(tmp));
56    unsigned char base64tmp[sizeof(tmp)*2];
57    int dlen=sizeof(base64tmp);
58    base64_encode(base64tmp, &dlen, tmp, sizeof(tmp));
59    ret.append((const char*)base64tmp, dlen);
60    ret.append(1, '\n');
61  }
62  return ret;
63}
64
65bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk)
66{
67  keyset_t keys = getKeys(zone, true);
68  // need to get an *active* one!
69  if(dpk && !keys.empty()) {
70    *dpk = keys.begin()->first;
71  }
72  return !keys.empty();
73 
74  #if 0
75  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/keys/" ) );
76
77  if ( !fs::exists( full_path ) )
78    return false;
79
80  fs::directory_iterator end_iter;
81  for ( fs::directory_iterator dir_itr( full_path );
82        dir_itr != end_iter;
83        ++dir_itr )
84  {
85    //    cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
86    if(ends_with(dir_itr->leaf(),".isc")) {
87      //      cerr<<"Hit!"<<endl;
88
89      if(dpk) {
90        getRSAKeyFromISC(&dpk->d_key.getContext(), dir_itr->path().file_string().c_str());
91       
92        if(getNSEC3PARAM(zone)) {
93          dpk->d_algorithm = 7;
94        }
95        else {
96          dpk->d_algorithm = 5;
97        }
98     
99      }
100      return true;
101    }
102  }
103
104  return false;
105  #endif
106}
107
108unsigned int DNSSECKeeper::getNextKeyIDFromDir(const std::string& dirname)
109{
110  fs::path full_path = fs::system_complete( fs::path(dirname));
111
112  if ( !fs::exists( full_path ) )
113    unixDie("Unable to get next free key id from '"+dirname+"'");
114
115  fs::directory_iterator end_iter;
116  unsigned int maxID=0;
117  for ( fs::directory_iterator dir_itr( full_path );
118        dir_itr != end_iter;
119        ++dir_itr )
120  {
121          if(ends_with(dir_itr->leaf(),".isc")) {
122                  maxID = max(maxID, (unsigned int)atoi(dir_itr->leaf().c_str()));
123          }
124  }
125  return maxID+1;
126}
127
128std::string DNSSECKeeper::getKeyFilenameById(const std::string& dirname, unsigned int id)
129{
130  fs::path full_path = fs::system_complete( fs::path(dirname));
131
132  if ( !fs::exists( full_path ) )
133    unixDie("Unable to get free key id from '"+dirname+"'");
134
135  fs::directory_iterator end_iter;
136  pair<string, string> parts;
137  for ( fs::directory_iterator dir_itr( full_path );
138    dir_itr != end_iter;
139    ++dir_itr )
140  {
141    parts = splitField(dir_itr->leaf(), '-');
142          if(atoi(parts.first.c_str()) == (signed int)id) 
143      return dirname+"/"+dir_itr->leaf();
144  }
145  throw runtime_error("Could not get filename for key id '"+lexical_cast<string>(id)+"'");
146}
147
148
149void DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, bool active)
150{
151  DNSSECPrivateKey dpk;
152  dpk.d_key.create(1024); // for testing, 1024
153
154  string isc = dpk.d_key.convertToISC();
155  DNSKEYRecordContent drc = dpk.getDNSKEY();
156  drc.d_flags = 256; // KSK
157  drc.d_algorithm = algorithm; 
158  string iscName=d_dirname+"/"+name+"/keys/";
159  unsigned int id = getNextKeyIDFromDir(iscName);
160  time_t inception=time(0);
161
162  struct tm ts;
163  gmtime_r(&inception, &ts);
164
165  iscName += (boost::format("%06d-%04d%02d%02d%02d%02d.%u") % id
166              % (1900+ts.tm_year) % (ts.tm_mon + 1)
167              % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag()).str();
168
169  iscName += keyOrZone ? ".ksk" : ".zsk";
170  iscName += active ? ".active" : ".passive";
171 
172  { 
173    ofstream iscFile((iscName+".isc").c_str());
174    iscFile << isc;
175  }
176
177  { 
178    ofstream dnskeyFile((iscName+".dnskey").c_str());
179    dnskeyFile << toCanonic("", name) << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
180  }
181
182}
183
184
185static bool zskCompareByID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b)
186{
187  return a.second.id < b.second.id;
188}
189
190void DNSSECKeeper::removeKey(const std::string& zname, unsigned int id)
191{
192  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
193  abort();
194}
195
196void DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id)
197{
198  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
199  string fname = getKeyFilenameById(d_dirname+"/keys/", id);
200  string newname = boost::replace_last_copy(fname, ".active", ".passive");
201  if(rename(fname.c_str(), newname.c_str()) < 0)
202    unixDie("renaming file to deactivate key, from: '"+fname+"' to '"+newname+"'");
203}
204
205void DNSSECKeeper::activateKey(const std::string& zname, unsigned int id)
206{
207  // unlink((d_dirname +"/"+ zname +"/zsks/"+fname).c_str());
208  abort();
209}
210
211bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p)
212{
213  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zname + "/nsec3param" ) );
214  ifstream ifs(full_path.external_directory_string().c_str());
215  // cerr<<"called for nsec3param..."<<endl;
216  if(!ifs)
217    return false;
218   
219  if(ns3p) {
220    string descr;
221    getline(ifs, descr);
222    NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, descr));
223    if(!tmp) {
224      cerr<<"Could not parse "<< full_path.external_directory_string() <<endl;
225      cerr<<"descr: '"<<descr<<"'\n";
226    }
227    *ns3p = *tmp;
228    delete tmp;
229   
230    cerr<<"hmm salt: "<<makeHexDump(ns3p->d_salt)<<endl;
231  }
232  return true;
233}
234
235void DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent* ns3p)
236{
237  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zname + "/nsec3param" ) );
238  if(ns3p) {
239    string descr = ns3p->getZoneRepresentation();
240   
241   
242    ofstream of(full_path.external_directory_string().c_str());
243    of << descr;
244  }
245  else {
246    unlink(full_path.external_directory_string().c_str());
247  }
248}
249
250
251DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone)
252{
253  keyset_t keyset;
254
255  fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/keys/" ) );
256
257  if ( !fs::exists( full_path ) )
258    return keyset;
259
260  fs::directory_iterator end_iter;
261  for ( fs::directory_iterator dir_itr( full_path );
262        dir_itr != end_iter;
263        ++dir_itr )
264  {
265    //cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
266    if(ends_with(dir_itr->leaf(),".isc")) {
267      DNSSECPrivateKey dpk;
268      getRSAKeyFromISC(&dpk.d_key.getContext(), dir_itr->path().file_string().c_str());
269
270      if(getNSEC3PARAM(zone)) {
271        dpk.d_algorithm = 7;
272      }
273      else {
274        dpk.d_algorithm = 5;
275      }
276     
277      struct tm ts1, ts2;
278     
279      memset(&ts1, 0, sizeof(ts1));
280      memset(&ts2, 0, sizeof(ts2));
281     
282      unsigned int id;
283      sscanf(dir_itr->leaf().c_str(), "%06u-%04d%02d%02d%02d%02d",
284        &id,
285        &ts1.tm_year, 
286        &ts1.tm_mon, &ts1.tm_mday, &ts1.tm_hour, &ts1.tm_min);
287             
288
289      ts1.tm_year -= 1900;
290     
291      ts1.tm_mon--;
292     
293      KeyMetaData kmd;
294     
295      kmd.id = id;
296      kmd.fname = dir_itr->leaf();
297      kmd.active = kmd.fname.find(".active") != string::npos;
298      kmd.keyOrZone = kmd.fname.find(".ksk") != string::npos;
299      if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone)
300        keyset.push_back(make_pair(dpk, kmd));
301    }
302    sort(keyset.begin(), keyset.end(), zskCompareByID);
303  }
304
305  return keyset;
306}
307
308DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY()
309{
310  return makeDNSKEYFromRSAKey(&d_key.getContext(), d_algorithm);
311}
312
313
314void DNSSECKeeper::secureZone(const std::string& name, int algorithm)
315{
316  mkdir((d_dirname+"/"+name).c_str(), 0700);
317  if(mkdir((d_dirname+"/"+name+"/keys").c_str(), 0700) < 0)
318    unixDie("Making directory for keys in '"+d_dirname+"'");
319
320
321  // now add the KSK
322
323  addKey(name, true, algorithm);
324#if 0
325
326  DNSSECPrivateKey dpk;
327  dpk.d_key.create(2048); // for testing, 1024
328
329  string isc = dpk.d_key.convertToISC();
330  DNSKEYRecordContent drc = dpk.getDNSKEY();
331  drc.d_flags = 257; // ZSK (?? for a KSK?)
332  drc.d_algorithm = algorithm; 
333  string iscName=d_dirname+"/"+name+"/keys/";
334
335  time_t now=time(0);
336  struct tm ts;
337  gmtime_r(&now, &ts);
338  unsigned int id=1;
339  iscName += (boost::format("%06d-%04d%02d%02d%02d%02d.%u.%s.%s") % id
340              % (1900+ts.tm_year) % (ts.tm_mon + 1)
341              % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag() % "ksk" % "active").str();
342
343
344  { 
345    ofstream iscFile((iscName+".isc").c_str());
346    iscFile << isc;
347  }
348
349  { 
350    ofstream dnskeyFile((iscName+".dnskey").c_str());
351    dnskeyFile << toCanonic("", name) << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
352  }
353#endif
354}
355 
356
Note: See TracBrowser for help on using the browser.