From a36ce05577275f5b238d18b0e648644818eee6f3 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 9 Nov 2015 12:29:57 +0100 Subject: [PATCH] Add --uid and --gid parameters in dnsdist to request dropping privileges. Fixes #2798. --- pdns/dnsdist.cc | 169 +++++++++++++++++++++++++++++++++++++++--------- pdns/dnsdist.hh | 4 +- 2 files changed, 139 insertions(+), 34 deletions(-) diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 38d266936..05cce505b 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -36,7 +36,8 @@ #include #include "sodcrypto.hh" #include "dnsrulactions.hh" - +#include +#include #include /* Known sins: @@ -136,7 +137,7 @@ struct DelayedPacket } }; -DelayPipe g_delay; +DelayPipe * g_delay = 0; // listens on a dedicated socket, lobs answers from downstream servers to original requestors void* responderThread(std::shared_ptr state) @@ -166,9 +167,9 @@ void* responderThread(std::shared_ptr 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(); + 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(); diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index cc6502ad7..871abbbef 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -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 { -- 2.40.0