Skip to content

Commit

Permalink
Merge pull request #15140 from omoerbeek/cookies-phase1
Browse files Browse the repository at this point in the history
sdig: implement cookie support
  • Loading branch information
omoerbeek authored Feb 11, 2025
2 parents 5569b42 + 7f03857 commit 83ee883
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 76 deletions.
2 changes: 2 additions & 0 deletions docs/manpages/sdig.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ tlsProvider *name*
when using DoT, use TLS provider *name*. Currently supported (if compiled in): `openssl` and `gnutls`. Default is `openssl` if available.
opcode *OPNUM*
Use opcode *OPNUM* instead of 0 (Query). For example, ``sdig 192.0.2.1 53 example.com SOA opcode 4`` sends a ``NOTIFY``.
cookie *COOKIE*
if *COOKIE* is ``-`` send a random client cookie. Otherwise send the given cookie, which should be a hex string received from a server earlier.

Examples
--------
Expand Down
1 change: 1 addition & 0 deletions pdns/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ sdig_SOURCES = \
dnsrecords.cc \
dnswriter.cc dnswriter.hh \
dolog.hh \
ednscookies.cc ednscookies.hh \
ednsextendederror.cc ednsextendederror.hh \
ednssubnet.cc iputils.cc \
libssl.cc libssl.hh \
Expand Down
78 changes: 48 additions & 30 deletions pdns/ednscookies.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H

#include "config.h"
#endif

#include "ednscookies.hh"
#include "misc.hh"
#include "dns_random.hh"
#include "iputils.hh"

#ifdef HAVE_CRYPTO_SHORTHASH
#include <sodium.h>
Expand Down Expand Up @@ -54,23 +55,27 @@ bool EDNSCookiesOpt::makeFromString(const char* option, unsigned int len)
string EDNSCookiesOpt::makeOptString() const
{
string ret;
if (!isWellFormed())
if (!isWellFormed()) {
return ret;
}
ret.assign(client);
if (server.length() != 0)
if (server.length() != 0) {
ret.append(server);
}
return ret;
}

void EDNSCookiesOpt::getEDNSCookiesOptFromString(const char* option, unsigned int len)
{
client.clear();
server.clear();
if (len < 8)
if (len < 8) {
return;
client = string(option, 8);
}
const std::string tmp(option, len);
client = tmp.substr(0, 8);
if (len > 8) {
server = string(option + 8, len - 8);
server = tmp.substr(8);
}
}

