]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnssecinfra.cc
Introduce DNSCryptoKeyEngine::makeFromPEMString
[thirdparty/pdns.git] / pdns / dnssecinfra.cc
CommitLineData
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
f7b9d3b7 25#include <functional>
4691b2df
BH
26#include "dnsparser.hh"
27#include "sstuff.hh"
28#include "misc.hh"
29#include "dnswriter.hh"
30#include "dnsrecords.hh"
acbfb1c1 31#ifndef RECURSOR
4691b2df 32#include "statbag.hh"
acbfb1c1 33#endif
4691b2df 34#include "iputils.hh"
fa8fd4d2 35
4691b2df 36#include <boost/algorithm/string.hpp>
1e05b07c 37#include "dnssecinfra.hh"
4691b2df 38#include "dnsseckeeper.hh"
5e8e902d
CH
39#include <openssl/hmac.h>
40#include <openssl/sha.h>
673208a2
BH
41#include <boost/assign/std/vector.hpp> // for 'operator+=()'
42#include <boost/assign/list_inserter.hpp>
f309dacd 43#include "base64.hh"
9e04108d 44#include "namespaces.hh"
8daea594
AT
45#ifdef HAVE_P11KIT1
46#include "pkcs11signers.hh"
47#endif
b08f1315 48#include "gss_context.hh"
da15912b 49#include "misc.hh"
8daea594 50
673208a2
BH
51using namespace boost::assign;
52
a2c6e554 53std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname)
4691b2df 54{
f309dacd 55 string sline, isc;
5e1f23ca 56 auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname, "r"), fclose);
699e6e37
BH
57 if(!fp) {
58 throw runtime_error("Unable to read file '"+string(fname)+"' for generating DNS Private Key");
59 }
1e05b07c 60
5e1f23ca 61 while(stringfgets(fp.get(), sline)) {
f309dacd 62 isc += sline;
699e6e37 63 }
5e1f23ca
RG
64 fp.reset();
65
a2c6e554 66 auto dke = makeFromISCString(drc, isc);
f7b9d3b7 67 auto checkKeyErrors = std::vector<std::string>{};
be135eeb 68
f7b9d3b7 69 if(!dke->checkKey(checkKeyErrors)) {
be135eeb 70 string reason;
f7b9d3b7 71 if(!checkKeyErrors.empty()) {
be135eeb
PD
72 reason = " ("+boost::algorithm::join(checkKeyErrors, ", ")+")";
73 }
74 throw runtime_error("Invalid DNS Private Key in file '"+string(fname)+"'"+reason);
8ca3ea33
RG
75 }
76 return dke;
f309dacd 77}
4691b2df 78
a2c6e554 79std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content)
f309dacd 80{
3b9e71ab
RG
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;
f309dacd 102 string sline, key, value, raw;
9e04108d 103 std::istringstream str(content);
f309dacd 104 map<string, string> stormap;
495155bf 105
3b9e71ab 106 while (std::getline(str, sline)) {
1a09e47b 107 std::tie(key,value) = splitField(sline, ':');
dc593046 108 boost::trim(value);
3b9e71ab
RG
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 {
a0383aad 128 auto num = pdns::checked_stoi<unsigned int>(value);
3b9e71ab
RG
129 stormap[key] = std::to_string(num);
130 if (key == "algorithm") {
131 algorithm = num;
132 }
133 }
134 catch (const std::exception& e) {
1c83d0da 135 throw std::runtime_error("Error while trying to parse the numeric value of the '" + key + "' key from the ISC map: " + e.what());
3b9e71ab
RG
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 }
f309dacd 148 }
f309dacd 149 }
3b9e71ab 150
a2c6e554 151 std::unique_ptr<DNSCryptoKeyEngine> dpk;
8daea594 152
3b9e71ab 153 if (stormap.count("engine")) {
8daea594 154#ifdef HAVE_P11KIT1
3b9e71ab 155 if (stormap.count("slot") == 0) {
f0a0745b 156 throw PDNSException("Cannot load PKCS#11 key, no Slot specified");
3b9e71ab 157 }
f0a0745b 158 // we need PIN to be at least empty
3b9e71ab
RG
159 if (stormap.count("pin") == 0) {
160 stormap["pin"] = "";
161 }
1e05b07c 162 dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm);
8daea594 163#else
70f0f8c4 164 throw PDNSException("Cannot load PKCS#11 key without support for it");
8daea594
AT
165#endif
166 } else {
a2c6e554 167 dpk = make(algorithm);
8daea594 168 }
f309dacd 169 dpk->fromISCMap(drc, stormap);
022e5e0b
BH
170 return dpk;
171}
172
e959b65f 173std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMFile(DNSKEYRecordContent& drc, const uint8_t algorithm, std::FILE& inputFile, const std::string& filename)
06cb6907 174{
87066241 175 auto maker = DNSCryptoKeyEngine::make(algorithm);
0186c941 176 maker->createFromPEMFile(drc, inputFile, filename);
87066241 177 return maker;
06cb6907
FM
178}
179
3de36d82
FM
180std::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
189bb9d2
BH
187std::string DNSCryptoKeyEngine::convertToISC() const
188{
d35497e5 189 storvector_t storvector = this->convertToISCVector();
189bb9d2 190 ostringstream ret;
fdb8aa96
FM
191 ret << "Private-key-format: v1.2\n";
192 for (const storvector_t::value_type& value : storvector) {
193 // clang-format off
1e05b07c 194 if(value.first != "Algorithm" && value.first != "PIN" &&
8daea594 195 value.first != "Slot" && value.first != "Engine" &&
fdb8aa96
FM
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
189bb9d2
BH
203 }
204 return ret.str();
205}
f309dacd 206
a2c6e554 207std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::make(unsigned int algo)
022e5e0b 208{
8455425c 209 const makers_t& makers = getMakers();
926eb28c
FM
210
211 auto iter = makers.find(algo);
0eadf4db 212 if (iter != makers.cend()) {
022e5e0b 213 return (iter->second)(algo);
0eadf4db 214 }
926eb28c
FM
215
216 throw runtime_error("Request to create key object for unknown algorithm number " + std::to_string(algo));
699e6e37 217}
4691b2df 218
98d13a90
PL
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 */
224vector<pair<uint8_t, string>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
225{
226 vector<pair<uint8_t, string>> ret;
227 for (auto const& value : getMakers()) {
a2c6e554 228 auto dcke = value.second(value.first);
e32a8d46 229 ret.emplace_back(value.first, dcke->getName());
98d13a90
PL
230 }
231 return ret;
232}
233
8d9f38f2 234void DNSCryptoKeyEngine::report(unsigned int algo, maker_t* maker, bool fallback)
022e5e0b 235{
189bb9d2 236 getAllMakers()[algo].push_back(maker);
d2e1a4e0 237 if (getMakers().count(algo) != 0 && fallback) {
f309dacd 238 return;
4691b2df 239 }
2c281453 240 getMakers()[algo] = maker;
699e6e37
BH
241}
242
166d8647 243bool DNSCryptoKeyEngine::testAll()
189bb9d2 244{
166d8647
KM
245 bool ret=true;
246
ef7cd021 247 for(const allmakers_t::value_type& value : getAllMakers())
189bb9d2 248 {
ef7cd021 249 for(maker_t* creator : value.second) {
530b4335 250
ef7cd021 251 for(maker_t* signer : value.second) {
530b4335 252 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
1e05b07c 253
ef7cd021 254 for(maker_t* verifier : value.second) {
530b4335 255 try {
f558c987 256 testMakers(value.first, creator, signer, verifier);
530b4335
PD
257 }
258 catch(std::exception& e)
259 {
260 cerr<<e.what()<<endl;
166d8647 261 ret=false;
530b4335 262 }
189bb9d2
BH
263 }
264 }
265 }
266 }
166d8647 267 return ret;
189bb9d2
BH
268}
269
166d8647 270bool DNSCryptoKeyEngine::testOne(int algo)
cbb0025b 271{
166d8647
KM
272 bool ret=true;
273
ef7cd021 274 for(maker_t* creator : getAllMakers()[algo]) {
530b4335 275
ef7cd021 276 for(maker_t* signer : getAllMakers()[algo]) {
cbb0025b
PD
277 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
278
ef7cd021 279 for(maker_t* verifier : getAllMakers()[algo]) {
cbb0025b 280 try {
f558c987 281 testMakers(algo, creator, signer, verifier);
cbb0025b
PD
282 }
283 catch(std::exception& e)
284 {
285 cerr<<e.what()<<endl;
166d8647 286 ret=false;
cbb0025b
PD
287 }
288 }
289 }
530b4335 290 }
166d8647 291 return ret;
cbb0025b 292}
f558c987
PD
293
294void DNSCryptoKeyEngine::testMakers(unsigned int algo, maker_t* creator, maker_t* signer, maker_t* verifier)
189bb9d2 295{
a2c6e554
RG
296 auto dckeCreate = creator(algo);
297 auto dckeSign = signer(algo);
298 auto dckeVerify = verifier(algo);
530b4335 299
66afe5d4 300 cout<<"Testing algorithm "<<algo<<"("<<DNSSECKeeper::algorithm2name(algo)<<"): '"<<dckeCreate->getName()<<"' ->'"<<dckeSign->getName()<<"' -> '"<<dckeVerify->getName()<<"' ";
189bb9d2
BH
301 unsigned int bits;
302 if(algo <= 10)
cc835c12 303 bits=2048;
902c4e9c
CH
304 else if(algo == DNSSECKeeper::ECCGOST || algo == DNSSECKeeper::ECDSA256 || algo == DNSSECKeeper::ED25519)
305 bits = 256;
306 else if(algo == DNSSECKeeper::ECDSA384)
45826dd7 307 bits = 384;
902c4e9c 308 else if(algo == DNSSECKeeper::ED448)
21a8834a 309 bits = 456;
45826dd7 310 else
335da0ba 311 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo));
45826dd7 312
f558c987
PD
313 DTime dt; dt.set();
314 for(unsigned int n = 0; n < 100; ++n)
315 dckeCreate->create(bits);
66afe5d4 316 cout<<"("<<dckeCreate->getBits()<<" bits) ";
f558c987 317 unsigned int udiffCreate = dt.udiff() / 100;
530b4335
PD
318
319 { // FIXME: this block copy/pasted from makeFromISCString
320 DNSKEYRecordContent dkrc;
d4bc94c2 321 unsigned int algorithm = 0;
530b4335
PD
322 string sline, key, value, raw;
323 std::istringstream str(dckeCreate->convertToISC());
324 map<string, string> stormap;
325
326 while(std::getline(str, sline)) {
1a09e47b 327 std::tie(key,value)=splitField(sline, ':');
dc593046 328 boost::trim(value);
530b4335 329 if(pdns_iequals(key,"algorithm")) {
a0383aad 330 pdns::checked_stoi_into(algorithm, value);
335da0ba 331 stormap["algorithm"]=std::to_string(algorithm);
530b4335 332 continue;
8daea594
AT
333 } else if (pdns_iequals(key,"pin")) {
334 stormap["pin"]=value;
335 continue;
336 } else if (pdns_iequals(key,"engine")) {
337 stormap["engine"]=value;
338 continue;
339 } else if (pdns_iequals(key,"slot")) {
335da0ba
AT
340 int slot = std::stoi(value);
341 stormap["slot"]=std::to_string(slot);
8daea594
AT
342 continue;
343 } else if (pdns_iequals(key,"label")) {
344 stormap["label"]=value;
345 continue;
530b4335
PD
346 }
347 else if(pdns_iequals(key, "Private-key-format"))
348 continue;
349 raw.clear();
350 B64Decode(value, raw);
351 stormap[toLower(key)]=raw;
352 }
353 dckeSign->fromISCMap(dkrc, stormap);
45c2bc60
RG
354 if(!dckeSign->checkKey()) {
355 throw runtime_error("Verification of key with creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
8ca3ea33 356 }
530b4335
PD
357 }
358
189bb9d2 359 string message("Hi! How is life?");
1e05b07c 360
189bb9d2 361 string signature;
f558c987 362 dt.set();
f011d0ad
BH
363 for(unsigned int n = 0; n < 100; ++n)
364 signature = dckeSign->sign(message);
365 unsigned int udiffSign= dt.udiff()/100, udiffVerify;
1e05b07c 366
189bb9d2 367 dckeVerify->fromPublicKeyString(dckeSign->getPublicKeyString());
7fbe4163
CH
368 if (dckeVerify->getPublicKeyString().compare(dckeSign->getPublicKeyString())) {
369 throw runtime_error("Comparison of public key loaded into verifier produced by signer failed");
370 }
189bb9d2 371 dt.set();
f558c987
PD
372 bool verified;
373 for(unsigned int n = 0; n < 100; ++n)
374 verified = dckeVerify->verify(message, signature);
375
376 if(verified) {
377 udiffVerify = dt.udiff() / 100;
66afe5d4 378 cout<<"Signature & verify ok, create "<<udiffCreate<<"usec, signature "<<udiffSign<<"usec, verify "<<udiffVerify<<"usec"<<endl;
189bb9d2
BH
379 }
380 else {
530b4335 381 throw runtime_error("Verification of creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
189bb9d2 382 }
189bb9d2
BH
383}
384
a2c6e554 385std::unique_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm, const std::string& content)
aa65a832 386{
a2c6e554 387 auto dpk = make(algorithm);
189bb9d2 388 dpk->fromPublicKeyString(content);
aa65a832
BH
389 return dpk;
390}
391
125058a0
PL
392/**
393 * Returns the string that should be hashed to create/verify the RRSIG content
394 *
395 * @param qname DNSName of the RRSIG's owner name.
396 * @param rrc The RRSIGRecordContent we take the Type Covered and
397 * original TTL fields from.
398 * @param signRecords A vector of DNSRecordContent shared_ptr's that are covered
399 * by the RRSIG, where we get the RDATA from.
400 * @param processRRSIGLabels A boolean to trigger processing the RRSIG's "Labels"
401 * field. This is usually only needed for validation
402 * purposes, as the authoritative server correctly
403 * sets qname to the wildcard.
404 */
e7ea7d4e 405string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, const sortedRecords_t& signRecords, bool processRRSIGLabels, bool includeRRSIG_RDATA)
4691b2df 406{
4691b2df 407 string toHash;
4691b2df 408
e7ea7d4e
O
409 // dnssec: signature = sign(RRSIG_RDATA | RR(1) | RR(2)... )
410 // From RFC 4034
411 // RRSIG_RDATA is the wire format of the RRSIG RDATA fields
412 // with the Signer's Name field in canonical form and
413 // the Signature field excluded;
414 // zonemd: digest = hash( RR(1) | RR(2) | RR(3) | ... ), so skip RRSIG_RDATA
415
416 if (includeRRSIG_RDATA) {
417 toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize(g_rootdnsname, true, true));
418 toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature!
419 }
125058a0
PL
420 string nameToHash(qname.toDNSStringLC());
421
422 if (processRRSIGLabels) {
423 unsigned int rrsig_labels = rrc.d_labels;
424 unsigned int fqdn_labels = qname.countLabels();
425
426 if (rrsig_labels < fqdn_labels) {
427 DNSName choppedQname(qname);
428 while (choppedQname.countLabels() > rrsig_labels)
429 choppedQname.chopOff();
430 nameToHash = "\x01*" + choppedQname.toDNSStringLC();
431 } else if (rrsig_labels > fqdn_labels) {
432 // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG
433 // can never be valid
434 return "";
435 }
436 }
437
c1e7b833 438 for(const shared_ptr<DNSRecordContent>& add : signRecords) {
125058a0 439 toHash.append(nameToHash);
4691b2df
BH
440 uint16_t tmp=htons(rrc.d_type);
441 toHash.append((char*)&tmp, 2);
442 tmp=htons(1); // class
443 toHash.append((char*)&tmp, 2);
444 uint32_t ttl=htonl(rrc.d_originalttl);
445 toHash.append((char*)&ttl, 4);
feb53a77 446 // for NSEC signatures, we should not lowercase the rdata section
12c06211 447 string rdata=add->serialize(g_rootdnsname, true, (add->getType() == QType::NSEC) ? false : true); // RFC 6840, 5.1
4691b2df
BH
448 tmp=htons(rdata.length());
449 toHash.append((char*)&tmp, 2);
450 toHash.append(rdata);
451 }
1e05b07c 452
f309dacd 453 return toHash;
4691b2df
BH
454}
455
8455425c
RG
456bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo)
457{
458 const makers_t& makers = getMakers();
459 makers_t::const_iterator iter = makers.find(algo);
460 return iter != makers.cend();
461}
462
463static unsigned int digestToAlgorithmNumber(uint8_t digest)
464{
465 switch(digest) {
690b86b7 466 case DNSSECKeeper::DIGEST_SHA1:
8455425c 467 return DNSSECKeeper::RSASHA1;
690b86b7 468 case DNSSECKeeper::DIGEST_SHA256:
8455425c 469 return DNSSECKeeper::RSASHA256;
690b86b7 470 case DNSSECKeeper::DIGEST_GOST:
8455425c 471 return DNSSECKeeper::ECCGOST;
690b86b7 472 case DNSSECKeeper::DIGEST_SHA384:
8455425c
RG
473 return DNSSECKeeper::ECDSA384;
474 default:
475 throw std::runtime_error("Unknown digest type " + std::to_string(digest));
476 }
477 return 0;
478}
479
480bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest)
481{
482 try {
483 unsigned int algo = digestToAlgorithmNumber(digest);
484 return isAlgorithmSupported(algo);
485 }
486 catch(const std::exception& e) {
487 return false;
488 }
489}
490
491DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest)
4691b2df
BH
492{
493 string toHash;
1e05b07c 494 toHash.assign(qname.toDNSStringLC());
290a083d 495 toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize(DNSName(), true, true));
1e05b07c 496
4691b2df 497 DSRecordContent dsrc;
8455425c
RG
498 try {
499 unsigned int algo = digestToAlgorithmNumber(digest);
a2c6e554 500 auto dpk = DNSCryptoKeyEngine::make(algo);
39315b97
BH
501 dsrc.d_digest = dpk->hash(toHash);
502 }
8455425c 503 catch(const std::exception& e) {
793bc32f 504 throw std::runtime_error("Asked to create (C)DS record of unknown digest type " + std::to_string(digest) + ": " + e.what());
8455425c 505 }
1e05b07c 506
8455425c
RG
507 dsrc.d_algorithm = drc.d_algorithm;
508 dsrc.d_digesttype = digest;
509 dsrc.d_tag = const_cast<DNSKEYRecordContent&>(drc).getTag();
8daea594 510
4691b2df
BH
511 return dsrc;
512}
513
4691b2df 514
7afd3f74 515static DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr<DNSCryptoKeyEngine>& pk, uint8_t algorithm, uint16_t flags)
699e6e37
BH
516{
517 DNSKEYRecordContent drc;
8daea594 518
4691b2df 519 drc.d_protocol=3;
c3c89361 520 drc.d_algorithm = algorithm;
4691b2df 521
4c1474f3 522 drc.d_flags=flags;
699e6e37 523 drc.d_key = pk->getPublicKeyString();
8daea594 524
4691b2df
BH
525 return drc;
526}
527
b61e407d 528uint32_t getStartOfWeek()
4691b2df 529{
b59b334d 530 // coverity[store_truncates_time_t]
4646277d 531 uint32_t now = time(nullptr);
4691b2df
BH
532 now -= (now % (7*86400));
533 return now;
534}
535
28e2e78e 536string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname)
4691b2df 537{
e4805005 538 return hashQNameWithSalt(ns3prc.d_salt, ns3prc.d_iterations, qname);
539}
540
541string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname)
542{
2dc20546 543 // rfc5155 section 5
e4805005 544 unsigned int times = iterations;
2880bfe6
RG
545 unsigned char hash[SHA_DIGEST_LENGTH];
546 string toHash(qname.toDNSStringLC() + salt);
2880bfe6
RG
547
548 for (;;) {
549 /* so the first time we hash the (lowercased) qname plus the salt,
550 then the result of the last iteration plus the salt */
551 SHA1(reinterpret_cast<const unsigned char*>(toHash.c_str()), toHash.length(), hash);
552 if (!times--) {
553 /* we are done, just copy the result and return it */
554 toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash));
28e2e78e 555 break;
2880bfe6
RG
556 }
557 if (times == (iterations-1)) {
558 /* first time, we need to replace the qname + salt with
559 the hash plus salt, since the qname will not likely
560 match the size of the hash */
a2e73018
RG
561 if (toHash.capacity() < (sizeof(hash) + salt.size())) {
562 toHash.reserve(sizeof(hash) + salt.size());
563 }
2880bfe6
RG
564 toHash.assign(reinterpret_cast<char*>(hash), sizeof(hash));
565 toHash.append(salt);
566 }
567 else {
568 /* starting with the second iteration, the hash size does not change, so we don't need to copy the salt again */
569 std::copy(reinterpret_cast<char*>(hash), reinterpret_cast<char*>(hash) + sizeof(hash), toHash.begin());
570 }
4691b2df 571 }
2880bfe6 572
28e2e78e 573 return toHash;
4691b2df 574}
28e2e78e 575
95823c07
RG
576void incrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
577{
578 if(raw.empty())
579 return;
580
581 for(string::size_type pos=raw.size(); pos; ) {
582 --pos;
583 unsigned char c = (unsigned char)raw[pos];
584 ++c;
585 raw[pos] = (char) c;
586 if(c)
587 break;
588 }
589}
590
591void decrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
592{
593 if(raw.empty())
594 return;
595
596 for(string::size_type pos=raw.size(); pos; ) {
597 --pos;
598 unsigned char c = (unsigned char)raw[pos];
599 --c;
600 raw[pos] = (char) c;
601 if(c != 0xff)
602 break;
603 }
604}
605
a3260095 606const DNSKEYRecordContent& DNSSECPrivateKey::getDNSKEY() const
673208a2 607{
a3260095
RG
608 return d_dnskey;
609}
610
611void DNSSECPrivateKey::computeDNSKEY()
612{
613 d_dnskey = makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags);
673208a2 614}
ed3f8559 615
ea3816cf 616static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) {
78bcb858 617
5e8e902d
CH
618 const EVP_MD* md_type;
619 unsigned int outlen;
620 unsigned char hash[EVP_MAX_MD_SIZE];
621 switch(hasher) {
622 case TSIG_MD5:
623 md_type = EVP_md5();
624 break;
625 case TSIG_SHA1:
626 md_type = EVP_sha1();
627 break;
628 case TSIG_SHA224:
629 md_type = EVP_sha224();
630 break;
631 case TSIG_SHA256:
632 md_type = EVP_sha256();
633 break;
634 case TSIG_SHA384:
635 md_type = EVP_sha384();
636 break;
637 case TSIG_SHA512:
638 md_type = EVP_sha512();
639 break;
640 default:
60a1c204 641 throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
5e8e902d
CH
642 }
643
644 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);
4646277d 645 if (out == nullptr || outlen == 0) {
60a1c204 646 throw PDNSException("HMAC computation failed");
5e8e902d 647 }
5e8e902d 648
60a1c204
RG
649 return string((char*) hash, outlen);
650}
651
ea3816cf 652static string makeTSIGPayload(const string& previous, const char* packetBegin, size_t packetSize, const DNSName& tsigKeyName, const TSIGRecordContent& trc, bool timersonly)
01cb2fe2
BH
653{
654 string message;
c48dec72 655
01cb2fe2
BH
656 if(!previous.empty()) {
657 uint16_t len = htons(previous.length());
ea3816cf 658 message.append(reinterpret_cast<const char*>(&len), sizeof(len));
01cb2fe2
BH
659 message.append(previous);
660 }
ea3816cf
RG
661
662 message.append(packetBegin, packetSize);
01cb2fe2
BH
663
664 vector<uint8_t> signVect;
290a083d 665 DNSPacketWriter dw(signVect, DNSName(), 0);
57ddc8ba 666 auto pos=signVect.size();
01cb2fe2 667 if(!timersonly) {
ea3816cf 668 dw.xfrName(tsigKeyName, false);
f4d26b4f 669 dw.xfr16BitInt(QClass::ANY); // class
01cb2fe2 670 dw.xfr32BitInt(0); // TTL
68e9d647 671 dw.xfrName(trc.d_algoName.makeLowerCase(), false);
01cb2fe2 672 }
1e05b07c
FM
673
674 uint32_t now = trc.d_time;
01cb2fe2
BH
675 dw.xfr48BitInt(now);
676 dw.xfr16BitInt(trc.d_fudge); // fudge
677 if(!timersonly) {
678 dw.xfr16BitInt(trc.d_eRcode); // extended rcode
679 dw.xfr16BitInt(trc.d_otherData.length()); // length of 'other' data
680 // dw.xfrBlob(trc->d_otherData);
681 }
57ddc8ba 682 message.append(signVect.begin()+pos, signVect.end());
01cb2fe2
BH
683 return message;
684}
685
ea3816cf 686static string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0)
01cb2fe2 687{
ea3816cf
RG
688 string message;
689 string packet(opacket);
981fa489 690
ea3816cf
RG
691 packet.resize(tsigOffset); // remove the TSIG record at the end as per RFC2845 3.4.1
692 packet[(dnsHeaderOffset + sizeof(struct dnsheader))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line.
1e05b07c 693
ea3816cf
RG
694
695 // Replace the message ID with the original message ID from the TSIG record.
696 // 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
697 // signature was created with the original ID, so we replace it here to get the originally signed message.
698 // If the message is not forwarded, we simply override it with the same id.
699 uint16_t origID = htons(trc.d_origID);
700 packet.replace(0, 2, (char*)&origID, 2);
701
702 return makeTSIGPayload(previous, packet.data(), packet.size(), keyname, trc, timersonly);
703}
704
705void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly)
706{
707 TSIGHashEnum algo;
708 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
86f1af1c 709 throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toLogString());
01cb2fe2 710 }
ea3816cf
RG
711
712 string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly);
01cb2fe2 713
7f9ac49b 714 if (algo == TSIG_GSS) {
b08f1315
OM
715 if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) {
716 throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toLogString()+string("'"));
717 }
7f9ac49b 718 } else {
ea3816cf
RG
719 trc.d_mac = calculateHMAC(tsigsecret, toSign, algo);
720 // trc.d_mac[0]++; // sabotage
7f9ac49b 721 }
e693ff5a 722 pw.startRecord(tsigkeyname, QType::TSIG, 0, QClass::ANY, DNSResourceRecord::ADDITIONAL, false);
ea3816cf 723 trc.toPacket(pw);
01cb2fe2
BH
724 pw.commit();
725}
8daea594 726
ea3816cf
RG
727bool 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)
728{
729 uint64_t delta = std::abs((int64_t)trc.d_time - (int64_t)time(nullptr));
730 if(delta > trc.d_fudge) {
731 throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta) + " > fudge " + std::to_string(trc.d_fudge));
732 }
733
734 TSIGHashEnum algo;
735 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
86f1af1c 736 throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toLogString());
ea3816cf
RG
737 }
738
739 TSIGHashEnum expectedAlgo;
740 if (!getTSIGHashEnum(tt.algo, expectedAlgo)) {
86f1af1c 741 throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toLogString());
ea3816cf
RG
742 }
743
744 if (algo != expectedAlgo) {
86f1af1c 745 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' does not match the expected algorithm (" + tt.algo.toLogString() + " / " + trc.d_algoName.toLogString() + ")");
ea3816cf
RG
746 }
747
748 string tsigMsg;
749 tsigMsg = makeTSIGMessageFromTSIGPacket(packet, sigPos, tt.name, trc, previousMAC, timersOnly, dnsHeaderOffset);
750
751 if (algo == TSIG_GSS) {
b08f1315
OM
752 GssContext gssctx(tt.name);
753 if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) {
754 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
755 }
ea3816cf
RG
756 } else {
757 string ourMac = calculateHMAC(tt.secret, tsigMsg, algo);
758
759 if(!constantTimeStringEquals(ourMac, theirMAC)) {
86f1af1c 760 throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
ea3816cf
RG
761 }
762 }
763
764 return true;
765}