]>
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 | |
8d9f38f2 | 28 | #include "md5.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 | |
d3e7090c | 36 | /* this is where the RRSIGs begin, keys are retrieved, |
882358c8 | 37 | but the actual signing happens in fillOutRRSIG */ |
675fa24c | 38 | int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL, |
8a95b04c | 39 | vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs) |
882358c8 BH |
40 | { |
41 | if(toSign.empty()) | |
42 | return -1; | |
b61e407d | 43 | uint32_t startOfWeek = getStartOfWeek(); |
882358c8 BH |
44 | RRSIGRecordContent rrc; |
45 | rrc.d_type=signQType; | |
46 | ||
8aa5a28c | 47 | rrc.d_labels=signQName.countLabels()-signQName.isWildcard(); |
882358c8 | 48 | rrc.d_originalttl=signTTL; |
b61e407d KM |
49 | rrc.d_siginception=startOfWeek - 7*86400; // XXX should come from zone metadata |
50 | rrc.d_sigexpire=startOfWeek + 14*86400; | |
675fa24c | 51 | rrc.d_signer = signer; |
882358c8 | 52 | rrc.d_tag = 0; |
7dc95f99 | 53 | |
b6bd795c | 54 | DNSSECKeeper::keyset_t keys = dk.getKeys(signer); |
7dc95f99 | 55 | |
b6bd795c | 56 | for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) { |
7dc95f99 | 57 | if(!keymeta.second.active) |
882358c8 | 58 | continue; |
8a95b04c | 59 | |
b6bd795c PL |
60 | if((signQType == QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::ZSK) || |
61 | (signQType != QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::KSK)) { | |
62 | continue; | |
7dc95f99 KM |
63 | } |
64 | ||
b6bd795c | 65 | fillOutRRSIG(keymeta.first, signQName, rrc, toSign); |
882358c8 BH |
66 | rrcs.push_back(rrc); |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | // this is the entrypoint from DNSPacket | |
675fa24c | 72 | void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType, |
e693ff5a | 73 | uint32_t signTTL, DNSResourceRecord::Place signPlace, |
794c2f92 | 74 | vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSResourceRecord>& outsigned, uint32_t origTTL) |
882358c8 | 75 | { |
51a3a4d4 | 76 | //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n"; |
882358c8 BH |
77 | if(toSign.empty()) |
78 | return; | |
d3e7090c BH |
79 | vector<RRSIGRecordContent> rrcs; |
80 | if(dk.isPresigned(signer)) { | |
51a3a4d4 | 81 | //cerr<<"Doing presignatures"<<endl; |
794c2f92 | 82 | dk.getPreRRSIGs(db, signer, signQName, wildcardname, QType(signQType), signPlace, outsigned, origTTL); // does it all |
d3e7090c | 83 | } |
6b35b541 | 84 | else { |
675fa24c | 85 | if(getRRSIGsForRRSET(dk, signer, wildcardname.countLabels() ? wildcardname : signQName, signQType, signTTL, toSign, rrcs) < 0) { |
6b35b541 BH |
86 | // cerr<<"Error signing a record!"<<endl; |
87 | return; | |
88 | } | |
e02d0a59 | 89 | |
6b35b541 BH |
90 | DNSResourceRecord rr; |
91 | rr.qname=signQName; | |
92 | rr.qtype=QType::RRSIG; | |
794c2f92 PD |
93 | if(origTTL) |
94 | rr.ttl=origTTL; | |
95 | else | |
96 | rr.ttl=signTTL; | |
6b35b541 | 97 | rr.auth=false; |
e693ff5a | 98 | rr.d_place = signPlace; |
ef7cd021 | 99 | for(RRSIGRecordContent& rrc : rrcs) { |
6b35b541 BH |
100 | rr.content = rrc.getZoneRepresentation(); |
101 | outsigned.push_back(rr); | |
102 | } | |
882358c8 | 103 | } |
882358c8 BH |
104 | toSign.clear(); |
105 | } | |
106 | ||
ccc3f9ed BH |
107 | static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER; |
108 | typedef map<pair<string, string>, string> signaturecache_t; | |
109 | static signaturecache_t g_signatures; | |
110 | static int g_cacheweekno; | |
882358c8 | 111 | |
c681ff64 | 112 | AtomicCounter* g_signatureCount; |
113 | ||
e903706d | 114 | uint64_t signatureCacheSize(const std::string& str) |
115 | { | |
116 | ReadLock l(&g_signatures_lock); | |
117 | return g_signatures.size(); | |
118 | } | |
119 | ||
561434a6 | 120 | void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign) |
882358c8 | 121 | { |
c681ff64 | 122 | if(!g_signatureCount) |
123 | g_signatureCount = S.getPointer("signatures"); | |
124 | ||
699e6e37 | 125 | DNSKEYRecordContent drc = dpk.getDNSKEY(); |
8d9f38f2 | 126 | const DNSCryptoKeyEngine* rc = dpk.getKey(); |
882358c8 BH |
127 | rrc.d_tag = drc.getTag(); |
128 | rrc.d_algorithm = drc.d_algorithm; | |
8d9f38f2 | 129 | |
f309dacd | 130 | string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign |
ccc3f9ed | 131 | pair<string, string> lookup(rc->getPubKeyHash(), pdns_md5sum(msg)); // this hash is a memory saving exercise |
6e015d20 | 132 | |
bec14a20 BH |
133 | bool doCache=1; |
134 | if(doCache) | |
882358c8 | 135 | { |
ccc3f9ed BH |
136 | ReadLock l(&g_signatures_lock); |
137 | signaturecache_t::const_iterator iter = g_signatures.find(lookup); | |
138 | if(iter != g_signatures.end()) { | |
139 | rrc.d_signature=iter->second; | |
882358c8 BH |
140 | return; |
141 | } | |
e125fc69 | 142 | // else cerr<<"Miss!"<<endl; |
882358c8 BH |
143 | } |
144 | ||
f309dacd | 145 | rrc.d_signature = rc->sign(msg); |
c681ff64 | 146 | (*g_signatureCount)++; |
bec14a20 | 147 | if(doCache) { |
ff1040bf | 148 | /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */ |
222efdc0 | 149 | int weekno = (time(0) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us |
bba84134 | 150 | const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX); |
a732a4c9 | 151 | |
152 | WriteLock l(&g_signatures_lock); | |
bba84134 KM |
153 | if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04 |
154 | L<<Logger::Warning<<"Cleared signature cache."<<endl; | |
ccc3f9ed BH |
155 | g_signatures.clear(); |
156 | g_cacheweekno = weekno; | |
157 | } | |
bec14a20 BH |
158 | g_signatures[lookup] = rrc.d_signature; |
159 | } | |
882358c8 | 160 | } |
e02d0a59 BH |
161 | |
162 | static bool rrsigncomp(const DNSResourceRecord& a, const DNSResourceRecord& b) | |
163 | { | |
dca357a1 | 164 | return tie(a.d_place, a.qtype) < tie(b.d_place, b.qtype); |
e02d0a59 BH |
165 | } |
166 | ||
675fa24c | 167 | static bool getBestAuthFromSet(const set<DNSName>& authSet, const DNSName& name, DNSName& auth) |
8d3cbffa | 168 | { |
675fa24c PD |
169 | auth.trimToLabels(0); |
170 | DNSName sname(name); | |
8d3cbffa BH |
171 | do { |
172 | if(authSet.find(sname) != authSet.end()) { | |
173 | auth = sname; | |
174 | return true; | |
175 | } | |
176 | } | |
675fa24c | 177 | while(sname.chopOff()); |
8d3cbffa BH |
178 | |
179 | return false; | |
180 | } | |
181 | ||
675fa24c | 182 | void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const set<DNSName>& authSet, vector<DNSResourceRecord>& rrs) |
e02d0a59 | 183 | { |
e02d0a59 BH |
184 | stable_sort(rrs.begin(), rrs.end(), rrsigncomp); |
185 | ||
675fa24c | 186 | DNSName signQName, wildcardQName; |
e02d0a59 BH |
187 | uint16_t signQType=0; |
188 | uint32_t signTTL=0; | |
794c2f92 | 189 | uint32_t origTTL=0; |
e02d0a59 | 190 | |
e693ff5a | 191 | DNSResourceRecord::Place signPlace=DNSResourceRecord::ANSWER; |
e02d0a59 BH |
192 | vector<shared_ptr<DNSRecordContent> > toSign; |
193 | ||
194 | vector<DNSResourceRecord> signedRecords; | |
d42896fb | 195 | signedRecords.reserve(rrs.size()*1.5); |
196 | // cout<<rrs.size()<<", "<<sizeof(DNSResourceRecord)<<endl; | |
675fa24c | 197 | DNSName signer; |
e02d0a59 | 198 | for(vector<DNSResourceRecord>::const_iterator pos = rrs.begin(); pos != rrs.end(); ++pos) { |
e02d0a59 | 199 | if(pos != rrs.begin() && (signQType != pos->qtype.getCode() || signQName != pos->qname)) { |
8d3cbffa | 200 | if(getBestAuthFromSet(authSet, signQName, signer)) |
794c2f92 | 201 | addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL); |
e02d0a59 | 202 | } |
8e9b7d99 | 203 | signedRecords.push_back(*pos); |
d42896fb | 204 | signQName= pos->qname.makeLowerCase(); |
e1a9ab9f | 205 | if(!pos->wildcardname.empty()) |
d42896fb | 206 | wildcardQName = pos->wildcardname.makeLowerCase(); |
07338ade KM |
207 | else |
208 | wildcardQName.clear(); | |
e02d0a59 | 209 | signQType = pos ->qtype.getCode(); |
794c2f92 PD |
210 | if(pos->signttl) |
211 | signTTL = pos->signttl; | |
212 | else | |
213 | signTTL = pos->ttl; | |
214 | origTTL = pos->ttl; | |
e693ff5a | 215 | signPlace = pos->d_place; |
e02d0a59 | 216 | if(pos->auth || pos->qtype.getCode() == QType::DS) { |
6b35b541 | 217 | string content = pos->content; |
e02d0a59 BH |
218 | if(!pos->content.empty() && pos->qtype.getCode()==QType::TXT && pos->content[0]!='"') { |
219 | content="\""+pos->content+"\""; | |
220 | } | |
221 | if(pos->content.empty()) // empty contents confuse the MOADNS setup | |
222 | content="."; | |
223 | ||
224 | shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, content)); | |
225 | toSign.push_back(drc); | |
226 | } | |
227 | } | |
8d3cbffa | 228 | if(getBestAuthFromSet(authSet, signQName, signer)) |
794c2f92 | 229 | addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL); |
e02d0a59 BH |
230 | rrs.swap(signedRecords); |
231 | } |