]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnssecinfra.cc
Merge pull request #5523 from rubenk/fix-typos-in-logmessage
[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
4691b2df
BH
25#include "dnsparser.hh"
26#include "sstuff.hh"
27#include "misc.hh"
28#include "dnswriter.hh"
29#include "dnsrecords.hh"
acbfb1c1 30#ifndef RECURSOR
4691b2df 31#include "statbag.hh"
acbfb1c1 32#endif
4691b2df 33#include "iputils.hh"
fa8fd4d2 34
4691b2df 35#include <boost/algorithm/string.hpp>
4691b2df
BH
36#include "dnssecinfra.hh"
37#include "dnsseckeeper.hh"
5e8e902d
CH
38#include <openssl/hmac.h>
39#include <openssl/sha.h>
673208a2
BH
40#include <boost/assign/std/vector.hpp> // for 'operator+=()'
41#include <boost/assign/list_inserter.hpp>
f309dacd 42#include "base64.hh"
9e04108d 43#include "namespaces.hh"
8daea594
AT
44#ifdef HAVE_P11KIT1
45#include "pkcs11signers.hh"
46#endif
7f9ac49b 47#include "gss_context.hh"
da15912b 48#include "misc.hh"
8daea594 49
673208a2
BH
50using namespace boost::assign;
51
e69c2dac 52shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname)
4691b2df 53{
f309dacd 54 string sline, isc;
4691b2df 55 FILE *fp=fopen(fname, "r");
699e6e37
BH
56 if(!fp) {
57 throw runtime_error("Unable to read file '"+string(fname)+"' for generating DNS Private Key");
58 }
f309dacd 59
699e6e37 60 while(stringfgets(fp, sline)) {
f309dacd 61 isc += sline;
699e6e37
BH
62 }
63 fclose(fp);
e69c2dac 64 shared_ptr<DNSCryptoKeyEngine> dke = makeFromISCString(drc, isc);
45c2bc60 65 if(!dke->checkKey()) {
8ca3ea33
RG
66 throw runtime_error("Invalid DNS Private Key in file '"+string(fname));
67 }
68 return dke;
f309dacd 69}
4691b2df 70
e69c2dac 71shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content)
f309dacd 72{
8daea594 73 bool pkcs11=false;
f309dacd
BH
74 int algorithm = 0;
75 string sline, key, value, raw;
9e04108d 76 std::istringstream str(content);
f309dacd 77 map<string, string> stormap;
495155bf 78
9e04108d 79 while(std::getline(str, sline)) {
f309dacd
BH
80 tie(key,value)=splitField(sline, ':');
81 trim(value);
82 if(pdns_iequals(key,"algorithm")) {
335da0ba
AT
83 algorithm = pdns_stou(value);
84 stormap["algorithm"]=std::to_string(algorithm);
f309dacd 85 continue;
8daea594
AT
86 } else if (pdns_iequals(key,"pin")) {
87 stormap["pin"]=value;
88 continue;
89 } else if (pdns_iequals(key,"engine")) {
90 stormap["engine"]=value;
91 pkcs11=true;
92 continue;
93 } else if (pdns_iequals(key,"slot")) {
248d701f 94 stormap["slot"]=value;
8daea594
AT
95 continue;
96 } else if (pdns_iequals(key,"label")) {
97 stormap["label"]=value;
98 continue;
f309dacd
BH
99 }
100 else if(pdns_iequals(key, "Private-key-format"))
101 continue;
102 raw.clear();
103 B64Decode(value, raw);
104 stormap[toLower(key)]=raw;
105 }
e69c2dac 106 shared_ptr<DNSCryptoKeyEngine> dpk;
8daea594
AT
107
108 if (pkcs11) {
109#ifdef HAVE_P11KIT1
f0a0745b
AT
110 if (stormap.find("slot") == stormap.end())
111 throw PDNSException("Cannot load PKCS#11 key, no Slot specified");
112 // we need PIN to be at least empty
113 if (stormap.find("pin") == stormap.end()) stormap["pin"] = "";
8daea594
AT
114 dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm);
115#else
70f0f8c4 116 throw PDNSException("Cannot load PKCS#11 key without support for it");
8daea594
AT
117#endif
118 } else {
119 dpk=make(algorithm);
120 }
f309dacd 121 dpk->fromISCMap(drc, stormap);
022e5e0b
BH
122 return dpk;
123}
124
189bb9d2
BH
125std::string DNSCryptoKeyEngine::convertToISC() const
126{
127 typedef map<string, string> stormap_t;
c99573a5 128 storvector_t stormap = this->convertToISCVector();
189bb9d2 129 ostringstream ret;
c99573a5 130 ret<<"Private-key-format: v1.2\n";
ef7cd021 131 for(const stormap_t::value_type& value : stormap) {
8daea594
AT
132 if(value.first != "Algorithm" && value.first != "PIN" &&
133 value.first != "Slot" && value.first != "Engine" &&
134 value.first != "Label")
c99573a5
BH
135 ret<<value.first<<": "<<Base64Encode(value.second)<<"\n";
136 else
137 ret<<value.first<<": "<<value.second<<"\n";
189bb9d2
BH
138 }
139 return ret.str();
140}
f309dacd 141
e69c2dac 142shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::make(unsigned int algo)
022e5e0b 143{
8455425c 144 const makers_t& makers = getMakers();
022e5e0b 145 makers_t::const_iterator iter = makers.find(algo);
8455425c 146 if(iter != makers.cend())
022e5e0b
BH
147 return (iter->second)(algo);
148 else {
335da0ba 149 throw runtime_error("Request to create key object for unknown algorithm number "+std::to_string(algo));
699e6e37 150 }
699e6e37 151}
4691b2df 152
98d13a90
PL
153/**
154 * Returns the supported DNSSEC algorithms with the name of the Crypto Backend used
155 *
156 * @return A vector with pairs of (algorithm-number (int), backend-name (string))
157 */
158vector<pair<uint8_t, string>> DNSCryptoKeyEngine::listAllAlgosWithBackend()
159{
160 vector<pair<uint8_t, string>> ret;
161 for (auto const& value : getMakers()) {
162 shared_ptr<DNSCryptoKeyEngine> dcke(value.second(value.first));
163 ret.push_back(make_pair(value.first, dcke->getName()));
164 }
165 return ret;
166}
167
8d9f38f2 168void DNSCryptoKeyEngine::report(unsigned int algo, maker_t* maker, bool fallback)
022e5e0b 169{
189bb9d2 170 getAllMakers()[algo].push_back(maker);
f309dacd
BH
171 if(getMakers().count(algo) && fallback) {
172 return;
4691b2df 173 }
f309dacd 174 getMakers()[algo]=maker;
699e6e37
BH
175}
176
166d8647 177bool DNSCryptoKeyEngine::testAll()
189bb9d2 178{
166d8647
KM
179 bool ret=true;
180
ef7cd021 181 for(const allmakers_t::value_type& value : getAllMakers())
189bb9d2 182 {
ef7cd021 183 for(maker_t* creator : value.second) {
530b4335 184
ef7cd021 185 for(maker_t* signer : value.second) {
530b4335
PD
186 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
187
ef7cd021 188 for(maker_t* verifier : value.second) {
530b4335 189 try {
12184d68 190 /* pair<unsigned int, unsigned int> res=*/ testMakers(value.first, creator, signer, verifier);
530b4335
PD
191 }
192 catch(std::exception& e)
193 {
194 cerr<<e.what()<<endl;
166d8647 195 ret=false;
530b4335 196 }
189bb9d2
BH
197 }
198 }
199 }
200 }
166d8647 201 return ret;
189bb9d2
BH
202}
203
166d8647 204bool DNSCryptoKeyEngine::testOne(int algo)
cbb0025b 205{
166d8647
KM
206 bool ret=true;
207
ef7cd021 208 for(maker_t* creator : getAllMakers()[algo]) {
530b4335 209
ef7cd021 210 for(maker_t* signer : getAllMakers()[algo]) {
cbb0025b
PD
211 // multi_map<unsigned int, maker_t*> bestSigner, bestVerifier;
212
ef7cd021 213 for(maker_t* verifier : getAllMakers()[algo]) {
cbb0025b 214 try {
12184d68 215 /* pair<unsigned int, unsigned int> res=*/testMakers(algo, creator, signer, verifier);
cbb0025b
PD
216 }
217 catch(std::exception& e)
218 {
219 cerr<<e.what()<<endl;
166d8647 220 ret=false;
cbb0025b
PD
221 }
222 }
223 }
530b4335 224 }
166d8647 225 return ret;
cbb0025b 226}
12184d68 227// returns times it took to sign and verify
530b4335 228pair<unsigned int, unsigned int> DNSCryptoKeyEngine::testMakers(unsigned int algo, maker_t* creator, maker_t* signer, maker_t* verifier)
189bb9d2 229{
530b4335 230 shared_ptr<DNSCryptoKeyEngine> dckeCreate(creator(algo));
189bb9d2
BH
231 shared_ptr<DNSCryptoKeyEngine> dckeSign(signer(algo));
232 shared_ptr<DNSCryptoKeyEngine> dckeVerify(verifier(algo));
530b4335
PD
233
234 cerr<<"Testing algorithm "<<algo<<": '"<<dckeCreate->getName()<<"' ->'"<<dckeSign->getName()<<"' -> '"<<dckeVerify->getName()<<"' ";
189bb9d2
BH
235 unsigned int bits;
236 if(algo <= 10)
237 bits=1024;
9d3727e0 238 else if(algo == 12 || algo == 13 || algo == 15) // ECC-GOST or ECDSAP256SHA256 or ED25519
189bb9d2 239 bits=256;
45826dd7
KM
240 else if(algo == 14) // ECDSAP384SHA384
241 bits = 384;
21a8834a
KM
242 else if(algo == 16) // ED448
243 bits = 456;
45826dd7 244 else
335da0ba 245 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algo));
45826dd7 246
530b4335
PD
247 dckeCreate->create(bits);
248
249 { // FIXME: this block copy/pasted from makeFromISCString
250 DNSKEYRecordContent dkrc;
251 int algorithm = 0;
252 string sline, key, value, raw;
253 std::istringstream str(dckeCreate->convertToISC());
254 map<string, string> stormap;
255
256 while(std::getline(str, sline)) {
257 tie(key,value)=splitField(sline, ':');
258 trim(value);
259 if(pdns_iequals(key,"algorithm")) {
335da0ba
AT
260 algorithm = pdns_stou(value);
261 stormap["algorithm"]=std::to_string(algorithm);
530b4335 262 continue;
8daea594
AT
263 } else if (pdns_iequals(key,"pin")) {
264 stormap["pin"]=value;
265 continue;
266 } else if (pdns_iequals(key,"engine")) {
267 stormap["engine"]=value;
268 continue;
269 } else if (pdns_iequals(key,"slot")) {
335da0ba
AT
270 int slot = std::stoi(value);
271 stormap["slot"]=std::to_string(slot);
8daea594
AT
272 continue;
273 } else if (pdns_iequals(key,"label")) {
274 stormap["label"]=value;
275 continue;
530b4335
PD
276 }
277 else if(pdns_iequals(key, "Private-key-format"))
278 continue;
279 raw.clear();
280 B64Decode(value, raw);
281 stormap[toLower(key)]=raw;
282 }
283 dckeSign->fromISCMap(dkrc, stormap);
45c2bc60
RG
284 if(!dckeSign->checkKey()) {
285 throw runtime_error("Verification of key with creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
8ca3ea33 286 }
530b4335
PD
287 }
288
189bb9d2
BH
289 string message("Hi! How is life?");
290
291 string signature;
292 DTime dt; dt.set();
f011d0ad
BH
293 for(unsigned int n = 0; n < 100; ++n)
294 signature = dckeSign->sign(message);
295 unsigned int udiffSign= dt.udiff()/100, udiffVerify;
189bb9d2
BH
296
297 dckeVerify->fromPublicKeyString(dckeSign->getPublicKeyString());
7fbe4163
CH
298 if (dckeVerify->getPublicKeyString().compare(dckeSign->getPublicKeyString())) {
299 throw runtime_error("Comparison of public key loaded into verifier produced by signer failed");
300 }
189bb9d2
BH
301 dt.set();
302 if(dckeVerify->verify(message, signature)) {
303 udiffVerify = dt.udiff();
304 cerr<<"Signature & verify ok, signature "<<udiffSign<<"usec, verify "<<udiffVerify<<"usec"<<endl;
305 }
306 else {
530b4335 307 throw runtime_error("Verification of creator "+dckeCreate->getName()+" with signer "+dckeSign->getName()+" and verifier "+dckeVerify->getName()+" failed");
189bb9d2
BH
308 }
309 return make_pair(udiffSign, udiffVerify);
310}
311
e69c2dac 312shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPublicKeyString(unsigned int algorithm, const std::string& content)
aa65a832 313{
e69c2dac 314 shared_ptr<DNSCryptoKeyEngine> dpk=make(algorithm);
189bb9d2 315 dpk->fromPublicKeyString(content);
aa65a832
BH
316 return dpk;
317}
318
f0397b95 319
e69c2dac 320shared_ptr<DNSCryptoKeyEngine> DNSCryptoKeyEngine::makeFromPEMString(DNSKEYRecordContent& drc, const std::string& raw)
ed3f8559 321{
ed3f8559 322
8455425c 323 for(const makers_t::value_type& val : getMakers())
022e5e0b 324 {
e69c2dac 325 shared_ptr<DNSCryptoKeyEngine> ret=nullptr;
022e5e0b
BH
326 try {
327 ret = val.second(val.first);
328 ret->fromPEMString(drc, raw);
329 return ret;
330 }
331 catch(...)
332 {
ed3f8559
BH
333 }
334 }
022e5e0b 335 return 0;
ed3f8559 336}
f0397b95 337
4691b2df 338
8455425c 339static bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b)
4691b2df 340{
12c06211 341 return a->serialize(g_rootdnsname, true, true) < b->serialize(g_rootdnsname, true, true);
4691b2df
BH
342}
343
125058a0
PL
344/**
345 * Returns the string that should be hashed to create/verify the RRSIG content
346 *
347 * @param qname DNSName of the RRSIG's owner name.
348 * @param rrc The RRSIGRecordContent we take the Type Covered and
349 * original TTL fields from.
350 * @param signRecords A vector of DNSRecordContent shared_ptr's that are covered
351 * by the RRSIG, where we get the RDATA from.
352 * @param processRRSIGLabels A boolean to trigger processing the RRSIG's "Labels"
353 * field. This is usually only needed for validation
354 * purposes, as the authoritative server correctly
355 * sets qname to the wildcard.
356 */
357string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords, bool processRRSIGLabels)
4691b2df
BH
358{
359 sort(signRecords.begin(), signRecords.end(), sharedDNSSECCompare);
360
361 string toHash;
12c06211 362 toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize(g_rootdnsname, true, true));
ade1b1e9 363 toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature!
4691b2df 364
125058a0
PL
365 string nameToHash(qname.toDNSStringLC());
366
367 if (processRRSIGLabels) {
368 unsigned int rrsig_labels = rrc.d_labels;
369 unsigned int fqdn_labels = qname.countLabels();
370
371 if (rrsig_labels < fqdn_labels) {
372 DNSName choppedQname(qname);
373 while (choppedQname.countLabels() > rrsig_labels)
374 choppedQname.chopOff();
375 nameToHash = "\x01*" + choppedQname.toDNSStringLC();
376 } else if (rrsig_labels > fqdn_labels) {
377 // The RRSIG Labels field is a lie (or the qname is wrong) and the RRSIG
378 // can never be valid
379 return "";
380 }
381 }
382
ef7cd021 383 for(shared_ptr<DNSRecordContent>& add : signRecords) {
125058a0 384 toHash.append(nameToHash);
4691b2df
BH
385 uint16_t tmp=htons(rrc.d_type);
386 toHash.append((char*)&tmp, 2);
387 tmp=htons(1); // class
388 toHash.append((char*)&tmp, 2);
389 uint32_t ttl=htonl(rrc.d_originalttl);
390 toHash.append((char*)&ttl, 4);
feb53a77 391 // for NSEC signatures, we should not lowercase the rdata section
12c06211 392 string rdata=add->serialize(g_rootdnsname, true, (add->getType() == QType::NSEC) ? false : true); // RFC 6840, 5.1
4691b2df
BH
393 tmp=htons(rdata.length());
394 toHash.append((char*)&tmp, 2);
395 toHash.append(rdata);
396 }
f96192e3 397
f309dacd 398 return toHash;
4691b2df
BH
399}
400
8455425c
RG
401bool DNSCryptoKeyEngine::isAlgorithmSupported(unsigned int algo)
402{
403 const makers_t& makers = getMakers();
404 makers_t::const_iterator iter = makers.find(algo);
405 return iter != makers.cend();
406}
407
408static unsigned int digestToAlgorithmNumber(uint8_t digest)
409{
410 switch(digest) {
411 case DNSSECKeeper::SHA1:
412 return DNSSECKeeper::RSASHA1;
413 case DNSSECKeeper::SHA256:
414 return DNSSECKeeper::RSASHA256;
415 case DNSSECKeeper::GOST:
416 return DNSSECKeeper::ECCGOST;
417 case DNSSECKeeper::SHA384:
418 return DNSSECKeeper::ECDSA384;
419 default:
420 throw std::runtime_error("Unknown digest type " + std::to_string(digest));
421 }
422 return 0;
423}
424
425bool DNSCryptoKeyEngine::isDigestSupported(uint8_t digest)
426{
427 try {
428 unsigned int algo = digestToAlgorithmNumber(digest);
429 return isAlgorithmSupported(algo);
430 }
431 catch(const std::exception& e) {
432 return false;
433 }
434}
435
436DSRecordContent makeDSFromDNSKey(const DNSName& qname, const DNSKEYRecordContent& drc, uint8_t digest)
4691b2df
BH
437{
438 string toHash;
0ba4e1ee 439 toHash.assign(qname.toDNSStringLC());
290a083d 440 toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize(DNSName(), true, true));
224778b0 441
4691b2df 442 DSRecordContent dsrc;
8455425c
RG
443 try {
444 unsigned int algo = digestToAlgorithmNumber(digest);
445 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algo));
39315b97
BH
446 dsrc.d_digest = dpk->hash(toHash);
447 }
8455425c 448 catch(const std::exception& e) {
335da0ba 449 throw std::runtime_error("Asked to a DS of unknown digest type " + std::to_string(digest)+"\n");
8455425c 450 }
224778b0 451
8455425c
RG
452 dsrc.d_algorithm = drc.d_algorithm;
453 dsrc.d_digesttype = digest;
454 dsrc.d_tag = const_cast<DNSKEYRecordContent&>(drc).getTag();
8daea594 455
4691b2df
BH
456 return dsrc;
457}
458
4691b2df 459
e69c2dac 460DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const std::shared_ptr<DNSCryptoKeyEngine> pk, uint8_t algorithm, uint16_t flags)
699e6e37
BH
461{
462 DNSKEYRecordContent drc;
8daea594 463
4691b2df 464 drc.d_protocol=3;
c3c89361 465 drc.d_algorithm = algorithm;
4691b2df 466
4c1474f3 467 drc.d_flags=flags;
699e6e37 468 drc.d_key = pk->getPublicKeyString();
8daea594 469
4691b2df
BH
470 return drc;
471}
472
b61e407d 473uint32_t getStartOfWeek()
4691b2df 474{
b61e407d 475 uint32_t now = time(0);
4691b2df
BH
476 now -= (now % (7*86400));
477 return now;
478}
479
28e2e78e 480string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname)
4691b2df 481{
e4805005 482 return hashQNameWithSalt(ns3prc.d_salt, ns3prc.d_iterations, qname);
483}
484
485string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname)
486{
487 unsigned int times = iterations;
4691b2df 488 unsigned char hash[20];
0ba4e1ee 489 string toHash(qname.toDNSStringLC());
28e2e78e 490
4691b2df 491 for(;;) {
e4805005 492 toHash.append(salt);
5e8e902d 493 SHA1((unsigned char*)toHash.c_str(), toHash.length(), hash);
f96192e3 494 toHash.assign((char*)hash, sizeof(hash));
28e2e78e
KM
495 if(!times--)
496 break;
4691b2df 497 }
28e2e78e 498 return toHash;
4691b2df 499}
28e2e78e 500
673208a2
BH
501DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY() const
502{
8d9f38f2 503 return makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags);
673208a2 504}
ed3f8559
BH
505
506class DEREater
507{
508public:
509 DEREater(const std::string& str) : d_str(str), d_pos(0)
510 {}
511
512 struct eof{};
513
514 uint8_t getByte()
515 {
516 if(d_pos >= d_str.length()) {
517 throw eof();
518 }
519 return (uint8_t) d_str[d_pos++];
520 }
521
522 uint32_t getLength()
523 {
524 uint8_t first = getByte();
525 if(first < 0x80) {
526 return first;
527 }
528 first &= ~0x80;
529
530 uint32_t len=0;
531 for(int n=0; n < first; ++n) {
532 len *= 0x100;
533 len += getByte();
534 }
535 return len;
536 }
537
538 std::string getBytes(unsigned int len)
539 {
540 std::string ret;
541 for(unsigned int n=0; n < len; ++n)
542 ret.append(1, (char)getByte());
543 return ret;
544 }
545
546 std::string::size_type getOffset()
547 {
548 return d_pos;
549 }
550private:
551 const std::string& d_str;
552 std::string::size_type d_pos;
553};
554
ea3816cf 555static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) {
78bcb858 556
5e8e902d
CH
557 const EVP_MD* md_type;
558 unsigned int outlen;
559 unsigned char hash[EVP_MAX_MD_SIZE];
560 switch(hasher) {
561 case TSIG_MD5:
562 md_type = EVP_md5();
563 break;
564 case TSIG_SHA1:
565 md_type = EVP_sha1();
566 break;
567 case TSIG_SHA224:
568 md_type = EVP_sha224();
569 break;
570 case TSIG_SHA256:
571 md_type = EVP_sha256();
572 break;
573 case TSIG_SHA384:
574 md_type = EVP_sha384();
575 break;
576 case TSIG_SHA512:
577 md_type = EVP_sha512();
578 break;
579 default:
60a1c204 580 throw PDNSException("Unknown hash algorithm requested from calculateHMAC()");
5e8e902d
CH
581 }
582
583 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);
60a1c204
RG
584 if (out == NULL || outlen == 0) {
585 throw PDNSException("HMAC computation failed");
5e8e902d 586 }
5e8e902d 587
60a1c204
RG
588 return string((char*) hash, outlen);
589}
590
ea3816cf 591static bool constantTimeStringEquals(const std::string& a, const std::string& b)
60a1c204
RG
592{
593 if (a.size() != b.size()) {
594 return false;
595 }
596 const size_t size = a.size();
597#if OPENSSL_VERSION_NUMBER >= 0x0090819fL
598 return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
599#else
600 const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
601 const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
602 unsigned char res = 0;
603
604 for (size_t idx = 0; idx < size; idx++) {
605 res |= _a[idx] ^ _b[idx];
606 }
607
608 return res == 0;
609#endif
3213be1e
AT
610}
611
ea3816cf 612static string makeTSIGPayload(const string& previous, const char* packetBegin, size_t packetSize, const DNSName& tsigKeyName, const TSIGRecordContent& trc, bool timersonly)
01cb2fe2
BH
613{
614 string message;
c48dec72 615
01cb2fe2
BH
616 if(!previous.empty()) {
617 uint16_t len = htons(previous.length());
ea3816cf 618 message.append(reinterpret_cast<const char*>(&len), sizeof(len));
01cb2fe2
BH
619 message.append(previous);
620 }
ea3816cf
RG
621
622 message.append(packetBegin, packetSize);
01cb2fe2
BH
623
624 vector<uint8_t> signVect;
290a083d 625 DNSPacketWriter dw(signVect, DNSName(), 0);
57ddc8ba 626 auto pos=signVect.size();
01cb2fe2 627 if(!timersonly) {
ea3816cf 628 dw.xfrName(tsigKeyName, false);
f4d26b4f 629 dw.xfr16BitInt(QClass::ANY); // class
01cb2fe2 630 dw.xfr32BitInt(0); // TTL
68e9d647 631 dw.xfrName(trc.d_algoName.makeLowerCase(), false);
01cb2fe2
BH
632 }
633
634 uint32_t now = trc.d_time;
635 dw.xfr48BitInt(now);
636 dw.xfr16BitInt(trc.d_fudge); // fudge
637 if(!timersonly) {
638 dw.xfr16BitInt(trc.d_eRcode); // extended rcode
639 dw.xfr16BitInt(trc.d_otherData.length()); // length of 'other' data
640 // dw.xfrBlob(trc->d_otherData);
641 }
57ddc8ba 642 message.append(signVect.begin()+pos, signVect.end());
01cb2fe2
BH
643 return message;
644}
645
ea3816cf 646static string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const DNSName& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0)
01cb2fe2 647{
ea3816cf
RG
648 string message;
649 string packet(opacket);
981fa489 650
ea3816cf
RG
651 packet.resize(tsigOffset); // remove the TSIG record at the end as per RFC2845 3.4.1
652 packet[(dnsHeaderOffset + sizeof(struct dnsheader))-1]--; // Decrease ARCOUNT because we removed the TSIG RR in the previous line.
01cb2fe2 653
ea3816cf
RG
654
655 // Replace the message ID with the original message ID from the TSIG record.
656 // 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
657 // signature was created with the original ID, so we replace it here to get the originally signed message.
658 // If the message is not forwarded, we simply override it with the same id.
659 uint16_t origID = htons(trc.d_origID);
660 packet.replace(0, 2, (char*)&origID, 2);
661
662 return makeTSIGPayload(previous, packet.data(), packet.size(), keyname, trc, timersonly);
663}
664
665void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly)
666{
667 TSIGHashEnum algo;
668 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
669 throw PDNSException(string("Unsupported TSIG HMAC algorithm ") + trc.d_algoName.toString());
01cb2fe2 670 }
ea3816cf
RG
671
672 string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly);
01cb2fe2 673
7f9ac49b 674 if (algo == TSIG_GSS) {
ea3816cf 675 if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) {
21a3792f 676 throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toString()+string("'"));
7f9ac49b
AT
677 }
678 } else {
ea3816cf
RG
679 trc.d_mac = calculateHMAC(tsigsecret, toSign, algo);
680 // trc.d_mac[0]++; // sabotage
7f9ac49b 681 }
e693ff5a 682 pw.startRecord(tsigkeyname, QType::TSIG, 0, QClass::ANY, DNSResourceRecord::ADDITIONAL, false);
ea3816cf 683 trc.toPacket(pw);
01cb2fe2
BH
684 pw.commit();
685}
8daea594 686
ea3816cf
RG
687bool 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)
688{
689 uint64_t delta = std::abs((int64_t)trc.d_time - (int64_t)time(nullptr));
690 if(delta > trc.d_fudge) {
691 throw std::runtime_error("Invalid TSIG time delta " + std::to_string(delta) + " > fudge " + std::to_string(trc.d_fudge));
692 }
693
694 TSIGHashEnum algo;
695 if (!getTSIGHashEnum(trc.d_algoName, algo)) {
696 throw std::runtime_error("Unsupported TSIG HMAC algorithm " + trc.d_algoName.toString());
697 }
698
699 TSIGHashEnum expectedAlgo;
700 if (!getTSIGHashEnum(tt.algo, expectedAlgo)) {
701 throw std::runtime_error("Unsupported TSIG HMAC algorithm expected " + tt.algo.toString());
702 }
703
704 if (algo != expectedAlgo) {
705 throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' does not match the expected algorithm (" + tt.algo.toString() + " / " + trc.d_algoName.toString() + ")");
706 }
707
708 string tsigMsg;
709 tsigMsg = makeTSIGMessageFromTSIGPacket(packet, sigPos, tt.name, trc, previousMAC, timersOnly, dnsHeaderOffset);
710
711 if (algo == TSIG_GSS) {
712 GssContext gssctx(tt.name);
713 if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) {
714 throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' failed to validate");
715 }
716 } else {
717 string ourMac = calculateHMAC(tt.secret, tsigMsg, algo);
718
719 if(!constantTimeStringEquals(ourMac, theirMAC)) {
720 throw std::runtime_error("Signature with TSIG key '"+tt.name.toString()+"' failed to validate");
721 }
722 }
723
724 return true;
725}