]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnssecsigner.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / dnssecsigner.cc
CommitLineData
882358c8 1/*
12471842
PL
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
882358c8
BH
25#include "dnssecinfra.hh"
26#include "namespaces.hh"
fa8fd4d2 27
bfcdbc13 28#include "digests.hh"
882358c8 29#include "dnsseckeeper.hh"
ff1040bf 30#include "dns_random.hh"
882358c8 31#include "lock.hh"
bba84134 32#include "arguments.hh"
c681ff64 33#include "statbag.hh"
34extern StatBag S;
882358c8 35
8455425c
RG
36static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER;
37typedef map<pair<string, string>, string> signaturecache_t;
38static signaturecache_t g_signatures;
39static int g_cacheweekno;
40
e335c490 41const static std::set<uint16_t> g_KSKSignedQTypes {QType::DNSKEY, QType::CDS, QType::CDNSKEY};
8455425c
RG
42AtomicCounter* g_signatureCount;
43
bfcdbc13
RG
44static std::string getLookupKey(const std::string& msg)
45{
46 try {
47 return pdns_md5sum(msg);
48 }
49 catch(const std::runtime_error& e) {
50 return pdns_sha1sum(msg);
51 }
52}
53
8455425c
RG
54static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign)
55{
56 if(!g_signatureCount)
57 g_signatureCount = S.getPointer("signatures");
58
59 DNSKEYRecordContent drc = dpk.getDNSKEY();
60 const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
61 rrc.d_tag = drc.getTag();
62 rrc.d_algorithm = drc.d_algorithm;
63
64 string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
bfcdbc13 65 pair<string, string> lookup(rc->getPubKeyHash(), getLookupKey(msg)); // this hash is a memory saving exercise
8455425c
RG
66
67 bool doCache=1;
68 if(doCache)
69 {
70 ReadLock l(&g_signatures_lock);
71 signaturecache_t::const_iterator iter = g_signatures.find(lookup);
72 if(iter != g_signatures.end()) {
73 rrc.d_signature=iter->second;
74 return;
75 }
76 // else cerr<<"Miss!"<<endl;
77 }
78
79 rrc.d_signature = rc->sign(msg);
80 (*g_signatureCount)++;
81 if(doCache) {
82 /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */
83 int weekno = (time(0) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us
84 const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
85
86 WriteLock l(&g_signatures_lock);
87 if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04
e6a9dde5 88 g_log<<Logger::Warning<<"Cleared signature cache."<<endl;
8455425c
RG
89 g_signatures.clear();
90 g_cacheweekno = weekno;
91 }
92 g_signatures[lookup] = rrc.d_signature;
93 }
94}
95
d3e7090c 96/* this is where the RRSIGs begin, keys are retrieved,
882358c8 97 but the actual signing happens in fillOutRRSIG */
8de7d2a0 98static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName& signQName, uint16_t signQType, uint32_t signTTL,
8455425c 99 vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs)
882358c8
BH
100{
101 if(toSign.empty())
102 return -1;
b61e407d 103 uint32_t startOfWeek = getStartOfWeek();
882358c8
BH
104 RRSIGRecordContent rrc;
105 rrc.d_type=signQType;
106
8aa5a28c 107 rrc.d_labels=signQName.countLabels()-signQName.isWildcard();
882358c8 108 rrc.d_originalttl=signTTL;
b61e407d
KM
109 rrc.d_siginception=startOfWeek - 7*86400; // XXX should come from zone metadata
110 rrc.d_sigexpire=startOfWeek + 14*86400;
675fa24c 111 rrc.d_signer = signer;
882358c8 112 rrc.d_tag = 0;
7dc95f99 113
b6bd795c 114 DNSSECKeeper::keyset_t keys = dk.getKeys(signer);
7dc95f99 115
b6bd795c 116 for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) {
7dc95f99 117 if(!keymeta.second.active)
882358c8 118 continue;
8a95b04c 119
e335c490
CHB
120 bool signWithKSK = g_KSKSignedQTypes.count(signQType) != 0;
121 // Do not sign DNSKEY RRsets with the ZSK
b6bd795c 122 if((signQType == QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::ZSK) ||
e335c490
CHB
123 // Do not sign any other RRset than DNSKEY, CDS and CDNSKEY with a KSK
124 (!signWithKSK && keymeta.second.keyType == DNSSECKeeper::KSK)) {
b6bd795c 125 continue;
7dc95f99
KM
126 }
127
b6bd795c 128 fillOutRRSIG(keymeta.first, signQName, rrc, toSign);
882358c8
BH
129 rrcs.push_back(rrc);
130 }
131 return 0;
132}
133
134// this is the entrypoint from DNSPacket
8de7d2a0 135static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName& signQName, const DNSName& wildcardname, uint16_t signQType,
8455425c
RG
136 uint32_t signTTL, DNSResourceRecord::Place signPlace,
137 vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL)
882358c8 138{
51a3a4d4 139 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
882358c8
BH
140 if(toSign.empty())
141 return;
d3e7090c
BH
142 vector<RRSIGRecordContent> rrcs;
143 if(dk.isPresigned(signer)) {
51a3a4d4 144 //cerr<<"Doing presignatures"<<endl;
794c2f92 145 dk.getPreRRSIGs(db, signer, signQName, wildcardname, QType(signQType), signPlace, outsigned, origTTL); // does it all
d3e7090c 146 }
6b35b541 147 else {
675fa24c 148 if(getRRSIGsForRRSET(dk, signer, wildcardname.countLabels() ? wildcardname : signQName, signQType, signTTL, toSign, rrcs) < 0) {
6b35b541
BH
149 // cerr<<"Error signing a record!"<<endl;
150 return;
151 }
e02d0a59 152
90ba52e0 153 DNSZoneRecord rr;
154 rr.dr.d_name=signQName;
155 rr.dr.d_type=QType::RRSIG;
794c2f92 156 if(origTTL)
90ba52e0 157 rr.dr.d_ttl=origTTL;
794c2f92 158 else
90ba52e0 159 rr.dr.d_ttl=signTTL;
6b35b541 160 rr.auth=false;
90ba52e0 161 rr.dr.d_place = signPlace;
ef7cd021 162 for(RRSIGRecordContent& rrc : rrcs) {
90ba52e0 163 rr.dr.d_content = std::make_shared<RRSIGRecordContent>(rrc);
6b35b541
BH
164 outsigned.push_back(rr);
165 }
882358c8 166 }
882358c8
BH
167 toSign.clear();
168}
169
e903706d 170uint64_t signatureCacheSize(const std::string& str)
171{
172 ReadLock l(&g_signatures_lock);
173 return g_signatures.size();
174}
175
90ba52e0 176static bool rrsigncomp(const DNSZoneRecord& a, const DNSZoneRecord& b)
e02d0a59 177{
90ba52e0 178 return tie(a.dr.d_place, a.dr.d_type) < tie(b.dr.d_place, b.dr.d_type);
e02d0a59
BH
179}
180
675fa24c 181static bool getBestAuthFromSet(const set<DNSName>& authSet, const DNSName& name, DNSName& auth)
8d3cbffa 182{
675fa24c
PD
183 auth.trimToLabels(0);
184 DNSName sname(name);
8d3cbffa
BH
185 do {
186 if(authSet.find(sname) != authSet.end()) {
187 auth = sname;
188 return true;
189 }
190 }
675fa24c 191 while(sname.chopOff());
8d3cbffa
BH
192
193 return false;
194}
195
90ba52e0 196void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const set<DNSName>& authSet, vector<DNSZoneRecord>& rrs)
e02d0a59 197{
e02d0a59
BH
198 stable_sort(rrs.begin(), rrs.end(), rrsigncomp);
199
675fa24c 200 DNSName signQName, wildcardQName;
e02d0a59
BH
201 uint16_t signQType=0;
202 uint32_t signTTL=0;
794c2f92 203 uint32_t origTTL=0;
e02d0a59 204
e693ff5a 205 DNSResourceRecord::Place signPlace=DNSResourceRecord::ANSWER;
e02d0a59
BH
206 vector<shared_ptr<DNSRecordContent> > toSign;
207
90ba52e0 208 vector<DNSZoneRecord> signedRecords;
d42896fb 209 signedRecords.reserve(rrs.size()*1.5);
90ba52e0 210 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
675fa24c 211 DNSName signer;
90ba52e0 212 for(auto pos = rrs.cbegin(); pos != rrs.cend(); ++pos) {
213 if(pos != rrs.cbegin() && (signQType != pos->dr.d_type || signQName != pos->dr.d_name)) {
8d3cbffa 214 if(getBestAuthFromSet(authSet, signQName, signer))
794c2f92 215 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
e02d0a59 216 }
8e9b7d99 217 signedRecords.push_back(*pos);
90ba52e0 218 signQName= pos->dr.d_name.makeLowerCase();
e1a9ab9f 219 if(!pos->wildcardname.empty())
d42896fb 220 wildcardQName = pos->wildcardname.makeLowerCase();
07338ade
KM
221 else
222 wildcardQName.clear();
90ba52e0 223 signQType = pos->dr.d_type;
794c2f92
PD
224 if(pos->signttl)
225 signTTL = pos->signttl;
226 else
90ba52e0 227 signTTL = pos->dr.d_ttl;
228 origTTL = pos->dr.d_ttl;
229 signPlace = pos->dr.d_place;
230 if(pos->auth || pos->dr.d_type == QType::DS) {
231 toSign.push_back(pos->dr.d_content); // so ponder.. should this be a deep copy perhaps?
e02d0a59
BH
232 }
233 }
8d3cbffa 234 if(getBestAuthFromSet(authSet, signQName, signer))
794c2f92 235 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
e02d0a59
BH
236 rrs.swap(signedRecords);
237}