From 694ac1e8a9505a8914074c28bbabf698dd00b1b2 Mon Sep 17 00:00:00 2001 From: ston1th Date: Wed, 19 Apr 2017 20:42:19 +0200 Subject: [PATCH] simplified library and added better examples and net.UnixListener support --- .gitignore | 3 +- LICENSE | 2 +- README.md | 2 + example/{main.go => port80.go} | 3 +- example/port80and443.go | 52 +++++++++++++++++++ godrop.go | 92 +++++++++++++--------------------- godrop_test.go | 2 + 7 files changed, 95 insertions(+), 61 deletions(-) rename example/{main.go => port80.go} (77%) create mode 100644 example/port80and443.go diff --git a/.gitignore b/.gitignore index 528cd5b..46d10b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -example/example +port80 +port80and443 diff --git a/LICENSE b/LICENSE index 9e52d9b..2bb060c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2016 Marius Schellenberger +Copyright (C) 2017 Marius Schellenberger All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 6410577..011c0da 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # godrop - drop privileges Godrop is a simple library to drop privileges on linux maschines. + +See the examples directory on how to use the `Drop` and `MultiDrop` functions. diff --git a/example/main.go b/example/port80.go similarity index 77% rename from example/main.go rename to example/port80.go index 2e4f848..4b1dd83 100644 --- a/example/main.go +++ b/example/port80.go @@ -24,8 +24,9 @@ func main() { fmt.Println("Failed to listen on FD 3:", err) os.Exit(1) } + _, port, _ := net.SplitHostPort(l.Addr().String()) http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "pid %d\nuid %d\ngid %d", os.Getpid(), os.Getuid(), os.Getgid()) + fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port, os.Getpid(), os.Getuid(), os.Getgid()) })) } diff --git a/example/port80and443.go b/example/port80and443.go new file mode 100644 index 0000000..4ffc7af --- /dev/null +++ b/example/port80and443.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "net" + "net/http" + "os" + + "git.giftfish.de/ston1th/godrop" +) + +func main() { + cfg := godrop.Config{ + User: "nobody", + Group: "nobody", + } + err := godrop.MultiDrop(cfg, func() ([]net.Listener, error) { + l1, err := net.Listen("tcp", ":80") + if err != nil { + return nil, err + } + l2, err := net.Listen("tcp", ":443") + return []net.Listener{l1, l2}, err + }) + if err != nil { + fmt.Println(err) + } + + l1, err := godrop.GetListener() + if err != nil { + fmt.Println("Failed to listen on FD 3:", err) + os.Exit(1) + } + _, port1, _ := net.SplitHostPort(l1.Addr().String()) + + l2, err := godrop.GetListenerFd(4) + if err != nil { + fmt.Println("Failed to listen on FD 4:", err) + os.Exit(1) + } + _, port2, _ := net.SplitHostPort(l2.Addr().String()) + + go func() { + http.Serve(l1, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port1, os.Getpid(), os.Getuid(), os.Getgid()) + })) + }() + + http.Serve(l2, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port2, os.Getpid(), os.Getuid(), os.Getgid()) + })) +} diff --git a/godrop.go b/godrop.go index d9a4531..292c276 100644 --- a/godrop.go +++ b/godrop.go @@ -1,7 +1,11 @@ +// Copyright (C) 2017 Marius Schellenberger + +// Package godrop provides a simple privileges dropping library package godrop import ( "errors" + "fmt" "io/ioutil" "net" "os" @@ -11,6 +15,10 @@ import ( "syscall" ) +func errf(err error) error { + return fmt.Errorf("godrop: %s", err) +} + func readFile(name, file string) (id int, err error) { id = -1 if name == "" { @@ -44,6 +52,7 @@ func GID(group string) (int, error) { return readFile(group, "/etc/group") } +// Config represents the drop config type Config struct { User string Group string @@ -52,78 +61,43 @@ type Config struct { // Drop will spawn a new process, hand over the listening socket file descriptor and terminate itself after func Drop(c Config, f func() (net.Listener, error)) error { - uid, err := UID(c.User) - if err != nil { - return err - } - gid, err := GID(c.Group) - if err != nil { - return err - } - switch os.Getuid() { - case 0: - ln, err := f() - if err != nil { - return err - } - - l, ok := ln.(*net.TCPListener) - if !ok { - return errors.New("godrop: interface conversion failed") - } - f, err := l.File() - if err != nil { - return err - } - - cmd := exec.Command(os.Args[0], os.Args[1:]...) - cmd.ExtraFiles = []*os.File{f} - cmd.SysProcAttr = &syscall.SysProcAttr{ - Chroot: c.Chroot, - Credential: &syscall.Credential{ - Uid: uint32(uid), - Gid: uint32(gid), - }, - Setsid: true, - } - - if err := cmd.Start(); err != nil { - return err - } - - cmd.Process.Release() - os.Exit(0) - case uid: - return nil - } - return errors.New("godrop: droping priviledges failed") + return MultiDrop(c, func() ([]net.Listener, error) { + l, err := f() + return []net.Listener{l}, err + }) } // MultiDrop will spawn a new process, hand over the all listening sockets and terminate itself after func MultiDrop(c Config, f func() ([]net.Listener, error)) error { uid, err := UID(c.User) if err != nil { - return err + return errf(err) } gid, err := GID(c.Group) if err != nil { - return err + return errf(err) } switch os.Getuid() { case 0: cmd := exec.Command(os.Args[0], os.Args[1:]...) ln, err := f() if err != nil { - return err + return errf(err) } - for _, v := range ln { - l, ok := v.(*net.TCPListener) - if !ok { - return errors.New("godrop: interface conversion failed") + for i, v := range ln { + var f *os.File + switch l := v.(type) { + case *net.TCPListener: + f, err = l.File() + l.Close() + case *net.UnixListener: + f, err = l.File() + l.Close() + default: + return errf(fmt.Errorf("index: %d listener is not type of either *net.TCPListener or *net.UnixListener", i)) } - f, err := l.File() if err != nil { - return err + return errf(fmt.Errorf("index: %d %s", i, err)) } cmd.ExtraFiles = append(cmd.ExtraFiles, f) } @@ -138,7 +112,7 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error { } if err := cmd.Start(); err != nil { - return err + return errf(err) } cmd.Process.Release() @@ -146,15 +120,17 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error { case uid: return nil } - return errors.New("godrop: droping priviledges failed") + return errf(errors.New("dropping priviledges failed")) } // GetListener returns the listener socket of file descriptor 3 func GetListener() (net.Listener, error) { - return net.FileListener(os.NewFile(3, "[socket]")) + return GetListenerFd(3) } // GetListenerFd returns the listener socket of the given file descriptor func GetListenerFd(fd int) (net.Listener, error) { - return net.FileListener(os.NewFile(uintptr(fd), "[socket]")) + f := os.NewFile(uintptr(fd), "") + defer f.Close() + return net.FileListener(f) } diff --git a/godrop_test.go b/godrop_test.go index 38b629d..65ce0dd 100644 --- a/godrop_test.go +++ b/godrop_test.go @@ -1,3 +1,5 @@ +// Copyright (C) 2017 Marius Schellenberger + package godrop import "testing"