]>
Commit | Line | Data |
---|---|---|
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" |
34 | extern StatBag S; | |
882358c8 | 35 | |
8455425c RG |
36 | static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER; |
37 | typedef map<pair<string, string>, string> signaturecache_t; | |
38 | static signaturecache_t g_signatures; | |
39 | static int g_cacheweekno; | |
40 | ||
e335c490 | 41 | const static std::set<uint16_t> g_KSKSignedQTypes {QType::DNSKEY, QType::CDS, QType::CDNSKEY}; |
8455425c RG |
42 | AtomicCounter* g_signatureCount; |
43 | ||
bfcdbc13 RG |
44 | static 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 |
54 | static 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 | 98 | static 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 | 135 | static 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 | 170 | uint64_t signatureCacheSize(const std::string& str) |
171 | { | |
172 | ReadLock l(&g_signatures_lock); | |
173 | return g_signatures.size(); | |
174 | } | |
175 | ||
90ba52e0 | 176 | static 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 | 181 | static 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 | 196 | void 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 | } |