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 const static std::set
<uint16_t> g_KSKSignedQTypes
{QType::DNSKEY
, QType::CDS
, QType::CDNSKEY
};
42 AtomicCounter
* g_signatureCount
;
44 static std::string
getLookupKey(const std::string
& msg
)
47 return pdns_md5sum(msg
);
49 catch(const std::runtime_error
& e
) {
50 return pdns_sha1sum(msg
);
54 static void fillOutRRSIG(DNSSECPrivateKey
& dpk
, const DNSName
& signQName
, RRSIGRecordContent
& rrc
, vector
<shared_ptr
<DNSRecordContent
> >& toSign
)
57 g_signatureCount
= S
.getPointer("signatures");
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
;
64 string msg
=getMessageForRRSET(signQName
, rrc
, toSign
); // this is what we will hash & sign
65 pair
<string
, string
> lookup(rc
->getPubKeyHash(), getLookupKey(msg
)); // this hash is a memory saving exercise
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
;
76 // else cerr<<"Miss!"<<endl;
79 rrc
.d_signature
= rc
->sign(msg
);
80 (*g_signatureCount
)++;
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
);
86 WriteLock
l(&g_signatures_lock
);
87 if(g_cacheweekno
< weekno
|| g_signatures
.size() >= (uint
) maxcachesize
) { // blunt but effective (C) Habbie, mind04
88 g_log
<<Logger::Warning
<<"Cleared signature cache."<<endl
;
90 g_cacheweekno
= weekno
;
92 g_signatures
[lookup
] = rrc
.d_signature
;
96 /* this is where the RRSIGs begin, keys are retrieved,
97 but the actual signing happens in fillOutRRSIG */
98 static int getRRSIGsForRRSET(DNSSECKeeper
& dk
, const DNSName
& signer
, const DNSName
& signQName
, uint16_t signQType
, uint32_t signTTL
,
99 vector
<shared_ptr
<DNSRecordContent
> >& toSign
, vector
<RRSIGRecordContent
>& rrcs
)
103 uint32_t startOfWeek
= getStartOfWeek();
104 RRSIGRecordContent rrc
;
105 rrc
.d_type
=signQType
;
107 rrc
.d_labels
=signQName
.countLabels()-signQName
.isWildcard();
108 rrc
.d_originalttl
=signTTL
;
109 rrc
.d_siginception
=startOfWeek
- 7*86400; // XXX should come from zone metadata
110 rrc
.d_sigexpire
=startOfWeek
+ 14*86400;
111 rrc
.d_signer
= signer
;
114 DNSSECKeeper::keyset_t keys
= dk
.getKeys(signer
);
116 for(DNSSECKeeper::keyset_t::value_type
& keymeta
: keys
) {
117 if(!keymeta
.second
.active
)
120 bool signWithKSK
= g_KSKSignedQTypes
.count(signQType
) != 0;
121 // Do not sign DNSKEY RRsets with the ZSK
122 if((signQType
== QType::DNSKEY
&& keymeta
.second
.keyType
== DNSSECKeeper::ZSK
) ||
123 // Do not sign any other RRset than DNSKEY, CDS and CDNSKEY with a KSK
124 (!signWithKSK
&& keymeta
.second
.keyType
== DNSSECKeeper::KSK
)) {
128 fillOutRRSIG(keymeta
.first
, signQName
, rrc
, toSign
);
134 // this is the entrypoint from DNSPacket
135 static void addSignature(DNSSECKeeper
& dk
, UeberBackend
& db
, const DNSName
& signer
, const DNSName
& signQName
, const DNSName
& wildcardname
, uint16_t signQType
,
136 uint32_t signTTL
, DNSResourceRecord::Place signPlace
,
137 vector
<shared_ptr
<DNSRecordContent
> >& toSign
, vector
<DNSZoneRecord
>& outsigned
, uint32_t origTTL
)
139 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
142 vector
<RRSIGRecordContent
> rrcs
;
143 if(dk
.isPresigned(signer
)) {
144 //cerr<<"Doing presignatures"<<endl;
145 dk
.getPreRRSIGs(db
, signer
, signQName
, wildcardname
, QType(signQType
), signPlace
, outsigned
, origTTL
); // does it all
148 if(getRRSIGsForRRSET(dk
, signer
, wildcardname
.countLabels() ? wildcardname
: signQName
, signQType
, signTTL
, toSign
, rrcs
) < 0) {
149 // cerr<<"Error signing a record!"<<endl;
154 rr
.dr
.d_name
=signQName
;
155 rr
.dr
.d_type
=QType::RRSIG
;
161 rr
.dr
.d_place
= signPlace
;
162 for(RRSIGRecordContent
& rrc
: rrcs
) {
163 rr
.dr
.d_content
= std::make_shared
<RRSIGRecordContent
>(rrc
);
164 outsigned
.push_back(rr
);
170 uint64_t signatureCacheSize(const std::string
& str
)
172 ReadLock
l(&g_signatures_lock
);
173 return g_signatures
.size();
176 static bool rrsigncomp(const DNSZoneRecord
& a
, const DNSZoneRecord
& b
)
178 return tie(a
.dr
.d_place
, a
.dr
.d_type
) < tie(b
.dr
.d_place
, b
.dr
.d_type
);
181 static bool getBestAuthFromSet(const set
<DNSName
>& authSet
, const DNSName
& name
, DNSName
& auth
)
183 auth
.trimToLabels(0);
186 if(authSet
.find(sname
) != authSet
.end()) {
191 while(sname
.chopOff());
196 void addRRSigs(DNSSECKeeper
& dk
, UeberBackend
& db
, const set
<DNSName
>& authSet
, vector
<DNSZoneRecord
>& rrs
)
198 stable_sort(rrs
.begin(), rrs
.end(), rrsigncomp
);
200 DNSName signQName
, wildcardQName
;
201 uint16_t signQType
=0;
205 DNSResourceRecord::Place signPlace
=DNSResourceRecord::ANSWER
;
206 vector
<shared_ptr
<DNSRecordContent
> > toSign
;
208 vector
<DNSZoneRecord
> signedRecords
;
209 signedRecords
.reserve(rrs
.size()*1.5);
210 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
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
)) {
214 if(getBestAuthFromSet(authSet
, signQName
, signer
))
215 addSignature(dk
, db
, signer
, signQName
, wildcardQName
, signQType
, signTTL
, signPlace
, toSign
, signedRecords
, origTTL
);
217 signedRecords
.push_back(*pos
);
218 signQName
= pos
->dr
.d_name
.makeLowerCase();
219 if(!pos
->wildcardname
.empty())
220 wildcardQName
= pos
->wildcardname
.makeLowerCase();
222 wildcardQName
.clear();
223 signQType
= pos
->dr
.d_type
;
225 signTTL
= pos
->signttl
;
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?
234 if(getBestAuthFromSet(authSet
, signQName
, signer
))
235 addSignature(dk
, db
, signer
, signQName
, wildcardQName
, signQType
, signTTL
, signPlace
, toSign
, signedRecords
, origTTL
);
236 rrs
.swap(signedRecords
);