Drop privileges with exec (#467)

* Drop privileges with exec and SysProcAttr

* Fix windows build

* Fix passing logfile fd
This commit is contained in:
Sebastian Schmidt 2018-06-13 14:52:41 +00:00 committed by Frank Denis
parent 8d99e95288
commit aab7e6380f
6 changed files with 151 additions and 5 deletions

View File

@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"unicode"
"os"
)
type CryptoConstruction uint16
@ -36,6 +37,11 @@ var (
InitialMinQuestionSize = 256
)
var (
FileDescriptors = make([]*os.File, 0)
FileDescriptorNum = 0
)
func PrefixWithSize(packet []byte) ([]byte, error) {
packetLen := len(packet)
if packetLen > 0xffff {

View File

@ -26,6 +26,7 @@ type Config struct {
ServerNames []string `toml:"server_names"`
ListenAddresses []string `toml:"listen_addresses"`
Daemonize bool
Username string `toml:"username"`
ForceTCP bool `toml:"force_tcp"`
Timeout int `toml:"timeout"`
KeepAlive int `toml:"keepalive"`
@ -186,6 +187,8 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error {
jsonOutput := flag.Bool("json", false, "output list as JSON")
check := flag.Bool("check", false, "check the configuration file and exit")
configFile := flag.String("config", DefaultConfigFileName, "Path to the configuration file")
username := flag.String("username", "", "After binding to the port user privileges are dropped")
child := flag.Bool("start-child", false, "Invokes program as a child process")
flag.Parse()
@ -225,11 +228,22 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error {
dlog.UseSyslog(true)
} else if config.LogFile != nil {
dlog.UseLogFile(*config.LogFile)
if !*child {
FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor())
} else {
FileDescriptorNum++
dlog.SetFileDescriptor(os.NewFile(uintptr(3), "logFile"))
}
}
proxy.logMaxSize = config.LogMaxSize
proxy.logMaxAge = config.LogMaxAge
proxy.logMaxBackups = config.LogMaxBackups
proxy.username = config.Username
if len(*username) > 0 {
proxy.username = *username
}
proxy.child = *child
proxy.xTransport = NewXTransport()
proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets
proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite

View File

@ -40,6 +40,11 @@ listen_addresses = ['127.0.0.1:53', '[::1]:53']
max_clients = 250
## After binding to the port(s) user privileges are dropped by calling itself
## via exec.command() with SysProcAttr.Credential()
username = 'nobody'
## Require servers (from static + remote sources) to satisfy specific properties

View File

@ -0,0 +1,56 @@
// +build !windows
package main
import (
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"syscall"
"github.com/jedisct1/dlog"
)
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
user, err := user.Lookup(userStr)
args := os.Args
if err != nil {
dlog.Fatal(err)
}
uid, err := strconv.Atoi(user.Uid)
if err != nil {
dlog.Fatal(err)
}
gid, err := strconv.Atoi(user.Gid)
if err != nil {
dlog.Fatal(err)
}
exec_path, err := exec.LookPath(args[0])
if err != nil {
dlog.Fatal(err)
}
path, err := filepath.Abs(exec_path)
if err != nil {
dlog.Fatal(err)
}
// remove arg[0]
copy(args[0:], args[0+1:])
args[len(args)-1] = ""
args = args[:len(args)-1]
args = append(args, "-start-child")
cmd := exec.Command(path, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.ExtraFiles = fds
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
dlog.Notice("Dropping privileges")
if err := cmd.Start(); err != nil {
dlog.Fatal(err)
}
os.Exit(0)
}

View File

@ -0,0 +1,5 @@
package main
import "os"
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {}

View File

@ -3,6 +3,7 @@ package main
import (
"io"
"io/ioutil"
"os"
"math/rand"
"net"
"sync/atomic"
@ -15,6 +16,8 @@ import (
)
type Proxy struct {
username string
child bool
proxyPublicKey [32]byte
proxySecretKey [32]byte
ephemeralKeys bool
@ -71,6 +74,7 @@ func (proxy *Proxy) StartProxy() {
for _, registeredServer := range proxy.registeredServers {
proxy.serversInfo.registerServer(proxy, registeredServer.name, registeredServer.stamp)
}
for _, listenAddrStr := range proxy.listenAddresses {
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
if err != nil {
@ -80,13 +84,69 @@ func (proxy *Proxy) StartProxy() {
if err != nil {
dlog.Fatal(err)
}
if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil {
dlog.Fatal(err)
}
if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil {
dlog.Fatal(err)
// if 'username' is not set, continue as before (Todo: refactor for DRYniss)
if !(len(proxy.username) > 0) {
if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil {
dlog.Fatal(err)
}
if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil {
dlog.Fatal(err)
}
} else {
// if 'username' is set and we are the parent process
if !proxy.child {
// parent
listenerUDP, err := net.ListenUDP("udp", listenUDPAddr)
if err != nil {
dlog.Fatal(err)
}
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
if err != nil {
dlog.Fatal(err)
}
fdUDP, err := listenerUDP.File() // On Windows, the File method of UDPConn is not implemented.
if err != nil {
dlog.Fatal(err)
}
fdTCP, err := listenerTCP.File() // On Windows, the File method of TCPListener is not implemented.
if err != nil {
dlog.Fatal(err)
}
defer listenerUDP.Close()
defer listenerTCP.Close()
FileDescriptors = append(FileDescriptors, fdUDP)
FileDescriptors = append(FileDescriptors, fdTCP)
// if 'username' is set and we are the child process
} else {
// child
listenerUDP, err := net.FilePacketConn(os.NewFile(uintptr(3+FileDescriptorNum), "listenerUDP"))
if err != nil {
dlog.Fatal(err)
}
FileDescriptorNum++
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
if err != nil {
dlog.Fatal(err)
}
FileDescriptorNum++
dlog.Noticef("Now listening to %v [UDP]", listenUDPAddr)
go proxy.udpListener(listenerUDP.(*net.UDPConn))
dlog.Noticef("Now listening to %v [TCP]", listenAddrStr)
go proxy.tcpListener(listenerTCP.(*net.TCPListener))
}
}
}
// if 'username' is set and we are the parent process drop privilege and exit
if len(proxy.username) > 0 && !proxy.child {
proxy.dropPrivilege(proxy.username, FileDescriptors)
}
if err := proxy.SystemDListeners(); err != nil {
dlog.Fatal(err)
}