]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnssecsigner.cc
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"
38 using signaturecache_t
= map
<pair
<string
, string
>, string
>;
39 static SharedLockGuarded
<signaturecache_t
> g_signatures
;
40 static int g_cacheweekno
;
42 const static std::set
<uint16_t> g_KSKSignedQTypes
{QType::DNSKEY
, QType::CDS
, QType::CDNSKEY
};
43 AtomicCounter
* g_signatureCount
;
45 static std::string
getLookupKeyFromMessage(const std::string
& msg
)
48 return pdns::md5(msg
);
50 catch(const std::runtime_error
& e
) {
51 return pdns::sha1(msg
);
55 static std::string
getLookupKeyFromPublicKey(const std::string
& pubKey
)
57 /* arbitrarily cut off at 64 bytes, the main idea is to save space
58 for very large keys like RSA ones (1024+ bits so 128+ bytes) by storing a 20 bytes hash
60 if (pubKey
.size() <= 64) {
63 return pdns::sha1sum(pubKey
);
66 static void fillOutRRSIG(DNSSECPrivateKey
& dpk
, const DNSName
& signQName
, RRSIGRecordContent
& rrc
, const sortedRecords_t
& toSign
)
69 g_signatureCount
= S
.getPointer("signatures");
71 DNSKEYRecordContent drc
= dpk
.getDNSKEY();
72 const std::shared_ptr
<DNSCryptoKeyEngine
>& rc
= dpk
.getKey();
73 rrc
.d_tag
= drc
.getTag();
74 rrc
.d_algorithm
= drc
.d_algorithm
;
76 string msg
= getMessageForRRSET(signQName
, rrc
, toSign
); // this is what we will hash & sign
77 pair
<string
, string
> lookup(getLookupKeyFromPublicKey(drc
.d_key
), getLookupKeyFromMessage(msg
)); // this hash is a memory saving exercise
81 auto signatures
= g_signatures
.read_lock();
82 signaturecache_t::const_iterator iter
= signatures
->find(lookup
);
83 if (iter
!= signatures
->end()) {
84 rrc
.d_signature
=iter
->second
;
87 // else cerr<<"Miss!"<<endl;
90 rrc
.d_signature
= rc
->sign(msg
);
91 (*g_signatureCount
)++;
93 /* we add some jitter here so not all your secondaries start pruning their caches at the very same millisecond */
94 int weekno
= (time(nullptr) - dns_random(3600)) / (86400*7); // we just spent milliseconds doing a signature, microsecond more won't kill us
95 const static int maxcachesize
=::arg().asNum("max-signature-cache-entries", INT_MAX
);
97 auto signatures
= g_signatures
.write_lock();
98 if (g_cacheweekno
< weekno
|| signatures
->size() >= (uint
) maxcachesize
) { // blunt but effective (C) Habbie, mind04
99 g_log
<<Logger::Warning
<<"Cleared signature cache."<<endl
;
101 g_cacheweekno
= weekno
;
103 (*signatures
)[lookup
] = rrc
.d_signature
;
107 /* this is where the RRSIGs begin, keys are retrieved,
108 but the actual signing happens in fillOutRRSIG */
109 static int getRRSIGsForRRSET(DNSSECKeeper
& dk
, const DNSName
& signer
, const DNSName
& signQName
, uint16_t signQType
, uint32_t signTTL
,
110 const sortedRecords_t
& toSign
, vector
<RRSIGRecordContent
>& rrcs
)
114 uint32_t startOfWeek
= getStartOfWeek();
115 RRSIGRecordContent rrc
;
116 rrc
.d_type
=signQType
;
118 rrc
.d_labels
=signQName
.countLabels()-signQName
.isWildcard();
119 rrc
.d_originalttl
=signTTL
;
120 rrc
.d_siginception
=startOfWeek
- 7*86400; // XXX should come from zone metadata
121 rrc
.d_sigexpire
=startOfWeek
+ 14*86400;
122 rrc
.d_signer
= signer
;
125 DNSSECKeeper::keyset_t keys
= dk
.getKeys(signer
);
127 for(DNSSECKeeper::keyset_t::value_type
& keymeta
: keys
) {
128 if(!keymeta
.second
.active
)
131 bool signWithKSK
= g_KSKSignedQTypes
.count(signQType
) != 0;
132 // Do not sign DNSKEY RRsets with the ZSK
133 if((signQType
== QType::DNSKEY
&& keymeta
.second
.keyType
== DNSSECKeeper::ZSK
) ||
134 // Do not sign any other RRset than DNSKEY, CDS and CDNSKEY with a KSK
135 (!signWithKSK
&& keymeta
.second
.keyType
== DNSSECKeeper::KSK
)) {
139 fillOutRRSIG(keymeta
.first
, signQName
, rrc
, toSign
);
145 // this is the entrypoint from DNSPacket
146 static void addSignature(DNSSECKeeper
& dk
, UeberBackend
& db
, const DNSName
& signer
, const DNSName
& signQName
, const DNSName
& wildcardname
, uint16_t signQType
,
147 uint32_t signTTL
, DNSResourceRecord::Place signPlace
,
148 sortedRecords_t
& toSign
, vector
<DNSZoneRecord
>& outsigned
, uint32_t origTTL
)
150 //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
153 vector
<RRSIGRecordContent
> rrcs
;
154 if(dk
.isPresigned(signer
)) {
155 //cerr<<"Doing presignatures"<<endl;
156 dk
.getPreRRSIGs(db
, outsigned
, origTTL
); // does it all
159 if(getRRSIGsForRRSET(dk
, signer
, wildcardname
.countLabels() ? wildcardname
: signQName
, signQType
, signTTL
, toSign
, rrcs
) < 0) {
160 // cerr<<"Error signing a record!"<<endl;
165 rr
.dr
.d_name
=signQName
;
166 rr
.dr
.d_type
=QType::RRSIG
;
172 rr
.dr
.d_place
= signPlace
;
173 for(RRSIGRecordContent
& rrc
: rrcs
) {
174 rr
.dr
.setContent(std::make_shared
<RRSIGRecordContent
>(rrc
));
175 outsigned
.push_back(rr
);
181 uint64_t signatureCacheSize(const std::string
& /* str */)
183 return g_signatures
.read_lock()->size();
186 static bool rrsigncomp(const DNSZoneRecord
& a
, const DNSZoneRecord
& b
)
188 return std::tie(a
.dr
.d_place
, a
.dr
.d_type
) < std::tie(b
.dr
.d_place
, b
.dr
.d_type
);
191 static bool getBestAuthFromSet(const set
<DNSName
>& authSet
, const DNSName
& name
, DNSName
& auth
)
193 auth
.trimToLabels(0);
196 if(authSet
.find(sname
) != authSet
.end()) {
201 while(sname
.chopOff());
206 void addRRSigs(DNSSECKeeper
& dk
, UeberBackend
& db
, const set
<DNSName
>& authSet
, vector
<DNSZoneRecord
>& rrs
)
208 stable_sort(rrs
.begin(), rrs
.end(), rrsigncomp
);
210 DNSName authQName
, signQName
, wildcardQName
;
211 uint16_t signQType
=0;
215 DNSResourceRecord::Place signPlace
=DNSResourceRecord::ANSWER
;
216 sortedRecords_t toSign
;
218 vector
<DNSZoneRecord
> signedRecords
;
219 signedRecords
.reserve(rrs
.size()*1.5);
220 // cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
222 for(auto pos
= rrs
.cbegin(); pos
!= rrs
.cend(); ++pos
) {
223 if(pos
!= rrs
.cbegin() && (signQType
!= pos
->dr
.d_type
|| signQName
!= pos
->dr
.d_name
)) {
224 if (getBestAuthFromSet(authSet
, authQName
, signer
))
225 addSignature(dk
, db
, signer
, signQName
, wildcardQName
, signQType
, signTTL
, signPlace
, toSign
, signedRecords
, origTTL
);
227 signedRecords
.push_back(*pos
);
228 signQName
= pos
->dr
.d_name
.makeLowerCase();
229 if (pos
->dr
.d_type
== QType::NSEC
) {
230 authQName
= signQName
.getCommonLabels(getRR
<NSECRecordContent
>(pos
->dr
)->d_next
);
233 authQName
= signQName
;
235 if(!pos
->wildcardname
.empty())
236 wildcardQName
= pos
->wildcardname
.makeLowerCase();
238 wildcardQName
.clear();
239 signQType
= pos
->dr
.d_type
;
241 signTTL
= pos
->signttl
;
243 signTTL
= pos
->dr
.d_ttl
;
244 origTTL
= pos
->dr
.d_ttl
;
245 signPlace
= pos
->dr
.d_place
;
246 if(pos
->auth
|| pos
->dr
.d_type
== QType::DS
) {
247 toSign
.insert(pos
->dr
.getContent()); // so ponder.. should this be a deep copy perhaps?
250 if (getBestAuthFromSet(authSet
, authQName
, signer
))
251 addSignature(dk
, db
, signer
, signQName
, wildcardQName
, signQType
, signTTL
, signPlace
, toSign
, signedRecords
, origTTL
);
252 rrs
.swap(signedRecords
);