File CVE-2018-14626-auth-4.1.4.patch of Package pdns.9910
diff -ru pdns-4.1.4.orig/pdns/auth-packetcache.cc pdns-4.1.4/pdns/auth-packetcache.cc
--- pdns-4.1.4.orig/pdns/auth-packetcache.cc 2018-08-29 16:07:05.000000000 +0200
+++ pdns-4.1.4/pdns/auth-packetcache.cc 2018-10-09 16:21:00.048215463 +0200
@@ -72,7 +72,7 @@
return false;
}
- uint32_t hash = canHashPacket(p->getString(), false);
+ uint32_t hash = canHashPacket(p->getString());
p->setHash(hash);
string value;
@@ -86,7 +86,7 @@
return false;
}
- haveSomething = getEntryLocked(mc.d_map, hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
+ haveSomething = getEntryLocked(mc.d_map, p->getString(), hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
}
if (!haveSomething) {
@@ -106,6 +106,11 @@
return true;
}
+bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
+{
+ return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
+}
+
void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL)
{
cleanupIfNeeded();
@@ -132,6 +137,7 @@
entry.qtype = q->qtype.getCode();
entry.value = r->getString();
entry.tcp = r->d_tcp;
+ entry.query = q->getString();
auto& mc = getMap(entry.qname);
{
@@ -146,8 +152,9 @@
auto iter = range.first;
for( ; iter != range.second ; ++iter) {
- if (iter->tcp != entry.tcp || iter->qtype != entry.qtype || iter->qname != entry.qname)
+ if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) {
continue;
+ }
iter->value = entry.value;
iter->ttd = now + ourttl;
@@ -161,7 +168,7 @@
}
}
-bool AuthPacketCache::getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
+bool AuthPacketCache::getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
{
auto& idx = map.get<HashTag>();
auto range = idx.equal_range(hash);
@@ -170,8 +177,9 @@
if (iter->ttd < now)
continue;
- if (iter->tcp != tcp || iter->qtype != qtype || iter->qname != qname)
+ if (!entryMatches(iter, query, qname, qtype, tcp)) {
continue;
+ }
value = iter->value;
return true;
diff -ru pdns-4.1.4.orig/pdns/auth-packetcache.hh pdns-4.1.4/pdns/auth-packetcache.hh
--- pdns-4.1.4.orig/pdns/auth-packetcache.hh 2018-08-29 16:07:04.000000000 +0200
+++ pdns-4.1.4/pdns/auth-packetcache.hh 2018-10-09 16:21:32.301227673 +0200
@@ -75,6 +75,7 @@
struct CacheEntry
{
+ mutable string query;
mutable string value;
DNSName qname;
@@ -109,7 +110,8 @@
return d_maps[name.hash() % d_maps.size()];
}
- bool getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
+ static bool entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp);
+ bool getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
void cleanupIfNeeded();
AtomicCounter d_ops{0};
diff -ru pdns-4.1.4.orig/pdns/packetcache.hh pdns-4.1.4/pdns/packetcache.hh
--- pdns-4.1.4.orig/pdns/packetcache.hh 2018-08-29 16:07:05.000000000 +0200
+++ pdns-4.1.4/pdns/packetcache.hh 2018-10-09 15:44:57.646908242 +0200
@@ -28,13 +28,13 @@
class PacketCache : public boost::noncopyable
{
-protected:
- static uint32_t canHashPacket(const std::string& packet, bool skipECS=true)
+public:
+ static uint32_t canHashPacket(const std::string& packet, uint16_t* ecsBegin, uint16_t* ecsEnd)
{
uint32_t ret = 0;
- ret=burtle((const unsigned char*)packet.c_str() + 2, 10, ret); // rest of dnsheader, skip id
+ ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
size_t packetSize = packet.size();
- size_t pos = 12;
+ size_t pos = sizeof(dnsheader);
const char* end = packet.c_str() + packetSize;
const char* p = packet.c_str() + pos;
@@ -43,36 +43,110 @@
ret=burtle(&l, 1, ret);
} // XXX the embedded 0 in the qname will break the subnet stripping
- struct dnsheader* dh = (struct dnsheader*)packet.c_str();
+ const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet.c_str());
const char* skipBegin = p;
const char* skipEnd = p;
+ if (ecsBegin != nullptr && ecsEnd != nullptr) {
+ *ecsBegin = 0;
+ *ecsEnd = 0;
+ }
/* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
+ OPT root label (1), type (2), class (2) and ttl (4)
+ the OPT RR rdlen (2)
= 16
*/
- if(skipECS && ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
+ if(ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
char* optionBegin = nullptr;
size_t optionLen = 0;
/* skip the final empty label (1), the qtype (2), qclass (2) */
/* root label (1), type (2), class (2) and ttl (4) */
- int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+ int res = getEDNSOption(const_cast<char*>(reinterpret_cast<const char*>(p)) + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
if (res == 0) {
skipBegin = optionBegin;
skipEnd = optionBegin + optionLen;
+ if (ecsBegin != nullptr && ecsEnd != nullptr) {
+ *ecsBegin = optionBegin - packet.c_str();
+ *ecsEnd = *ecsBegin + optionLen;
+ }
}
}
if (skipBegin > p) {
- // cerr << "Hashing from " << (p-packet.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-packet.c_str() << endl;
- ret = burtle((const unsigned char*)p, skipBegin-p, ret);
+ ret = burtle(reinterpret_cast<const unsigned char*>(p), skipBegin-p, ret);
}
if (skipEnd < end) {
- // cerr << "Hashing from " << (skipEnd-packet.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-packet.c_str() << endl;
- ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
+ ret = burtle(reinterpret_cast<const unsigned char*>(skipEnd), end-skipEnd, ret);
}
return ret;
}
+
+ static uint32_t canHashPacket(const std::string& packet)
+ {
+ uint32_t ret = 0;
+ ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
+ size_t packetSize = packet.size();
+ size_t pos = sizeof(dnsheader);
+ const char* end = packet.c_str() + packetSize;
+ const char* p = packet.c_str() + pos;
+
+ for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
+ const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
+ ret=burtle(&l, 1, ret);
+ } // XXX the embedded 0 in the qname will break the subnet stripping
+
+ if (p < end) {
+ ret = burtle(reinterpret_cast<const unsigned char*>(p), end-p, ret);
+ }
+
+ return ret;
+ }
+
+ static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
+ {
+ if (cachedQuery.size() != query.size()) {
+ return false;
+ }
+
+ return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
+ }
+
+ static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname)
+ {
+ if (!queryHeaderMatches(cachedQuery, query)) {
+ return false;
+ }
+
+ size_t pos = sizeof(dnsheader) + qname.wirelength();
+
+ return (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) == 0);
+ }
+
+ static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, uint16_t ecsBegin, uint16_t ecsEnd)
+ {
+ if (!queryHeaderMatches(cachedQuery, query)) {
+ return false;
+ }
+
+ size_t pos = sizeof(dnsheader) + qname.wirelength();
+
+ if (ecsBegin != 0 && ecsBegin >= pos && ecsEnd > ecsBegin) {
+ if (cachedQuery.compare(pos, ecsBegin - pos, query, pos, ecsBegin - pos) != 0) {
+ return false;
+ }
+
+ if (cachedQuery.compare(ecsEnd, cachedQuery.size() - ecsEnd, query, ecsEnd, query.size() - ecsEnd) != 0) {
+ return false;
+ }
+ }
+ else {
+ if (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) != 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
};
#endif /* PACKETCACHE_HH */