root/trunk/pdns/pdns/dnssecsigner.cc @ 1893

Revision 1893, 6.5 KB (checked in by ahu, 2 years ago)

implement 'pdnssec set-presigned', allowing PowerDNSSEC to serve pre-signed zones. Rather experimental, but does appear to work

Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2001 - 2011  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*/
18#include "dnssecinfra.hh"
19#include "namespaces.hh"
20#include <boost/foreach.hpp>
21#include "dnsseckeeper.hh"
22#include "lock.hh"
23
24/* this is where the RRSIGs begin, keys are retrieved,
25   but the actual signing happens in fillOutRRSIG */
26int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string& signer, const std::string signQName, uint16_t signQType, uint32_t signTTL, 
27                     vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs, bool ksk)
28{
29  if(toSign.empty())
30    return -1;
31  RRSIGRecordContent rrc;
32  rrc.d_type=signQType;
33
34  rrc.d_labels=countLabels(signQName); 
35  rrc.d_originalttl=signTTL; 
36  rrc.d_siginception=getCurrentInception();;
37  rrc.d_sigexpire = rrc.d_siginception + 14*86400; // XXX should come from zone metadata
38  rrc.d_signer = signer;
39  rrc.d_tag = 0;
40 
41  // we sign the RRSET in toSign + the rrc w/o hash
42 
43  DNSSECKeeper::keyset_t keys = dk.getKeys(rrc.d_signer);
44  vector<DNSSECPrivateKey> KSKs, ZSKs;
45  vector<DNSSECPrivateKey>* signingKeys;
46 
47  // if ksk==1, only get KSKs
48  // if ksk==0, get ZSKs, unless there is no ZSK, then get KSK
49  BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type& keymeta, keys) {
50    rrc.d_algorithm = keymeta.first.d_algorithm;
51    if(!keymeta.second.active) 
52      continue;
53     
54    if(keymeta.second.keyOrZone)
55      KSKs.push_back(keymeta.first);
56    else if(!ksk)
57      ZSKs.push_back(keymeta.first);
58  }
59  if(ksk)
60    signingKeys = &KSKs;
61  else {
62    if(ZSKs.empty())
63      signingKeys = &KSKs;
64    else
65      signingKeys =&ZSKs;
66  }
67 
68  BOOST_FOREACH(DNSSECPrivateKey& dpk, *signingKeys) {
69    fillOutRRSIG(dpk, signQName, rrc, toSign);
70    rrcs.push_back(rrc);
71  }
72  return 0;
73}
74
75// this is the entrypoint from DNSPacket
76void addSignature(DNSSECKeeper& dk, const std::string& signer, const std::string signQName, const std::string& wildcardname, uint16_t signQType, 
77  uint32_t signTTL, DNSPacketWriter::Place signPlace, 
78  vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSResourceRecord>& outsigned)
79{
80  // cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
81  if(toSign.empty())
82    return;
83  vector<RRSIGRecordContent> rrcs;
84  if(dk.isPresigned(signer)) {
85        dk.getPreRRSIGs(signer, signQName, QType(signQType), signPlace, outsigned);
86  }
87  else if(getRRSIGsForRRSET(dk, signer, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrcs, signQType == QType::DNSKEY) < 0) {
88    // cerr<<"Error signing a record!"<<endl;
89    return;
90  }
91 
92  DNSResourceRecord rr;
93  rr.qname=signQName;
94  rr.qtype=QType::RRSIG;
95  rr.ttl=signTTL;
96  rr.auth=false;
97 
98  BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) {
99    rr.content = rrc.getZoneRepresentation();
100    outsigned.push_back(rr);
101  }
102
103  toSign.clear();
104}
105
106static pthread_mutex_t g_signatures_lock = PTHREAD_MUTEX_INITIALIZER;
107static map<pair<string, string>, string> g_signatures;
108
109void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign) 
110{
111  DNSKEYRecordContent drc= dpk.getDNSKEY(); 
112  RSAContext& rc = dpk.d_key;
113  rrc.d_tag = drc.getTag();
114  rrc.d_algorithm = drc.d_algorithm;
115  string realhash=getHashForRRSET(signQName, rrc, toSign); // this is what we sign
116
117  unsigned char signature[mpi_size(&rc.getContext().N)];
118  pair<string, string> lookup(rc.getPubKeyHash(), realhash);
119 
120  {
121    Lock l(&g_signatures_lock);
122    if(g_signatures.count(lookup)) {
123      // cerr<<"Hit!"<<endl;
124      rrc.d_signature=g_signatures[lookup];
125      return;
126    }
127    else
128      ; // cerr<<"Miss!"<<endl;
129  }
130 
131  int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE, 
132    rrc.d_algorithm < 8 ? SIG_RSA_SHA1 : SIG_RSA_SHA256, 
133    rrc.d_algorithm < 8 ? 20 : 32,
134    (unsigned char*) realhash.c_str(), signature);
135 
136  if(ret!=0) {
137    cerr<<"signing returned: "<<ret<<endl;
138    exit(1);
139  }
140 
141  rrc.d_signature.assign((char*)signature, sizeof(signature));
142
143  Lock l(&g_signatures_lock);
144  g_signatures[lookup] = rrc.d_signature;
145}
146
147static bool rrsigncomp(const DNSResourceRecord& a, const DNSResourceRecord& b)
148{
149  return a.d_place < b.d_place;
150}
151
152void addRRSigs(DNSSECKeeper& dk, const std::string& signer, DNSPacket& p)
153{
154  vector<DNSResourceRecord>& rrs=p.getRRS();
155 
156  stable_sort(rrs.begin(), rrs.end(), rrsigncomp);
157 
158  string signQName, wildcardQName;
159  uint16_t signQType=0;
160  uint32_t signTTL=0;
161 
162  DNSPacketWriter::Place signPlace=DNSPacketWriter::ANSWER;
163  vector<shared_ptr<DNSRecordContent> > toSign;
164
165  vector<DNSResourceRecord> signedRecords;
166
167  for(vector<DNSResourceRecord>::const_iterator pos = rrs.begin(); pos != rrs.end(); ++pos) {
168    signedRecords.push_back(*pos);
169    if(pos != rrs.begin() && (signQType != pos->qtype.getCode()  || signQName != pos->qname)) {
170      addSignature(dk, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords);
171    }
172    signQName= pos->qname;
173    wildcardQName = pos->wildcardname;
174    signQType = pos ->qtype.getCode();
175    signTTL = pos->ttl;
176    signPlace = (DNSPacketWriter::Place) pos->d_place;
177    if(pos->auth || pos->qtype.getCode() == QType::DS) {
178      string content = pos ->content;
179      if(pos->qtype.getCode()==QType::MX || pos->qtype.getCode() == QType::SRV) { 
180        content = lexical_cast<string>(pos->priority) + " " + pos->content;
181      }
182      if(!pos->content.empty() && pos->qtype.getCode()==QType::TXT && pos->content[0]!='"') {
183        content="\""+pos->content+"\"";
184      }
185      if(pos->content.empty())  // empty contents confuse the MOADNS setup
186        content=".";
187     
188      shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, content)); 
189      toSign.push_back(drc);
190    }
191  }
192  addSignature(dk, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords);
193 
194  rrs.swap(signedRecords);
195}
Note: See TracBrowser for help on using the browser.