]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add --uid and --gid parameters in dnsdist to request dropping privileges. Fixes ... 2877/head
authorRemi Gacogne <rgacogne-github@coredump.fr>
Mon, 9 Nov 2015 11:29:57 +0000 (12:29 +0100)
committerRemi Gacogne <rgacogne-github@coredump.fr>
Tue, 17 Nov 2015 14:42:18 +0000 (15:42 +0100)
pdns/dnsdist.cc
pdns/dnsdist.hh

index 38d266936c94c9d4a268ac1bc717ad03a9607b3c..05cce505b7905879bfad02585656ff606ce2fc2b 100644 (file)
@@ -36,7 +36,8 @@
 #include <unistd.h>
 #include "sodcrypto.hh"
 #include "dnsrulactions.hh"
-
+#include <grp.h>
+#include <pwd.h>
 #include <getopt.h>
 
 /* Known sins:
@@ -136,7 +137,7 @@ struct DelayedPacket
   }
 };
 
-DelayPipe<DelayedPacket> g_delay;
+DelayPipe<DelayedPacket> * g_delay = 0;
 
 // listens on a dedicated socket, lobs answers from downstream servers to original requestors
 void* responderThread(std::shared_ptr<DownstreamState> state)
@@ -166,9 +167,9 @@ void* responderThread(std::shared_ptr<DownstreamState> state)
     dh->id = ids->origID;
     g_stats.responses++;
 
-    if(ids->delayMsec) {
+    if(ids->delayMsec && g_delay) {
       DelayedPacket dp{ids->origFD, string(packet,len), ids->origRemote, ids->origDest};
-      g_delay.submit(dp, ids->delayMsec);
+      g_delay->submit(dp, ids->delayMsec);
     }
     else {
       if(ids->origDest.sin4.sin_family == 0)
@@ -895,6 +896,77 @@ static void bindAny(int af, int sock)
 #endif
 }
 
+static void dropGroupPrivs(gid_t gid)
+{
+  if (gid) {
+    if (setgid(gid) == 0) {
+      if (setgroups(0, NULL) < 0) {
+        warnlog("Warning: Unable to drop supplementary gids: %s", strerror(errno));
+      }
+    }
+    else {
+      warnlog("Warning: Unable to set group ID to %d: %s", gid, strerror(errno));
+    }
+  }
+}
+
+static void dropUserPrivs(uid_t uid)
+{
+  if(uid) {
+    if(setuid(uid) < 0) {
+      warnlog("Warning: Unable to set user ID to %d: %s", uid, strerror(errno));
+    }
+  }
+}
+
+static uid_t strToUID(const string &str)
+{
+  uid_t result = 0;
+  const char * cstr = str.c_str();
+  struct passwd * pwd = getpwnam(cstr);
+
+  if (pwd == NULL) {
+    char * endptr = 0;
+    long int val = strtol(cstr, &endptr, 10);
+
+    if (((val == LONG_MAX || val == LLONG_MIN) && errno == ERANGE) || endptr == cstr || val <= 0) {
+      warnlog("Warning: Unable to parse user ID %s", cstr);
+    }
+    else {
+      result = val;
+    }
+  }
+  else {
+    result = pwd->pw_uid;
+  }
+
+  return result;
+}
+
+static gid_t strToGID(const string &str)
+{
+  gid_t result = 0;
+  const char * cstr = str.c_str();
+  struct group * grp = getgrnam(cstr);
+
+  if (grp == NULL) {
+    char * endptr = 0;
+    long int val = strtol(cstr, &endptr, 10);
+
+    if (((val == LONG_MAX || val == LLONG_MIN) && errno == ERANGE) || endptr == cstr || val <= 0) {
+      warnlog("Warning: Unable to parse group ID %s", cstr);
+    }
+    else {
+      result = val;
+    }
+  }
+  else {
+    result = grp->gr_gid;
+  }
+
+  return result;
+}
+
 /**** CARGO CULT CODE AHEAD ****/
 extern "C" {
 char* my_generator(const char* text, int state)
@@ -942,6 +1014,8 @@ struct
   string pidfile;
   string command;
   string config;
+  string uid;
+  string gid;
 } g_cmdLine;
 
 
@@ -967,16 +1041,18 @@ try
     {"config", required_argument, 0, 'C'},
     {"execute", required_argument, 0, 'e'},
     {"client", 0, 0, 'c'},
+    {"gid",  required_argument, 0, 'g'},
     {"local",  required_argument, 0, 'l'},
     {"daemon", 0, 0, 'd'},
     {"pidfile",  required_argument, 0, 'p'},
     {"supervised", 0, 0, 's'},
