]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnssecsigner.cc
Merge pull request #5426 from stasic/postgres-bigint
[thirdparty/pdns.git] / pdns / dnssecsigner.cc
1 /*
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "dnssecinfra.hh"
26 #include "namespaces.hh"
27
28 #include "md5.hh"
29 #include "dnsseckeeper.hh"
30 #include "dns_random.hh"
31 #include "lock.hh"
32 #include "arguments.hh"
33 #include "statbag.hh"
34 extern StatBag S;
35
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
41 AtomicCounter* g_signatureCount;
42
43 static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign)
44 {
45 if(!g_signatureCount)
46 g_signatureCount = S.getPointer("signatures");
47
48 DNSKEYRecordContent drc = dpk.getDNSKEY();
49 const std::shared_ptr<DNSCryptoKeyEngine> rc = dpk.getKey();
50 rrc.d_tag = drc.getTag();
51 rrc.d_algorithm = drc.d_algorithm;
52
53 string msg=getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
54 pair<string, string> lookup(rc->getPubKeyHash(), pdns_md5sum(msg)); // this hash is a memory saving exercise
55
56 bool doCache=1;
57 if(doCache)
58 {
59 ReadLock l(&g_signatures_lock);
60 signaturecache_t::const_iterator iter = g_signatures.find(lookup);
61 if(iter != g_signatures.end()) {
62 rrc.d_signature=iter->second;
63 return;
64 }
65 // else cerr<<"Miss!"<<endl;
66 }
67
68 rrc.d_signature = rc->sign(msg);
69 (*g_signatureCount)++;
70 if(doCache) {
71 /* we add some jitter here so not all your slaves start pruning their caches at the very same millisecond */
72 int weekno = (time(0) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us
73 const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
74
75 WriteLock l(&g_signatures_lock);
76 if(g_cacheweekno < weekno || g_signatures.size() >= (uint) maxcachesize) { // blunt but effective (C) Habbie, mind04
77 L<<Logger::Warning<<"Cleared signature cache."<<endl;
78 g_signatures.clear();
79 g_cacheweekno = weekno;
80 }
81 g_signatures[lookup] = rrc.d_signature;
82 }
83 }
84
85 /* this is where the RRSIGs begin, keys are retrieved,
86 but the actual signing happens in fillOutRRSIG */
87 static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName signQName, uint16_t signQType, uint32_t signTTL,
88 vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs)
89 {
90 if(toSign.empty())
91 return -1;
92 uint32_t startOfWeek = getStartOfWeek();
93 RRSIGRecordContent rrc;
94 rrc.d_type=signQType;
95
96 rrc.d_labels=signQName.countLabels()-signQName.isWildcard();
97 rrc.d_originalttl=signTTL;
98 rrc.d_siginception=startOfWeek - 7*86400; // XXX should come from zone metadata
99 rrc.d_sigexpire=startOfWeek + 14*86400;
100 rrc.d_signer = signer;
101 rrc.d_tag = 0;
102
103 DNSSECKeeper::keyset_t keys = dk.getKeys(signer);
104
105 for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) {
106 if(!keymeta.second.active)
107 continue;
108
109 if((signQType == QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::ZSK) ||
110 (signQType != QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::KSK)) {
111 continue;
112 }
113
114 fillOutRRSIG(keymeta.first, signQName, rrc, toSign);
115 rrcs.push_back(rrc);
116 }
117 return 0;
118 }
119
120 // this is the entrypoint from DNSPacket
121 static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName signQName, const DNSName& wildcardname, uint16_t signQType,
122 uint32_t signTTL, DNSResourceRecord::Place signPlace,
123 vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL)
124 {
125 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
126 if(toSign.empty())
127 return;
128 vector<RRSIGRecordContent> rrcs;
129 if(dk.isPresigned(signer)) {
130 //cerr<<"Doing presignatures"<<endl;
131 dk.getPreRRSIGs(db, signer, signQName, wildcardname, QType(signQType), signPlace, outsigned, origTTL); // does it all
132 }
133 else {
134 if(getRRSIGsForRRSET(dk, signer, wildcardname.countLabels() ? wildcardname : signQName, signQType, signTTL, toSign, rrcs) < 0) {
135 // cerr<<"Error signing a record!"<<endl;
136 return;
137 }
138
139 DNSZoneRecord rr;
140 rr.dr.d_name=signQName;
141 rr.dr.d_type=QType::RRSIG;
142 if(origTTL)
143 rr.dr.d_ttl=origTTL;
144 else
145 rr.dr.d_ttl=signTTL;
146 rr.auth=false;
147 rr.dr.d_place = signPlace;
148 for(RRSIGRecordContent& rrc : rrcs) {
149 rr.dr.d_content = std::make_shared<RRSIGRecordContent>(rrc);
150 outsigned.push_back(rr);
151 }
152 }
153 toSign.clear();
154 }
155
156 uint64_t signatureCacheSize(const std::string& str)
157 {
158 ReadLock l(&g_signatures_lock);
159 return g_signatures.size();
160 }
161
162 static bool rrsigncomp(const DNSZoneRecord& a, const DNSZoneRecord& b)
163 {
164 return tie(a.dr.d_place, a.dr.d_type) < tie(b.dr.d_place, b.dr.d_type);
165 }
166
167 static bool getBestAuthFromSet(const set<DNSName>& authSet, const DNSName& name, DNSName& auth)
168 {
169 auth.trimToLabels(0);
170 DNSName sname(name);
171 do {
172 if(authSet.find(sname) != authSet.end()) {
173 auth = sname;
174 return true;
175 }
176 }
177 while(sname.chopOff());
178
179 return false;
180 }
181
182 void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const set<DNSName>& authSet, vector<DNSZoneRecord>& rrs)
183 {
184 stable_sort(rrs.begin(), rrs.end(), rrsigncomp);
185
186 DNSName signQName, wildcardQName;
187 uint16_t signQType=0;
188 uint32_t signTTL=0;
189 uint32_t origTTL=0;
190
191 DNSResourceRecord::Place signPlace=DNSResourceRecord::ANSWER;
192 vector<shared_ptr<DNSRecordContent> > toSign;
193
194 vector<DNSZoneRecord> signedRecords;
195 signedRecords.reserve(rrs.size()*1.5);
196 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
197 DNSName signer;
198 for(auto pos = rrs.cbegin(); pos != rrs.cend(); ++pos) {
199 if(pos != rrs.cbegin() && (signQType != pos->dr.d_type || signQName != pos->dr.d_name)) {
200 if(getBestAuthFromSet(authSet, signQName, signer))
201 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
202 }
203 signedRecords.push_back(*pos);
204 signQName= pos->dr.d_name.makeLowerCase();
205 if(!pos->wildcardname.empty())
206 wildcardQName = pos->wildcardname.makeLowerCase();
207 else
208 wildcardQName.clear();
209 signQType = pos->dr.d_type;
210 if(pos->signttl)
211 signTTL = pos->signttl;
212 else
213 signTTL = pos->dr.d_ttl;
214 origTTL = pos->dr.d_ttl;
215 signPlace = pos->dr.d_place;
216 if(pos->auth || pos->dr.d_type == QType::DS) {
217 toSign.push_back(pos->dr.d_content); // so ponder.. should this be a deep copy perhaps?
218 }
219 }
220 if(getBestAuthFromSet(authSet, signQName, signer))
221 addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL);
222 rrs.swap(signedRecords);
223 }