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