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