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