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