]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #4815 from rgacogne/dnsdist-console-no-replay
authorRemi Gacogne <rgacogne@users.noreply.github.com>
Thu, 19 Jan 2017 14:04:47 +0000 (15:04 +0100)
committerGitHub <noreply@github.com>
Thu, 19 Jan 2017 14:04:47 +0000 (15:04 +0100)
dnsdist: Merge the client and server nonces to prevent replay attacks

1  2 
pdns/dnsdist-console.cc
regression-tests.dnsdist/dnsdisttests.py

diff --combined pdns/dnsdist-console.cc
index bd20660a3b5d847c0d741ffb78c565f065653873,ad9c4bb3c642d1424f65d10ae319393f6993959d..3c34191d7fddece54e295287722da69caff45aad
@@@ -21,7 -21,6 +21,7 @@@
   */
  #include "dnsdist.hh"
  #include "sodcrypto.hh"
 +#include "pwd.h"
  
  #if defined (__OpenBSD__)
  #include <readline/readline.h>
@@@ -46,26 -45,6 +46,26 @@@ void feedConfigDelta(const std::string
    g_confDelta.push_back({now,line});
  }
  
 +string historyFile(const bool &ignoreHOME = false)
 +{
 +  string ret;
 +
 +  struct passwd pwd;
 +  struct passwd *result;
 +  char buf[16384];
 +  getpwuid_r(geteuid(), &pwd, buf, sizeof(buf), &result);
 +
 +  const char *homedir = getenv("HOME");
 +  if (result)
 +    ret = string(pwd.pw_dir);
 +  if (homedir && !ignoreHOME) // $HOME overrides what the OS tells us
 +    ret = string(homedir);
 +  if (ret.empty())
 +    ret = "."; // CWD if nothing works..
 +  ret.append("/.dnsdist_history");
 +  return ret;
 +}
 +
  void doClient(ComboAddress server, const std::string& command)
  {
    if(g_verbose)
    }
    SConnect(fd, server);
    setTCPNoDelay(fd);
-   SodiumNonce theirs, ours;
+   SodiumNonce theirs, ours, readingNonce, writingNonce;
    ours.init();
  
    writen2(fd, (const char*)ours.value, sizeof(ours.value));
    readn2(fd, (char*)theirs.value, sizeof(theirs.value));
