]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnssecinfra.cc
Merge pull request #14195 from rgacogne/ddist-no-assertions
[thirdparty/pdns.git] / pdns / dnssecinfra.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 <functional>
26 #include "dnsparser.hh"
27 #include "sstuff.hh"
28 #include "misc.hh"
29 #include "dnswriter.hh"
30 #include "dnsrecords.hh"
31 #ifndef RECURSOR
32 #include "statbag.hh"
33 #endif
34 #include "iputils.hh"
35
36 #include <boost/algorithm/string.hpp>
37 #include "dnssecinfra.hh"
38 #include "dnsseckeeper.hh"
39 #include <openssl/hmac.h>
40 #include <openssl/sha.h>
41 #include <boost/assign/std/vector.hpp> // for 'operator+=()'
42 #include <boost/assign/list_inserter.hpp>
43 #include "base64.hh"
44 #include "namespaces.hh"
45 #ifdef HAVE_P11KIT1
46 #include "pkcs11signers.hh"
47 #endif
48 #include "gss_context.hh"
49 #include "misc.hh"
50
51 using namespace boost::assign;
52
53 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname)
54 {
55 string sline, isc;
56 auto filePtr = pdns::UniqueFilePtr(fopen(fname, "r"));
57 if(!filePtr) {
58 throw runtime_error("Unable to read file '"+string(fname)+"' for generating DNS Private Key");
59 }
60
61 while(stringfgets(filePtr.get(), sline)) {
62 isc += sline;
63 }
64 filePtr.reset();
65
66 auto dke = makeFromISCString(drc, isc);
67 auto checkKeyErrors = std::vector<std::string>{};
68
69 if(!dke->checkKey(checkKeyErrors)) {
70 string reason;
71 if(!checkKeyErrors.empty()) {
72 reason = " ("+boost::algorithm::join(checkKeyErrors, ", ")+")";
73 }
74 throw runtime_error("Invalid DNS Private Key in file '"+string(fname)+"'"+reason);
75 }
76 return dke;
77 }
78
79 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content)
80 {
81 enum class KeyTypes : uint8_t { str, numeric, base64 };
82 const std::map<std::string, KeyTypes> knownKeys = {
83 { "algorithm", KeyTypes::numeric },
84 { "modulus", KeyTypes::base64 },
85 { "publicexponent", KeyTypes::base64 },
86 { "privateexponent", KeyTypes::base64 },
87 { "prime1", KeyTypes::base64 },
88 { "prime2", KeyTypes::base64 },
89 { "exponent1", KeyTypes::base64 },
90 { "exponent2", KeyTypes::base64 },
91 { "coefficient", KeyTypes::base64 },
92 { "privatekey", KeyTypes::base64 },
93 { "engine", KeyTypes::str },
94 { "slot", KeyTypes::str },
95 { "pin", KeyTypes::str },
96 { "label", KeyTypes::str },
97 { "publabel", KeyTypes::str },
98 { "private-key-format", KeyTypes::str },
99 { "flags", KeyTypes::numeric }
100 };
101 unsigned int algorithm = 0;
102 string sline, key, value, raw;
103 std::istringstream str(content);
104 map<string, string> stormap;
105
106 while (std::getline(str, sline)) {
107 std::tie(key,value) = splitField(sline, ':');
108 boost::trim(value);
109
110 toLowerInPlace(key);
111 const auto it = knownKeys.find(key);
112 if (it != knownKeys.cend()) {
113 if (it->second == KeyTypes::str) {
114 stormap[key] = value;
115 }
116 else if (it->second == KeyTypes::base64) {
117 try {
118 raw.clear();
119 B64Decode(value, raw);
120 stormap[key] = raw;
121 }
122 catch (const std::exception& e) {
123 throw std::runtime_error("Error while trying to base64 decode the value of the '" + key + "' key from the ISC map: " + e.what());
124 }
125 }
126 else if (it->second == KeyTypes::numeric) {
127 try {
128 auto num = pdns::checked_stoi<unsigned int>(value);
129 stormap[key] = std::to_string(num);
130 if (key == "algorithm") {
131 algorithm = num;
132 }
133 }
134 catch (const std::exception& e) {
135 throw std::runtime_error("Error while trying to parse the numeric value of the '" + key + "' key from the ISC map: " + e.what());
136 }
137 }
138 }
139 else {
140 try {
141 raw.clear();
142 B64Decode(value, raw);
143 stormap[key] = raw;
144 }
145 catch (const std::exception& e) {
146 stormap[key] = value;
147 }
148 }
149 }
150
151 std::unique_ptr<DNSCryptoKeyEngine> dpk;
152
153 if (stormap.count("engine")) {
154 #ifdef HAVE_P11KIT1
155 if (stormap.count("slot") == 0) {
156 throw PDNSException("Cannot load PKCS#11 key, no Slot specified");
157 }
158 // we need PIN to be at least empty
159 if (stormap.count("pin") == 0) {
160 stormap["pin"] = "";
161 }
162 dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm);
163 #else
164 throw PDNSException("Cannot load PKCS#11 key without support for it");
165 #endif
166 } else {
167 dpk = make(algorithm);
168 }
169 dpk->fromISCMap(drc, stormap);
170 return dpk;
171 }
172
173 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMFile(DNSKEYRecordContent& drc, const uint8_t algorithm, std::FILE& inputFile, const std::string& filename)
174 {
175 auto maker = DNSCryptoKeyEngine::make(algorithm);
176 maker->createFromPEMFile(drc, inputFile, filename);
177 return maker;
178 }
179
180 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent& drc, uint8_t algorithm, const std::string& contents)
181 {
182 auto maker = DNSCryptoKeyEngine::make(algorithm);
183 maker->createFromPEMString(drc, contents);
184 return maker;
185 }
186
187 std::string DNSCryptoKeyEngine::convertToISC() const
188 {
189 storvector_t storvector = this->convertToISCVector();
190 ostringstream ret;
191 ret << "Private-key-format: v1.2\n";
192 for (const storvector_t::value_type& value : storvector) {
193 // clang-format off
194 if(value.first != "Algorithm" && value.first != "PIN" &&
195 value.first != "Slot" && value.first != "Engine" &&
196 value.first != "Label" && value.first != "PubLabel") {
197 ret << value.first << ": " << Base64Encode(value.second) << "\n";
198 }
199 else {
200 ret << value.first << ": " << value.second << "\n";
201 }
202 // clang-format on
203 }
204 return ret.str();
205 }
206
207 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::make(unsigned int algo)
208 {
209 const makers_t& makers = getMakers();
210
211 auto iter = makers.find(algo);
212 if (iter != makers.cend()) {
213 return (iter->second)(algo);
214 }
215
216 throw runtime_error("Request to create key object for unknown algorithm number " + std::to_string(algo));
217 }
218
219 /**
220 * Returns the supported DNSSEC algorithms with the name of the Crypto Backend used
221 *
222 * @return A vector with pairs of (algorithm-number (int), backend-name (string))
223 */
224 vector<pair<uint8_t, string>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
225 {
226 vector<pair<uint8_t, string>> ret;
227 for (auto const& value : getMakers()) {
228 auto dcke = value.second(value.first);
229 ret.emplace_back(value.first, dcke->getName());
230 }
231 return ret;
232 }
233
234 string DNSCryptoKeyEngine::listSupportedAlgoNames()
235 {
236 set<unsigned int> algos;
237 auto pairs = DNSCryptoKeyEngine::listAllAlgosWithBackend();
238 for (const auto& pair : pairs) {
239 algos.insert(pair.first);
240 }
241 string ret;
242 bool first = true;
243 for (auto algo : algos) {
244 if (!first) {
245 ret.append(" ");
246 }
247 else {
248 first = false;
249 }
250 ret.append(DNSSECKeeper::algorithm2name(algo));
251 if (isAlgorithmSwitchedOff(algo)) {
252 ret.append("(disabled)");
253 }
254 }
255 ret.append("\n");
256 return ret;
257 }
258
259 void DNSCryptoKeyEngine::report(unsigned int algo, maker_t* maker, bool fallback)
260 {
261 getAllMakers()[algo].push_back(maker);
262 if (getMakers().count(algo) != 0 && fallback) {
263 return;
264 }
265 getMakers()[algo] = maker;
266 }
267
268 bool DNSCryptoKeyEngine::testAll()
269 {
270 bool ret=true;
271
272 for(const allmakers_t::value_type& value : getAllMakers())
273 {
274 for(maker_t* creator : value.second) {
275
276 for(maker_t* signer : value.second) {
277 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
278
279 for(maker_t* verifier : value.second) {
280 try {
281 testMakers(value.first, creator, signer, verifier);
282 }
283 catch(std::exception& e)
284 {
285 cerr<<e.what()<<endl;
286 ret=false;
287 }
288 }
289 }
290 }
291 }
292 return ret;
293 }
294
295 bool DNSCryptoKeyEngine::testOne(int algo)
296 {
297 bool ret=true;
298
299 for(maker_t* creator : getAllMakers()[algo]) {
300
301 for(maker_t* signer : getAllMakers()[algo]) {
302 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
303
304 for(maker_t* verifier : getAllMakers()[algo]) {
305 try {
306 testMakers(algo, creator, signer, verifier);
307 }
308 catch(std::exception& e)
309 {
310 cerr<<e.what()<<endl;
311 ret=false;
312 }
313 }
314 }
315 }
316 return ret;
317 }
318
319 static map<string, string> ISCStringtoMap(const string& argStr)
320 {
321 unsigned int algorithm = 0;
322 string sline;
323 string key;
324 string value;
325 string raw;
326 std::istringstream str(argStr);
327 map<string, string> stormap;
328
329 while(std::getline(str, sline)) {
330 std::tie(key,value)=splitField(sline, ':');
331 boost::trim(value);
332 if(pdns_iequals(key,"algorithm")) {
333 pdns::checked_stoi_into(algorithm, value);
334 stormap["algorithm"] = std::to_string(algorithm);
335 continue;
336 }
337 if (pdns_iequals(key,"pin")) {
338 stormap["pin"] = value;
339 continue;
340 }
341 if (pdns_iequals(key,"engine")) {
342 stormap["engine"] = value;
343 continue;
344 }
345 if (pdns_iequals(key,"slot")) {
346 int slot = std::stoi(value);
347 stormap["slot"]=std::to_string(slot);
348 continue;
349 }
350 if (pdns_iequals(key,"label")) {
351 stormap["label"] = value;
352 continue;
353 }
354 if(pdns_iequals(key, "Private-key-format")) {
355 continue;
356 }
357 raw.clear();
358 B64Decode(value, raw);
359 stormap[toLower(key)] = raw;
360 }
361 return stormap;
362 }
363
364 bool DNSCryptoKeyEngine::testVerify(unsigned int algo, maker_t* verifier)
365 {
366 const string message("Hi! How is life?");
367 const string pubkey5 = "AwEAAe2srzo8UfPx5WwoRXTRdo0H8U4iYW6qneronwKlRtXrpOqgZWPtYGVZl1Q7JXqbxxH9aVK5iK6aYOVfxbwwGHejaY0NraqrxL60F5FhHGHg+zox1en8kEX2TcQHxoZaiK1iUgPkMrHJlX5yI5+p2V4qap5VPQsR/WfeFVudNsBEF/XRvg0Exh65fPI/e8sYNgAiflzdN9/5RM644r6viBdieuwUNwEV2HPizCBMssYzx2F29CqNseToqCKQlj1tghuGAsiiSKeosfDLlRPDe/uxtij0wqe0FNybj1oL3OG8Lq3xp8yXIG4CF59xmRDKdnGDmVycKzUWkVOZpesCsUU=";
368 const string sig5 = "nMnMakbQiiCKIYsEiv4R75+8wvjQav2LPGIKucbqUZUz5sy1ovc2Pp7JVcOuyVyzQu5XH+CetDnTlqiEJWFHNU1jqEwwFK83GVOLABtvXSOvgmGwZGnHOouAchkrzgSSBoEh3+UUN3OsFZA21q6TZVRJBNBm7Ch/PxqSBkFS46ko/qLAUJ1p7/ymzwGNhuOfguHO3dAJ+LgcrNGLZQFDJ1aqT3kZ7LtXX2CQdd7EXgUs6VkE4Z3JN1RmPTk8kAJdZ4JLUR6lgu1dRlSPLGzqv+5d1yI7+h+B0LFNuDdQblDlBstO3LEs1KSaQld+TqVExpjj87oEg6wL/G/XOGabmQ==";
369
370 const string pubkey7 = "AwEAAc4n7xPG6yJe6YAsg6oQ+7YjbL7wuDLCP4juOSaDsst2Mehc5eYdT7xJT2H9foTIq7ABkkp8Er1Bh6gDzB/0xvArARdH6DS3P5pUP6w5Zoz4Gu79y3pP6IsR3ZyhiQRSnht1ElnIGZzb1zpi7Y4Y8LZ18NYN2qdLasXx/h6hpRjdcF1s7svZKvfJdvCSgDHHD/JFtDGSOn6qt6i5UFSrObxMUMWbxfOsnqr/eXUQcF/aePdqDXO47yDaSH8sFZoglgvEDiOIkky9DV5VKamvVW8anxE5Vv7y4EPpZKXB3CgUW+NvaoasdgYPFmGM4EcnXh2EFFnSPDL6iwDubiL7s2k=";
371 const string sig7 = "B04Oqmh/nF6BybBGsInauTXH6nlW3VhT2PeSzXVaxQ42QsbbXUgIKuzp2/R7diiEBzbbQ3Eg5vtHOKfEQDkArmOR1oU6yIkyrKHsJkpCvclCyaFiJXrwxkH+A2y8vB+loeDMJKJVwjn7fH9zwBI3Mk7SFuOgYXgzBUNhb5DeQ9RzRbxMcpSc8Cgtjn+QpmTNgL6olpBNsStYz9bSLXBk1EGhmZeBYhliw/2Fse75OoRxIuufKiN6sAD5bKQxp73QQUU+yunVuSeHJizNct8b4f9RXFe49wtZWt5rB0oYXG6zUv0Dq7xJHpUq6v1eB2wf2NucftCKwWu18r4TxkVC5A==";
372
373 string b64pubkey;
374 string b64sig;
375 switch (algo) {
376 case DNSSECKeeper::RSASHA1:
377 b64pubkey = pubkey5;
378 b64sig = sig5;
379 break;
380 case DNSSECKeeper::RSASHA1NSEC3SHA1:
381 b64pubkey = pubkey7;
382 b64sig = sig7;
383 break;
384 default:
385 throw runtime_error("Verification of verifier called for unimplemented case");
386 }
387
388 string pubkey;
389 string sig;
390 B64Decode(b64pubkey, pubkey);
391 B64Decode(b64sig, sig);
392 auto dckeVerify = verifier(algo);
393 dckeVerify->fromPublicKeyString(pubkey);
394
395 auto ret = dckeVerify->verify(message, sig);
396 return ret;
397 }
398
399 bool DNSCryptoKeyEngine::verifyOne(unsigned int algo)
400 {
401 const auto& makers = getAllMakers();
402 auto iter = makers.find(algo);
403 // No algo found
404 if (iter == makers.cend()) {
405 return false;
406 }
407 // Algo found, but maker empty? Should not happen
408 if (iter->second.empty()) {
409 return false;
410 }
411 // Check that all maker->verify return true
412 return std::all_of(iter->second.begin(), iter->second.end(), [algo](maker_t* verifier) {
413 try {
414 if (!testVerify(algo, verifier)) {
415 return false;
416 }
417 }
418 catch (std::exception& e) {
419 return false;
420 }
421 return true;
422 });
423 }
424
425 void DNSCryptoKeyEngine::testMakers(unsigned int algo, maker_t* creator, maker_t* signer, maker_t* verifier)
426 {
427 auto dckeCreate = creator(algo);
428 auto dckeSign = signer(algo);
429 auto dckeVerify = verifier(algo);
430
431 cout<<"Testing algorithm "<<algo<<"("<<DNSSECKeeper::algorithm2name(algo)<<"): '"<<dckeCreate->getName()<<"' ->'"<<dckeSign->getName()<<"' -> '"<<dckeVerify->getName()<<"' ";
432 unsigned int bits;
433 if(algo <= 10)
434 bits=2048;
435 else if(algo == DNSSECKeeper::ECCGOST || algo == DNSSECKeeper::ECDSA256 || algo == DNSSECKeeper::ED25519)
436 bits = 256;
437 else if(algo == DNSSECKeeper::ECDSA384)
438 bits = 384;
439 else if(algo == DNSSECKeeper::ED448)
440 bits = 456;
441 else
442 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo));
443
444 DTime dt; dt.set();
445 for(unsigned int n = 0; n < 100; ++n)
446 dckeCreate->create(bits);
447 cout<<"("<<dckeCreate->getBits()<<" bits) ";
448 unsigned int udiffCreate = dt.udiff() / 100;
449
450 {
451 DNSKEYRecordContent dkrc;
452 auto stormap = ISCStringtoMap(dckeCreate->convertToISC());
453
454 dckeSign->fromISCMap(dkrc, stormap);
455 if(!dckeSign->checkKey()) {
456 throw runtime_error("Verification of key with creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
457 }
458 }
459
460 string message("Hi! How is life?");
461
462 string signature;
463 dt.set();
464 for(unsigned int n = 0; n < 100; ++n)
465 signature = dckeSign->sign(message);
466 unsigned int udiffSign= dt.udiff()/100, udiffVerify;
467
468 dckeVerify->fromPublicKeyString(dckeSign->getPublicKeyString());
469 if (dckeVerify->getPublicKeyString().compare(dckeSign->getPublicKeyString())) {
470 throw runtime_error("Comparison of public key loaded into verifier produced by signer failed");
471 }
472 dt.set();
473 bool verified;
474 for(unsigned int n = 0; n < 100; ++n)
475 verified = dckeVerify->verify(message, signature);
476
477 if(verified) {
478 udiffVerify = dt.udiff() / 100;
479 cout<<"Signature & verify ok, create "<<udiffCreate<<"us, signature "<<udiffSign<<"us, verify "<<udiffVerify<<"us"<<endl;
480 }
481 else {
482 throw runtime_error("Verification of creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
483 }
484 }
485
486 std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm, const std::string& content)
487 {
488 auto dpk = make(algorithm);
489 dpk->fromPublicKeyString(content);
490 return dpk;
491 }
492
493 /**
494 * Returns the string that should be hashed to create/verify the RRSIG content
495 *
496 * @param qname DNSName of the RRSIG's owner name.
497 * @param rrc The RRSIGRecordContent we take the Type Covered and
498 * original TTL fields from.
499 * @param signRecords A vector of DNSRecordContent shared_ptr's that are covered
500 * by the RRSIG, where we get the RDATA from.
501 * @param processRRSIGLabels A boolean to trigger processing the RRSIG's "Labels"
502 * field. This is usually only needed for validation
503 * purposes, as the authoritative server correctly
504 * sets qname to the wildcard.
505 */
506 string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, const sortedRecords_t& signRecords, bool processRRSIGLabels, bool includeRRSIG_RDATA)
507 {
508 string toHash;
509
510 // dnssec: signature = sign(RRSIG_RDATA | RR(1) | RR(2)... )
511 // From RFC 4034
512 // RRSIG_RDATA is the wire format of the RRSIG RDATA fields
513 // with the Signer's Name field in canonical form and
514 // the Signature field excluded;
515 // zonemd: digest = hash( RR(1) | RR(2) | RR(3) | ... ), so skip RRSIG_RDATA
516
517 if (includeRRSIG_RDATA) {
518 toHash.append(rrc.serialize(g_rootdnsname, true, true));
519 toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature!
520 }
521 string nameToHash(qname.toDNSStringLC());
522
523 if (processRRSIGLabels) {
524 unsigned int rrsig_labels = rrc.d_labels;
525 unsigned int fqdn_labels = qname.countLabels();
526
527 if (rrsig_labels < fqdn_labels) {
528 DNSName choppedQname(qname);
529 while (choppedQname.countLabels() > rrsig_labels) {
530 choppedQname.chopOff();
531 }
532 nameToHash = "\x01*" + choppedQname.toDNSStringLC();
533 } else if (rrsig_labels > fqdn_labels) {
534 // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG
535 // can never be valid
536 return "";
537 }
538 }
539
540 for (const shared_ptr<const DNSRecordContent>& add : signRecords) {
541 toHash.append(nameToHash);
542 uint16_t tmp=htons(rrc.d_type);
543 toHash.append((char*)&tmp, 2);
544 tmp=htons(1); // class
545 toHash.append((char*)&tmp, 2);
546 uint32_t ttl=htonl(rrc.d_originalttl);
547 toHash.append((char*)&ttl, 4);
548 // for NSEC signatures, we should not lowercase the rdata section
549 string rdata=add->serialize(g_rootdnsname, true, (add->getType() == QType::NSEC) ? false : true); // RFC 6840, 5.1
550 tmp=htons(rdata.length());
551 toHash.append((char*)&tmp, 2);
552 toHash.append(rdata);
553 }
554
555 return toHash;
556 }
557
558 std::unordered_set<unsigned int> DNSCryptoKeyEngine::s_switchedOff;
559
560 bool DNSCryptoKeyEngine::isAlgorithmSwitchedOff(unsigned int algo)
561 {
562 return s_switchedOff.count(algo) != 0;
563 }
564
565 void DNSCryptoKeyEngine::switchOffAlgorithm(unsigned int algo)
566 {
567 s_switchedOff.insert(algo);
568 }
569
570 bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo)
571 {
572 if (isAlgorithmSwitchedOff(algo)) {
573 return false;
574 }
575 const makers_t& makers = getMakers();
576 auto iter = makers.find(algo);
577 return iter != makers.cend();
578 }
579
580 static unsigned int digestToAlgorithmNumber(uint8_t digest)
581 {
582 switch(digest) {
583 case DNSSECKeeper::DIGEST_SHA1:
584 return DNSSECKeeper::RSASHA1;
585 case DNSSECKeeper::DIGEST_SHA256:
586 return DNSSECKeeper::RSASHA256;
587 case DNSSECKeeper::DIGEST_GOST:
588 return DNSSECKeeper::ECCGOST;
589 case DNSSECKeeper::DIGEST_SHA384:
590 return DNSSECKeeper::ECDSA384;
591 default:
592 throw std::runtime_error("Unknown digest type " + std::to_string(digest));
593 }
594 return 0;
595 }
596
597 bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest)
598 {
599 try {
600 unsigned int algo = digestToAlgorithmNumber(digest);
601 return isAlgorithmSupported(algo);
602 }
603 catch(const std::exception& e) {
604 return false;
605 }
606 }
607
608 DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest)
609 {
610 string toHash;
611 toHash.assign(qname.toDNSStringLC());
612 toHash.append(drc.serialize(DNSName(), true, true));
613
614 DSRecordContent dsrc;
615 try {
616 unsigned int algo = digestToAlgorithmNumber(digest);
617 auto dpk = DNSCryptoKeyEngine::make(algo);
618 dsrc.d_digest = dpk->hash(toHash);
619 }
620 catch(const std::exception& e) {
621 throw std::runtime_error("Asked to create (C)DS record of unknown digest type " + std::to_string(digest) + ": " + e.what());
622 }
623
624 dsrc.d_algorithm = drc.d_algorithm;
625 dsrc.d_digesttype = digest;
626 dsrc.d_tag = const_cast<DNSKEYRecordContent&>(drc).getTag();
627
628 return dsrc;
629 }
630
631
632 static DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr<DNSCryptoKeyEngine>& pk, uint8_t algorithm, uint16_t flags)
633 {
634 DNSKEYRecordContent drc;
635
636 drc.d_protocol=3;
637 drc.d_algorithm = algorithm;
638
639 drc.d_flags=flags;
640 drc.d_key = pk->getPublicKeyString();
641
642 return drc;
643 }
644
645 uint32_t getStartOfWeek()
646 {
647 // coverity[store_truncates_time_t]
648 uint32_t now = time(nullptr);
649 now -= (now % (7*86400));
650 return now;
651 }
652
653 string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname)
654 {
655 return hashQNameWithSalt(ns3prc.d_salt, ns3prc.d_iterations, qname);
656 }
657
658 string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname)
659 {
660 // rfc5155 section 5
661 unsigned int times = iterations;
662 unsigned char hash[SHA_DIGEST_LENGTH];
663 string toHash(qname.toDNSStringLC() + salt);
664
665 for (;;) {
666 /* so the first time we hash the (lowercased) qname plus the salt,
667 then the result of the last iteration plus the salt */
668 SHA1(reinterpret_cast<const unsigned char*>(toHash.c_str()), toHash.length(), hash);
669 if (!times--) {
670 /* we are done, just copy the result and return it */
671 toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash));
672 break;
673 }
674 if (times == (iterations-1)) {
675 /* first time, we need to replace the qname + salt with
676 the hash plus salt, since the qname will not likely
677 match the size of the hash */
678 if (toHash.capacity() < (sizeof(hash) + salt.size())) {
679 toHash.reserve(sizeof(hash) + salt.size());
680 }
681 toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash));
682 toHash.append(salt);
683 }
684 else {
685 /* starting with the second iteration, the hash size does not change, so we don't need to copy the salt again */
686 std::copy(reinterpret_cast<char*>(hash), reinterpret_cast<char*>(hash) + sizeof(hash), toHash.begin());
687 }
688 }
689
690 return toHash;
691 }
692
693 void incrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
694 {
695 if(raw.empty())
696 return;
697
698 for(string::size_type pos=raw.size(); pos; ) {
699 --pos;
700 unsigned char c = (unsigned char)raw[pos];
701 ++c;
702 raw[pos] = (char) c;
703 if(c)
704 break;
705 }
706 }
707
708 void decrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
709 {
710 if(raw.empty())
711 return;
712
713 for(string::size_type pos=raw.size(); pos; ) {
714 --pos;
715 unsigned char c = (unsigned char)raw[pos];
716 --c;
717 raw[pos] = (char) c;
718 if(c != 0xff)
719 break;
720 }
721 }
722
723 const DNSKEYRecordContent& DNSSECPrivateKey::getDNSKEY() const
724 {
725 return d_dnskey;
726 }
727
728 void DNSSECPrivateKey::computeDNSKEY()
729 {
730 d_dnskey = makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags);
731 }
732
733 static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) {
734
735 const EVP_MD* md_type;
736 unsigned int outlen;
737 unsigned char hash[EVP_MAX_MD_SIZE];
738 switch(hasher) {
739 case TSIG_MD5:
740 md_type = EVP_md5();
741 break;
742 case TSIG_SHA1:
743 md_type = EVP_sha1();
744 break;
745 case TSIG_SHA224:
746 md_type = EVP_sha224();
747 break;
748 case TSIG_SHA256:
749 md_type = EVP_sha256();
750 break;
751 case TSIG_SHA384:
752 md_type = EVP_sha384();
753 break;
754 case TSIG_SHA512:
755 md_type = EVP_sha512();
756 break;
757 default:
758 throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
759 }
760
761 unsigned char* out = HMAC(md_type, reinterpret_cast<const unsigned char*>(key.c_str()), key.size(), reinterpret_cast<const unsigned char*>(text.c_str()), text.size(), hash, &outlen);
762 if (out == nullptr || outlen == 0) {
763 throw PDNSException("HMAC computation failed");
764 }
765
766 return string((char*) hash, outlen);
767 }
768
769 static string makeTSIGPayload(const string& previous, const char* packetBegin, size_t packetSize, const DNSName& tsigKeyName, const TSIGRecordContent& trc, bool timersonly)
770 {
771 string message;
772
773 if(!previous.empty()) {
774 uint16_t len = htons(previous.length());
775 message.append(reinterpret_cast<const char*>(&len), sizeof(len));
776 message.append(previous);
777 }
778
779 message.append(packetBegin, packetSize);
780
781 vector<uint8_t> signVect;
782 DNSPacketWriter dw(signVect, DNSName(), 0);
783 auto pos=signVect.size();
784 if(!timersonly) {
785 dw.xfrName(tsigKeyName, false);
786 dw.xfr16BitInt(QClass::ANY); // class
787 dw.xfr32BitInt(0); // TTL
788 dw.xfrName(trc.d_algoName.makeLowerCase(), false);
789 }
790
791 uint32_t now = trc.d_time;
792 dw.xfr48BitInt(now);
793 dw.xfr16BitInt(trc.d_fudge); // fudge
794 if(!timersonly) {
795 dw.xfr16BitInt(trc.d_eRcode); // extended rcode
796 dw.xfr16BitInt(trc.d_otherData.length()); // length of 'other' data
797 // dw.xfrBlob(trc->d_otherData);
798 }
799 message.append(signVect.begin()+pos, signVect.end());
800 return message;
801 }
802
803 static string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0)
804 {
805 string message;
806 string packet(opacket);
807
808 packet.resize(tsigOffset); // remove the TSIG record at the end as per RFC2845 3.4.1
809 packet[(dnsHeaderOffset + sizeof(struct dnsheader))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line.
810
811
812 // Replace the message ID with the original message ID from the TSIG record.
813 // This is needed for forwarded DNS Update as they get a new ID when forwarding (section 6.1 of RFC2136). The TSIG record stores the original ID and the
814 // signature was created with the original ID, so we replace it here to get the originally signed message.
815 // If the message is not forwarded, we simply override it with the same id.
816 uint16_t origID = htons(trc.d_origID);
817 packet.replace(0, 2, (char*)&origID, 2);
818
819 return makeTSIGPayload(previous, packet.data(), packet.size(), keyname, trc, timersonly);
820 }
821
822 void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly)
823 {
824 TSIGHashEnum algo;
825 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
826 throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toLogString());
827 }
828
829 string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly);
830
831 if (algo == TSIG_GSS) {
832 if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) {
833 throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toLogString()+string("'"));
834 }
835 } else {
836 trc.d_mac = calculateHMAC(tsigsecret, toSign, algo);
837 // trc.d_mac[0]++; // sabotage
838 }
839 pw.startRecord(tsigkeyname, QType::TSIG, 0, QClass::ANY, DNSResourceRecord::ADDITIONAL, false);
840 trc.toPacket(pw);
841 pw.commit();
842 }
843
844 bool validateTSIG(const std::string& packet, size_t sigPos, const TSIGTriplet& tt, const TSIGRecordContent& trc, const std::string& previousMAC, const std::string& theirMAC, bool timersOnly, unsigned int dnsHeaderOffset)
845 {
846 uint64_t delta = std::abs((int64_t)trc.d_time - (int64_t)time(nullptr));
847 if(delta > trc.d_fudge) {
848 throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta) + " > fudge " + std::to_string(trc.d_fudge));
849 }
850
851 TSIGHashEnum algo;
852 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
853 throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toLogString());
854 }
855
856 TSIGHashEnum expectedAlgo;
857 if (!getTSIGHashEnum(tt.algo, expectedAlgo)) {
858 throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toLogString());
859 }
860
861 if (algo != expectedAlgo) {
862 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' does not match the expected algorithm (" + tt.algo.toLogString() + " / " + trc.d_algoName.toLogString() + ")");
863 }
864
865 string tsigMsg;
866 tsigMsg = makeTSIGMessageFromTSIGPacket(packet, sigPos, tt.name, trc, previousMAC, timersOnly, dnsHeaderOffset);
867
868 if (algo == TSIG_GSS) {
869 GssContext gssctx(tt.name);
870 if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) {
871 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
872 }
873 } else {
874 string ourMac = calculateHMAC(tt.secret, tsigMsg, algo);
875
876 if(!constantTimeStringEquals(ourMac, theirMAC)) {
877 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
878 }
879 }
880
881 return true;
882 }