Expand All @@ -84,16 +89,16 @@ bool EDNSCookiesOpt::isValid([[maybe_unused]] const string& secret, [[maybe_unus
// Version is not 1, can't verify
return false;
}
uint32_t ts;
memcpy(&ts, &server[4], sizeof(ts));
ts = ntohl(ts);
uint32_t timestamp{};
memcpy(&timestamp, &server[4], sizeof(timestamp));
timestamp = ntohl(timestamp);
// coverity[store_truncates_time_t]
uint32_t now = static_cast<uint32_t>(time(nullptr));
auto now = static_cast<uint32_t>(time(nullptr));
// RFC 9018 section 4.3:
// The DNS server
// SHOULD allow cookies within a 1-hour period in the past and a
// 5-minute period into the future
if (rfc1982LessThan(now + 300, ts) && rfc1982LessThan(ts + 3600, now)) {
if (rfc1982LessThan(now + 300, timestamp) && rfc1982LessThan(timestamp + 3600, now)) {
return false;
}
if (secret.length() != crypto_shorthash_KEYBYTES) {
Expand All @@ -103,11 +108,13 @@ bool EDNSCookiesOpt::isValid([[maybe_unused]] const string& secret, [[maybe_unus
string toHash = client + server.substr(0, 8) + source.toByteString();
string hashResult;
hashResult.resize(8);
crypto_shorthash(
reinterpret_cast<unsigned char*>(&hashResult[0]),
reinterpret_cast<const unsigned char*>(&toHash[0]),
toHash.length(),
reinterpret_cast<const unsigned char*>(&secret[0]));

// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
crypto_shorthash(reinterpret_cast<unsigned char*>(hashResult.data()),
reinterpret_cast<const unsigned char*>(toHash.data()),
toHash.length(),
reinterpret_cast<const unsigned char*>(secret.data()));
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
return constantTimeStringEquals(server.substr(8), hashResult);
#else
return false;
Expand All @@ -119,30 +126,40 @@ bool EDNSCookiesOpt::shouldRefresh() const
if (server.size() < 16) {
return true;
}
uint32_t ts;
memcpy(&ts, &server[4], sizeof(ts));
ts = ntohl(ts);
uint32_t timestamp{};
memcpy(&timestamp, &server.at(4), sizeof(timestamp));
timestamp = ntohl(timestamp);
// coverity[store_truncates_time_t]
uint32_t now = static_cast<uint32_t>(time(nullptr));
auto now = static_cast<uint32_t>(time(nullptr));
// RFC 9018 section 4.3:
// The DNS server
// SHOULD allow cookies within a 1-hour period in the past and a
// 5-minute period into the future
// If this is not the case, we need to refresh
if (rfc1982LessThan(now + 300, ts) && rfc1982LessThan(ts + 3600, now)) {
if (rfc1982LessThan(now + 300, timestamp) && rfc1982LessThan(timestamp + 3600, now)) {
return true;
}

// RFC 9018 section 4.3:
// The DNS server SHOULD generate a new Server Cookie at least if the
// received Server Cookie from the client is more than half an hour old
return rfc1982LessThan(ts + 1800, now);
return rfc1982LessThan(timestamp + 1800, now);
}

void EDNSCookiesOpt::makeClientCookie()
{
uint32_t lower = dns_random_uint32();
uint32_t upper = dns_random_uint32();
client = string();
client.resize(sizeof(lower) + sizeof(upper));
memcpy(client.data(), &lower, sizeof(lower));
memcpy(&client.at(sizeof(lower)), &upper, sizeof(upper));
}

bool EDNSCookiesOpt::makeServerCookie([[maybe_unused]] const string& secret, [[maybe_unused]] const ComboAddress& source)
{
#ifdef HAVE_CRYPTO_SHORTHASH
static_assert(EDNSCookieSecretSize == crypto_shorthash_KEYBYTES * 2, "The EDNSCookieSecretSize is not twice crypto_shorthash_KEYBYTES");
static_assert(EDNSCookieSecretSize == crypto_shorthash_KEYBYTES * static_cast<size_t>(2), "The EDNSCookieSecretSize is not twice crypto_shorthash_KEYBYTES");

if (isValid(secret, source) && !shouldRefresh()) {
return true;
Expand All @@ -158,18 +175,19 @@ bool EDNSCookiesOpt::makeServerCookie([[maybe_unused]] const string& secret, [[m
server.resize(4, '\0'); // 3 reserved bytes
// coverity[store_truncates_time_t]
uint32_t now = htonl(static_cast<uint32_t>(time(nullptr)));
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
server += string(reinterpret_cast<const char*>(&now), sizeof(now));
server.resize(8);

string toHash = client;
toHash += server;
toHash += source.toByteString();
server.resize(16);
crypto_shorthash(
reinterpret_cast<unsigned char*>(&server[8]),
reinterpret_cast<const unsigned char*>(&toHash[0]),
toHash.length(),
reinterpret_cast<const unsigned char*>(&secret[0]));
crypto_shorthash(reinterpret_cast<unsigned char*>(&server.at(8)),
reinterpret_cast<const unsigned char*>(toHash.data()),
toHash.length(),
reinterpret_cast<const unsigned char*>(secret.data()));
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
return true;
#else
return false;
Expand Down
30 changes: 17 additions & 13 deletions pdns/ednscookies.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "namespaces.hh"
#include "iputils.hh"

#include <string>

union ComboAddress;

struct EDNSCookiesOpt
{
Expand All @@ -35,38 +37,40 @@ struct EDNSCookiesOpt
bool makeFromString(const std::string& option);
bool makeFromString(const char* option, unsigned int len);

size_t size() const
[[nodiscard]] size_t size() const
{
return server.size() + client.size();
}

bool isWellFormed() const
[[nodiscard]] bool isWellFormed() const
{
// RFC7873 section 5.2.2
// In summary, valid cookie lengths are 8 and 16 to 40 inclusive.
// That's the total size. We account for client and server size seperately
return (
client.size() == 8 && (server.size() == 0 || (server.size() >= 8 && server.size() <= 32)));
client.size() == 8 && (server.empty() || (server.size() >= 8 && server.size() <= 32)));
}

bool isValid(const string& secret, const ComboAddress& source) const;
bool makeServerCookie(const string& secret, const ComboAddress& source);
string makeOptString() const;
string getServer() const
[[nodiscard]] bool isValid(const std::string& secret, const ComboAddress& source) const;
void makeClientCookie();
bool makeServerCookie(const std::string& secret, const ComboAddress& source);
[[nodiscard]] std::string makeOptString() const;
[[nodiscard]] std::string getServer() const
{
return server;
}
string getClient() const
[[nodiscard]] std::string getClient() const
{
return client;
}

private:
bool shouldRefresh() const;
[[nodiscard]] bool shouldRefresh() const;

// the client cookie
string client;
std::string client;
// the server cookie
string server;
std::string server;

void getEDNSCookiesOptFromString(const char* option, unsigned int len);
};
7 changes: 4 additions & 3 deletions pdns/misc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -630,15 +630,16 @@ string U32ToIP(uint32_t val)
}


string makeHexDump(const string& str)
string makeHexDump(const string& str, const string& sep)
{
std::array<char, 5> tmp;
string ret;
ret.reserve(static_cast<size_t>(str.size()*2.2));
ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));

for (char n : str) {
snprintf(tmp.data(), tmp.size(), "%02x ", static_cast<unsigned char>(n));
snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(n));
ret += tmp.data();
ret += sep;
}
return ret;
}
Expand Down
2 changes: 1 addition & 1 deletion pdns/misc.hh
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ inline double getTime()
throw runtime_error(why + ": " + stringerror(errno));
}

string makeHexDump(const string& str);
string makeHexDump(const string& str, const string& sep = " ");
//! Convert the hexstring in to a byte string
string makeBytesFromHex(const string &in);

Expand Down
2 changes: 2 additions & 0 deletions pdns/recursordist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pdns_recursor_SOURCES = \
dnsseckeeper.hh \
dnswriter.cc dnswriter.hh \
dolog.hh \
ednscookies.cc ednscookies.hh \
ednsextendederror.cc ednsextendederror.hh \
ednsoptions.cc ednsoptions.hh \
ednspadding.cc ednspadding.hh \
Expand Down Expand Up @@ -358,6 +359,7 @@ testrunner_SOURCES = \
test-dnsparser_hh.cc \
test-dnsrecordcontent.cc \
test-dnsrecords_cc.cc \
test-ednscookie_cc.cc \
test-ednsoptions_cc.cc \
test-filterpo_cc.cc \
test-histogram_hh.cc \
Expand Down
3 changes: 2 additions & 1 deletion pdns/recursordist/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ common_sources += files(
src_dir / 'dnsrecords.cc',
src_dir / 'dnssecinfra.cc',
src_dir / 'dnswriter.cc',
src_dir / 'ednscookies.cc',
src_dir / 'ednsextendederror.cc',
src_dir / 'ednsoptions.cc',
src_dir / 'ednspadding.cc',
Expand Down Expand Up @@ -445,7 +446,6 @@ tools = {

test_sources = []
test_sources += files(
src_dir / 'ednscookies.cc',
src_dir / 'test-aggressive_nsec_cc.cc',
src_dir / 'test-arguments_cc.cc',
src_dir / 'test-base32_cc.cc',
Expand All @@ -457,6 +457,7 @@ test_sources += files(
src_dir / 'test-dnsparser_hh.cc',
src_dir / 'test-dnsrecordcontent.cc',
src_dir / 'test-dnsrecords_cc.cc',
src_dir / 'test-ednscookie_cc.cc',
src_dir / 'test-ednsoptions_cc.cc',
src_dir / 'test-filterpo_cc.cc',
src_dir / 'test-histogram_hh.cc',
Expand Down
1 change: 1 addition & 0 deletions pdns/recursordist/test-ednscookie_cc.cc
Loading

0 comments on commit 83ee883

Please sign in to comment.