+   readingNonce.merge(ours, theirs);
+   writingNonce.merge(theirs, ours);
  
    if(!command.empty()) {
-     string msg=sodEncryptSym(command, g_key, ours);
+     string msg=sodEncryptSym(command, g_key, writingNonce);
      putMsgLen32(fd, (uint32_t) msg.length());
      if(!msg.empty())
        writen2(fd, msg);
@@@ -94,7 -75,7 +96,7 @@@
          boost::scoped_array<char> resp(new char[len]);
          readn2(fd, resp.get(), len);
          msg.assign(resp.get(), len);
-         msg=sodDecryptSym(msg, g_key, theirs);
+         msg=sodDecryptSym(msg, g_key, readingNonce);
          cout<<msg;
          cout.flush();
        }
      return; 
    }
  
 +  string histfile = historyFile();
    set<string> dupper;
    {
 -    ifstream history(".dnsdist_history");
 +    ifstream history(histfile);
      string line;
      while(getline(history, line))
        add_history(line.c_str());
    }
 -  ofstream history(".dnsdist_history", std::ios_base::app);
 +  ofstream history(histfile, std::ios_base::app);
    string lastline;
    for(;;) {
      char* sline = readline("> ");
      if(line.empty())
        continue;
  
-     string msg=sodEncryptSym(line, g_key, ours);
+     string msg=sodEncryptSym(line, g_key, writingNonce);
      putMsgLen32(fd, (uint32_t) msg.length());
      writen2(fd, msg);
      uint32_t len;
        boost::scoped_array<char> resp(new char[len]);
        readn2(fd, resp.get(), len);
        msg.assign(resp.get(), len);
-       msg=sodDecryptSym(msg, g_key, theirs);
+       msg=sodDecryptSym(msg, g_key, readingNonce);
        cout<<msg;
        cout.flush();
      }
  
  void doConsole()
  {
 +  string histfile = historyFile(true);
    set<string> dupper;
    {
 -    ifstream history(".dnsdist_history");
 +    ifstream history(histfile);
      string line;
      while(getline(history, line))
        add_history(line.c_str());
    }
 -  ofstream history(".dnsdist_history", std::ios_base::app);
 +  ofstream history(histfile, std::ios_base::app);
    string lastline;
    for(;;) {
      char* sline = readline("> ");
@@@ -355,14 -334,10 +357,14 @@@ const std::vector<ConsoleKeyword> g_con
    { "setKey", true, "key", "set access key to that key" },
    { "setLocal", true, "netmask, [true], [false], [TCP Fast Open queue size]", "reset list of addresses we listen on to this address. Second optional parameter sets TCP or not. Third optional parameter sets SO_REUSEPORT when available. Last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0." },
    { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" },
 +  { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" },
 +  { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" },
 +  { "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" },
    { "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" },
    { "setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 10240" },
    { "setQueryCount", true, "bool", "set whether queries should be counted" },
    { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" },
 +  { "setRingBuffersSize", true, "n", "set the capacity of the ringbuffers used for live traffic inspection to `n`" },
    { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" },
    { "setServerPolicy", true, "policy", "set server selection policy to that policy" },
    { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
@@@ -444,11 -419,12 +446,12 @@@ void controlClientThread(int fd, ComboA
  try
  {
    setTCPNoDelay(fd);
-   SodiumNonce theirs;
-   readn2(fd, (char*)theirs.value, sizeof(theirs.value));
-   SodiumNonce ours;
+   SodiumNonce theirs, ours, readingNonce, writingNonce;
    ours.init();
+   readn2(fd, (char*)theirs.value, sizeof(theirs.value));
    writen2(fd, (char*)ours.value, sizeof(ours.value));
+   readingNonce.merge(ours, theirs);
+   writingNonce.merge(theirs, ours);
  
    for(;;) {
      uint32_t len;
      readn2(fd, msg.get(), len);
      
      string line(msg.get(), len);
-     line = sodDecryptSym(line, g_key, theirs);
+     line = sodDecryptSym(line, g_key, readingNonce);
      //    cerr<<"Have decrypted line: "<<line<<endl;
      string response;
      try {
      catch(const LuaContext::SyntaxErrorException& e) {
        response = "Error: " + string(e.what()) + ": ";
      }
-     response = sodEncryptSym(response, g_key, ours);
+     response = sodEncryptSym(response, g_key, writingNonce);
      putMsgLen32(fd, response.length());
      writen2(fd, response.c_str(), response.length());
    }
index f62aed9baaa33ffcb0bbd17a42a6f2048ff29270,9a649c583dea370d56c7ae46dda9b1a2988cd02b..08f5467da430fc2a707923ac1989730f0e0ddb8a
@@@ -235,57 -235,42 +235,57 @@@ class DNSDistTest(unittest.TestCase)
          return (receivedQuery, message)
  
      @classmethod
 -    def sendTCPQuery(cls, query, response, useQueue=True, timeout=2.0, rawQuery=False):
 -        if useQueue:
 -            cls._toResponderQueue.put(response, True, timeout)
 +    def openTCPConnection(cls, timeout=None):
          sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          if timeout:
              sock.settimeout(timeout)
  
          sock.connect(("127.0.0.1", cls._dnsDistPort))
 +        return sock
  
 -        try:
 -            if not rawQuery:
 -                wire = query.to_wire()
 -            else:
 -                wire = query
 +    @classmethod
 +    def sendTCPQueryOverConnection(cls, sock, query, rawQuery=False):
 +        if not rawQuery:
 +            wire = query.to_wire()
 +        else:
 +            wire = query
  
 -            sock.send(struct.pack("!H", len(wire)))
 -            sock.send(wire)
 -            data = sock.recv(2)
 +        sock.send(struct.pack("!H", len(wire)))
 +        sock.send(wire)
 +
 +    @classmethod
 +    def recvTCPResponseOverConnection(cls, sock):
 +        message = None
 +        data = sock.recv(2)
 +        if data:
 +            (datalen,) = struct.unpack("!H", data)
 +            data = sock.recv(datalen)
              if data:
 -                (datalen,) = struct.unpack("!H", data)
 -                data = sock.recv(datalen)
 +                message = dns.message.from_wire(data)
 +        return message
 +
 +    @classmethod
 +    def sendTCPQuery(cls, query, response, useQueue=True, timeout=2.0, rawQuery=False):
 +        message = None
 +        if useQueue:
 +            cls._toResponderQueue.put(response, True, timeout)
 +
 +        sock = cls.openTCPConnection(timeout)
 +
 +        try:
 +            cls.sendTCPQueryOverConnection(sock, query, rawQuery)
 +            message = cls.recvTCPResponseOverConnection(sock)
          except socket.timeout as e:
              print("Timeout: %s" % (str(e)))
 -            data = None
          except socket.error as e:
              print("Network error: %s" % (str(e)))
 -            data = None
          finally:
              sock.close()
  
          receivedQuery = None
 -        message = None
          if useQueue and not cls._fromResponderQueue.empty():
              receivedQuery = cls._fromResponderQueue.get(True, timeout)
 -        if data:
 -            message = dns.message.from_wire(data)
 +
          return (receivedQuery, message)
  
      @classmethod
          sock.send(ourNonce)
          theirNonce = sock.recv(len(ourNonce))
  
-         msg = cls._encryptConsole(command, ourNonce)
+         halfNonceSize = len(ourNonce) / 2
+         readingNonce = ourNonce[0:halfNonceSize] + theirNonce[halfNonceSize:]
+         writingNonce = theirNonce[0:halfNonceSize] + ourNonce[halfNonceSize:]
+         msg = cls._encryptConsole(command, writingNonce)
          sock.send(struct.pack("!I", len(msg)))
          sock.send(msg)
          data = sock.recv(4)
          (responseLen,) = struct.unpack("!I", data)
          data = sock.recv(responseLen)
-         response = cls._decryptConsole(data, theirNonce)
+         response = cls._decryptConsole(data, readingNonce)
          return response