2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include "dnssecinfra.hh"
26 #include "namespaces.hh"
29 #include "dnsseckeeper.hh"
30 #include "dns_random.hh"
32 #include "arguments.hh"
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
;
41 AtomicCounter
* g_signatureCount
;
43 static void fillOutRRSIG(DNSSECPrivateKey
& dpk
, const DNSName
& signQName
, RRSIGRecordContent
& rrc
, vector
<shared_ptr
<DNSRecordContent
> >& toSign
)
46 g_signatureCount
= S
.getPointer("signatures");
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
;
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
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
;
65 // else cerr<<"Miss!"<<endl;
68 rrc
.d_signature
= rc
->sign(msg
);
69 (*g_signatureCount
)++;
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
);
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
;
79 g_cacheweekno
= weekno
;
81 g_signatures
[lookup
] = rrc
.d_signature
;
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
)
92 uint32_t startOfWeek
= getStartOfWeek();
93 RRSIGRecordContent rrc
;
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
;
103 DNSSECKeeper::keyset_t keys
= dk
.getKeys(signer
);
105 for(DNSSECKeeper::keyset_t::value_type
& keymeta
: keys
) {
106 if(!keymeta
.second
.active
)
109 if((signQType
== QType::DNSKEY
&& keymeta
.second
.keyType
== DNSSECKeeper::ZSK
) ||
110 (signQType
!= QType::DNSKEY
&& keymeta
.second
.keyType
== DNSSECKeeper::KSK
)) {
114 fillOutRRSIG(keymeta
.first
, signQName
, rrc
, toSign
);
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
)
125 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
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
134 if(getRRSIGsForRRSET(dk
, signer
, wildcardname
.countLabels() ? wildcardname
: signQName
, signQType
, signTTL
, toSign
, rrcs
) < 0) {
135 // cerr<<"Error signing a record!"<<endl;
140 rr
.dr
.d_name
=signQName
;
141 rr
.dr
.d_type
=QType::RRSIG
;
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
);
156 uint64_t signatureCacheSize(const std::string
& str
)
158 ReadLock
l(&g_signatures_lock
);
159 return g_signatures
.size();
162 static bool rrsigncomp(const DNSZoneRecord
& a
, const DNSZoneRecord
& b
)
164 return tie(a
.dr
.d_place
, a
.dr
.d_type
) < tie(b
.dr
.d_place
, b
.dr
.d_type
);
167 static bool getBestAuthFromSet(const set
<DNSName
>& authSet
, const DNSName
& name
, DNSName
& auth
)
169 auth
.trimToLabels(0);
172 if(authSet
.find(sname
) != authSet
.end()) {
177 while(sname
.chopOff());
182 void addRRSigs(DNSSECKeeper
& dk
, UeberBackend
& db
, const set
<DNSName
>& authSet
, vector
<DNSZoneRecord
>& rrs
)
184 stable_sort(rrs
.begin(), rrs
.end(), rrsigncomp
);
186 DNSName signQName
, wildcardQName
;
187 uint16_t signQType
=0;
191 DNSResourceRecord::Place signPlace
=DNSResourceRecord::ANSWER
;
192 vector
<shared_ptr
<DNSRecordContent
> > toSign
;
194 vector
<DNSZoneRecord
> signedRecords
;
195 signedRecords
.reserve(rrs
.size()*1.5);
196 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
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
);
203 signedRecords
.push_back(*pos
);
204 signQName
= pos
->dr
.d_name
.makeLowerCase();
205 if(!pos
->wildcardname
.empty())
206 wildcardQName
= pos
->wildcardname
.makeLowerCase();
208 wildcardQName
.clear();
209 signQType
= pos
->dr
.d_type
;
211 signTTL
= pos
->signttl
;
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?
220 if(getBestAuthFromSet(authSet
, signQName
, signer
))
221 addSignature(dk
, db
, signer
, signQName
, wildcardQName
, signQType
, signTTL
, signPlace
, toSign
, signedRecords
, origTTL
);
222 rrs
.swap(signedRecords
);