]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnssecsigner.cc
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[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"
ec47cf6f
FM
34#include "sha.hh"
35
c681ff64 36extern StatBag S;
882358c8 37
ec47cf6f 38using signaturecache_t = map<pair<string, string>, string>;
4f4b8ed8 39static SharedLockGuarded<signaturecache_t> g_signatures;
8455425c
RG
40static int g_cacheweekno;
41
e335c490 42const static std::set<uint16_t> g_KSKSignedQTypes {QType::DNSKEY, QType::CDS, QType::CDNSKEY};
8455425c
RG
43AtomicCounter* g_signatureCount;
44
b60fb4a9 45static std::string getLookupKeyFromMessage(const std::string& msg)
bfcdbc13
RG
46{
47 try {
f2e4e85a 48 return pdns::md5(msg);
bfcdbc13
RG
49 }
50 catch(const std::runtime_error& e) {
f2e4e85a 51 return pdns::sha1(msg);
bfcdbc13
RG
52 }
53}
54
b60fb4a9 55static std::string getLookupKeyFromPublicKey(const std::string& pubKey)
ab5df144
RG
56{
57 /* arbitrarily cut off at 64 bytes, the main idea is to save space
b60fb4a9 58 for very large keys like RSA ones (1024+ bits so 128+ bytes) by storing a 20 bytes hash
ab5df144
RG
59 instead */
60 if (pubKey.size() <= 64) {
61 return pubKey;
62 }
ec47cf6f 63 return pdns::sha1sum(pubKey);
ab5df144
RG
64}
65
c1e7b833 66static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, const sortedRecords_t& toSign)
8455425c
RG
67{
68 if(!g_signatureCount)
69 g_signatureCount = S.getPointer("signatures");
70
71 DNSKEYRecordContent drc = dpk.getDNSKEY();
ca23a01d 72 const std::shared_ptr<DNSCryptoKeyEngine>& rc = dpk.getKey();
8455425c
RG
73 rrc.d_tag = drc.getTag();
74 rrc.d_algorithm = drc.d_algorithm;
75
ab5df144 76 string msg = getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
b60fb4a9 77 pair<string, string> lookup(getLookupKeyFromPublicKey(drc.d_key), getLookupKeyFromMessage(msg)); // this hash is a memory saving exercise
8455425c 78
b60fb4a9
RG
79 bool doCache = true;
80 if (doCache) {
4f4b8ed8
RG
81 auto signatures = g_signatures.read_lock();
82 signaturecache_t::const_iterator iter = signatures->find(lookup);
83 if (iter != signatures->end()) {
8455425c
RG
84 rrc.d_signature=iter->second;
85 return;
86 }
87 // else cerr<<"Miss!"<<endl;
88 }
89
90 rrc.d_signature = rc->sign(msg);
91 (*g_signatureCount)++;
92 if(doCache) {
c02c999b 93 /* we add some jitter here so not all your secondaries start pruning their caches at the very same millisecond */
4646277d 94 int weekno = (time(nullptr) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us
8455425c
RG
95 const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
96
4f4b8ed8
RG
97 auto signatures = g_signatures.write_lock();
98 if (g_cacheweekno < weekno || signatures->size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04
e6a9dde5 99 g_log<<Logger::Warning<<"Cleared signature cache."<<endl;
4f4b8ed8 100 signatures->clear();
8455425c
RG
101 g_cacheweekno = weekno;
102 }
4f4b8ed8 103 (*signatures)[lookup] = rrc.d_signature;
8455425c
RG
104 }
105}
106
d3e7090c 107/* this is where the RRSIGs begin, keys are retrieved,
882358c8 108 but the actual signing happens in fillOutRRSIG */
8de7d2a0 109static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName& signQName, uint16_t signQType, uint32_t signTTL,
c1e7b833 110 const sortedRecords_t& toSign, vector<RRSIGRecordContent>& rrcs)
882358c8
BH
111{
112 if(toSign.empty())
113 return -1;
b61e407d 114 uint32_t startOfWeek = getStartOfWeek();
882358c8
BH
115 RRSIGRecordContent rrc;
116 rrc.d_type=signQType;
117
8aa5a28c 118 rrc.d_labels=signQName.countLabels()-signQName.isWildcard();
bb85386d 119 rrc.d_originalttl=signTTL;
b61e407d
KM
120 rrc.d_siginception=startOfWeek - 7*86400; // XXX should come from zone metadata
121 rrc.d_sigexpire=startOfWeek + 14*86400;
675fa24c 122 rrc.d_signer = signer;
882358c8 123 rrc.d_tag = 0;
7dc95f99 124
b6bd795c 125 DNSSECKeeper::keyset_t keys = dk.getKeys(signer);
7dc95f99 126
b6bd795c 127 for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) {
7dc95f99 128 if(!keymeta.second.active)
882358c8 129 continue;
8a95b04c 130
e335c490
CHB
131 bool signWithKSK = g_KSKSignedQTypes.count(signQType) != 0;
132 // Do not sign DNSKEY RRsets with the ZSK
b6bd795c 133 if((signQType == QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::ZSK) ||
e335c490
CHB
134 // Do not sign any other RRset than DNSKEY, CDS and CDNSKEY with a KSK
135 (!signWithKSK && keymeta.second.keyType == DNSSECKeeper::KSK)) {
b6bd795c 136 continue;
7dc95f99
KM
137 }
138
b6bd795c 139 fillOutRRSIG(keymeta.first, signQName, rrc, toSign);
882358c8
BH
140 rrcs.push_back(rrc);
141 }
142 return 0;
143}
144
145// this is the entrypoint from DNSPacket
8de7d2a0 146static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName& signQName, const DNSName& wildcardname, uint16_t signQType,
8455425c 147 uint32_t signTTL, DNSResourceRecord::Place signPlace,
c1e7b833 148 sortedRecords_t& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL)
882358c8 149{
51a3a4d4 150 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
882358c8
BH
151 if(toSign.empty())
152 return;
d3e7090c
BH
153 vector<RRSIGRecordContent> rrcs;
154 if(dk.isPresigned(signer)) {
51a3a4d4 155 //cerr<<"Doing presignatures"<<endl;
e6fb0c9b 156 dk.getPreRRSIGs(db, outsigned, origTTL); // does it all
d3e7090c 157 }
6b35b541 158 else {
675fa24c 159 if(getRRSIGsForRRSET(dk, signer, wildcardname.countLabels() ? wildcardname : signQName, signQType, signTTL, toSign, rrcs) < 0) {
6b35b541
BH
160 // cerr<<"Error signing a record!"<<endl;
161 return;
bb85386d
FM
162 }
163
90ba52e0 164 DNSZoneRecord rr;
165 rr.dr.d_name=signQName;
166 rr.dr.d_type=QType::RRSIG;
794c2f92 167 if(origTTL)
90ba52e0 168 rr.dr.d_ttl=origTTL;
794c2f92 169 else
90ba52e0 170 rr.dr.d_ttl=signTTL;
6b35b541 171 rr.auth=false;
90ba52e0 172 rr.dr.d_place = signPlace;
ef7cd021 173 for(RRSIGRecordContent& rrc : rrcs) {
d06dcda4 174 rr.dr.setContent(std::make_shared<RRSIGRecordContent>(rrc));
6b35b541
BH
175 outsigned.push_back(rr);
176 }
882358c8 177 }
882358c8
BH
178 toSign.clear();
179}
180
d73de874 181uint64_t signatureCacheSize(const std::string& /* str */)
e903706d 182{
4f4b8ed8 183 return g_signatures.read_lock()->size();
e903706d 184}
185
90ba52e0 186static bool rrsigncomp(const DNSZoneRecord& a, const DNSZoneRecord& b)
e02d0a59 187{
905dae56 188 return std::tie(a.dr.d_place, a.dr.d_type) < std::tie(b.dr.d_place, b.dr.d_type);
e02d0a59
BH
189}
190
675fa24c 191static bool getBestAuthFromSet(const set<DNSName>& authSet, const DNSName& name, DNSName& auth)
8d3cbffa 192{
675fa24c
PD
193 auth.trimToLabels(0);
194 DNSName sname(name);
8d3cbffa
BH
195 do {
196 if(authSet.find(sname) != authSet.end()) {
197 auth = sname;
198 return true;
199 }
200 }
675fa24c 201 while(sname.chopOff());
bb85386d 202
8d3cbffa
BH
203 return false;
204}
205
90ba52e0 206void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const set<DNSName>& authSet, vector<DNSZoneRecord>& rrs)
e02d0a59 207{
e02d0a59 208 stable_sort(rrs.begin(), rrs.end(), rrsigncomp);
bb85386d 209
edacfa5d 210 DNSName authQName, signQName, wildcardQName;
e02d0a59
BH
211 uint16_t signQType=0;
212 uint32_t signTTL=0;
794c2f92 213 uint32_t origTTL=0;
bb85386d 214
e693ff5a 215 DNSResourceRecord::Place signPlace=DNSResourceRecord::ANSWER;
c1e7b833 216 sortedRecords_t toSign;
e02d0a59 217
90ba52e0 218 vector<DNSZoneRecord> signedRecords;
d42896fb 219 signedRecords.reserve(rrs.size()*1.5);
90ba52e0 220 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
675fa24c 221 DNSName signer;
90ba52e0 222 for(auto pos = rrs.cbegin(); pos != rrs.cend(); ++pos) {
223 if(pos != rrs.cbegin() && (signQType != pos->dr.d_type || signQName != pos->dr.d_name)) {
edacfa5d 224 if (getBestAuthFromSet(authSet, authQName, signer))
794c2f92 225 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
e02d0a59 226 }
8e9b7d99 227 signedRecords.push_back(*pos);
edacfa5d
KM
228 signQName = pos->dr.d_name.makeLowerCase();
229 if (pos->dr.d_type == QType::NSEC) {
230 authQName = signQName.getCommonLabels(getRR<NSECRecordContent>(pos->dr)->d_next);
edacfa5d
KM
231 }
232 else {
233 authQName = signQName;
234 }
e1a9ab9f 235 if(!pos->wildcardname.empty())
d42896fb 236 wildcardQName = pos->wildcardname.makeLowerCase();
07338ade
KM
237 else
238 wildcardQName.clear();
90ba52e0 239 signQType = pos->dr.d_type;
794c2f92
PD
240 if(pos->signttl)
241 signTTL = pos->signttl;
242 else
90ba52e0 243 signTTL = pos->dr.d_ttl;
244 origTTL = pos->dr.d_ttl;
245 signPlace = pos->dr.d_place;
246 if(pos->auth || pos->dr.d_type == QType::DS) {
d06dcda4 247 toSign.insert(pos->dr.getContent()); // so ponder.. should this be a deep copy perhaps?
e02d0a59
BH
248 }
249 }
edacfa5d 250 if (getBestAuthFromSet(authSet, authQName, signer))
794c2f92 251 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
e02d0a59
BH
252 rrs.swap(signedRecords);
253}