+    {"uid",  required_argument, 0, 'u'},
     {"help", 0, 0, 'h'},
     {0,0,0,0} 
   };
   int longindex=0;
   for(;;) {
-    int c=getopt_long(argc, argv, "hcde:C:l:vp:", longopts, &longindex);
+    int c=getopt_long(argc, argv, "hcde:C:l:vp:g:u:", longopts, &longindex);
     if(c==-1)
       break;
     switch(c) {
@@ -992,6 +1068,9 @@ try
     case 'e':
       g_cmdLine.command=optarg;
       break;
+    case 'g':
+      g_cmdLine.gid=optarg;
+      break;
     case 'h':
       cout<<"Syntax: dnsdist [-C,--config file] [-c,--client] [-d,--daemon]\n";
       cout<<"[-p,--pidfile file] [-e,--execute cmd] [-h,--help] [-l,--local addr]\n";
@@ -1000,11 +1079,13 @@ try
       cout<<"-c,--client           Operate as a client, connect to dnsdist\n";
       cout<<"-d,--daemon           Operate as a daemon\n";
       cout<<"-e,--execute cmd      Connect to dnsdist and execute 'cmd'\n";
+      cout<<"-g,--gid gid          Change the process group ID after binding sockets\n";
       cout<<"-h,--help             Display this helpful message\n";
       cout<<"-l,--local address    Listen on this local address\n";
       cout<<"--supervised          Don't open a console, I'm supervised\n";
       cout<<"                        (use with e.g. systemd and daemontools)\n";
       cout<<"-p,--pidfile file     Write a pidfile, works only with --daemon\n";
+      cout<<"-u,--uid uid          Change the process user ID after binding sockets\n";
       cout<<"\n";
       exit(EXIT_SUCCESS);
       break;
@@ -1017,6 +1098,9 @@ try
     case 's':
       g_cmdLine.beSupervised=true;
       break;
+    case 'u':
+      g_cmdLine.uid=optarg;
+      break;
     case 'v':
       g_verbose=true;
       break;
@@ -1083,6 +1167,44 @@ try
     toLaunch.push_back(cs);
   }
 
+  for(const auto& local : g_locals) {
+    ClientState* cs = new ClientState;
+    if(!local.second) { // no TCP/IP
+      warnlog("Not providing TCP/IP service on local address '%s'", local.first.toStringWithPort());
+      continue;
+    }
+    cs->local= local.first;
+
+    cs->tcpFD = SSocket(cs->local.sin4.sin_family, SOCK_STREAM, 0);
+
+    SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEADDR, 1);
+#ifdef TCP_DEFER_ACCEPT
+    SSetsockopt(cs->tcpFD, SOL_TCP,TCP_DEFER_ACCEPT, 1);
+#endif
+    if(cs->local.sin4.sin_family == AF_INET6) {
+      SSetsockopt(cs->tcpFD, IPPROTO_IPV6, IPV6_V6ONLY, 1);
+    }
+    //    if(g_vm.count("bind-non-local"))
+      bindAny(cs->local.sin4.sin_family, cs->tcpFD);
+    SBind(cs->tcpFD, cs->local);
+    SListen(cs->tcpFD, 64);
+    warnlog("Listening on %s",cs->local.toStringWithPort());
+
+    toLaunch.push_back(cs);
+  }
+
+  uid_t newgid=0;
+  gid_t newuid=0;
+
+  if(!g_cmdLine.gid.empty())
+    newgid = strToGID(g_cmdLine.gid.c_str());
+
+  if(!g_cmdLine.uid.empty())
+    newuid = strToUID(g_cmdLine.uid.c_str());
+
+  dropGroupPrivs(newgid);
+  dropUserPrivs(newuid);
+
   if(g_cmdLine.beDaemon) {
     g_console=false;
     daemonize();
@@ -1092,6 +1214,9 @@ try
     vinfolog("Running in the foreground");
   }
 
+  /* this need to be done _after_ dropping privileges */
+  g_delay = new DelayPipe<DelayedPacket>();
+
   for(auto& t : todo)
     t();
 
@@ -1114,37 +1239,17 @@ try
 
 
   for(auto& cs : toLaunch) {
-    thread t1(udpClientThread, cs);
-    t1.detach();
-  }
-
-  for(const auto& local : g_locals) {
-    ClientState* cs = new ClientState;
-    if(!local.second) { // no TCP/IP
-      warnlog("Not providing TCP/IP service on local address '%s'", local.first.toStringWithPort());
-      continue;
+    if (cs->udpFD >= 0) {
+      thread t1(udpClientThread, cs);
+      t1.detach();
     }
-    cs->local= local.first;
-
-    cs->tcpFD = SSocket(cs->local.sin4.sin_family, SOCK_STREAM, 0);
-
-    SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEADDR, 1);
-#ifdef TCP_DEFER_ACCEPT
-    SSetsockopt(cs->tcpFD, SOL_TCP,TCP_DEFER_ACCEPT, 1);
-#endif
-    if(cs->local.sin4.sin_family == AF_INET6) {
-      SSetsockopt(cs->tcpFD, IPPROTO_IPV6, IPV6_V6ONLY, 1);
+    else if (cs->tcpFD >= 0) {
+      thread t1(tcpAcceptorThread, cs);
+      t1.detach();
     }
-    //    if(g_vm.count("bind-non-local"))
-      bindAny(cs->local.sin4.sin_family, cs->tcpFD);
-    SBind(cs->tcpFD, cs->local);
-    SListen(cs->tcpFD, 64);
-    warnlog("Listening on %s",cs->local.toStringWithPort());
-    
-    thread t1(tcpAcceptorThread, cs);
-    t1.detach();
   }
 
+
   thread carbonthread(carbonDumpThread);
   carbonthread.detach();
 
index cc6502ad7f509d171ab3e4740f820ce1eae1c964..871abbbef2f0e87cc3671137737b3bc5870fe370 100644 (file)
@@ -196,8 +196,8 @@ extern Rings g_rings; // XXX locking for this is still substandard, queryRing an
 struct ClientState
 {
   ComboAddress local;
-  int udpFD;
-  int tcpFD;
+  int udpFD{-1};
+  int tcpFD{-1};
 };
 
 class TCPClientCollection {