diff --git a/.gitignore b/.gitignore index af033d5..46d10b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ port80 -port80fg port80and443 diff --git a/LICENSE b/LICENSE index aa39852..2bb060c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2022 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 bcd5479..011c0da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # godrop - drop privileges -Godrop is a simple library to drop privileges on Linux and OpenBSD. +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/port80.go b/example/port80.go index 9a67d27..3a8f1f3 100644 --- a/example/port80.go +++ b/example/port80.go @@ -2,27 +2,28 @@ package main import ( "fmt" - "log" "net" "net/http" "os" - "git.giftfish.de/ston1th/godrop/v2" + "git.giftfish.de/ston1th/godrop" ) func main() { cfg := godrop.Config{ - User: "nobody", - Group: "nobody", + User: "nobody", + Group: "nobody", + Foreground: true, } err := godrop.Drop(cfg, func() (net.Listener, error) { return net.Listen("tcp", ":80") }) if err != nil { - log.Fatal(err) + fmt.Println(err) } l, err := godrop.GetListener() if err != nil { - log.Fatal("Failed to listen on FD 3:", err) + fmt.Println("Failed to listen on FD 3:", err) + os.Exit(1) } _, port, _ := net.SplitHostPort(l.Addr().String()) diff --git a/example/port80and443.go b/example/port80and443.go index f1ba7ce..c24e8ab 100644 --- a/example/port80and443.go +++ b/example/port80and443.go @@ -2,12 +2,11 @@ package main import ( "fmt" - "log" "net" "net/http" "os" - "git.giftfish.de/ston1th/godrop/v2" + "git.giftfish.de/ston1th/godrop" ) func main() { @@ -25,23 +24,23 @@ func main() { return []net.Listener{l1, l2}, err }) if err != nil { - log.Fatal(err) + fmt.Println(err) } l1, err := godrop.GetListener() if err != nil { - log.Fatal("Failed to listen on FD 3:", err) + 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 { - log.Fatal("Failed to listen on FD 4:", err) + fmt.Println("Failed to listen on FD 4:", err) + os.Exit(1) } _, port2, _ := net.SplitHostPort(l2.Addr().String()) - fmt.Printf("port1 %s\nport2 %s\npid %d\nuid %d\ngid %d\n", port1, port2, os.Getpid(), os.Getuid(), os.Getgid()) - 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()) diff --git a/example/port80fg.go b/example/port80fg.go deleted file mode 100644 index c924719..0000000 --- a/example/port80fg.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net" - "net/http" - "os" - - "git.giftfish.de/ston1th/godrop/v2" -) - -func main() { - cfg := godrop.Config{ - User: "nobody", - Group: "nobody", - Foreground: true, - } - err := godrop.Drop(cfg, func() (net.Listener, error) { return net.Listen("tcp", ":80") }) - if err != nil { - log.Fatal(err) - } - - l, err := godrop.GetListener() - if err != nil { - log.Fatal("Failed to listen on FD 3:", err) - } - _, port, _ := net.SplitHostPort(l.Addr().String()) - - fmt.Printf("port %s\npid %d\nuid %d\ngid %d\n", port, os.Getpid(), os.Getuid(), os.Getgid()) - - http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port, os.Getpid(), os.Getuid(), os.Getgid()) - })) -} diff --git a/go.mod b/go.mod deleted file mode 100644 index c939651..0000000 --- a/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module git.giftfish.de/ston1th/godrop/v2 - -go 1.19 - -require golang.org/x/sys v0.24.0 diff --git a/go.sum b/go.sum deleted file mode 100644 index d88e7bd..0000000 --- a/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/godrop.go b/godrop.go index 37c7e75..8df3298 100644 --- a/godrop.go +++ b/godrop.go @@ -1,9 +1,6 @@ -// Copyright (C) 2022 Marius Schellenberger +// Copyright (C) 2017 Marius Schellenberger -//go:build go1.11 -// +build go1.11 - -// Package godrop provides a simple library to drop privileges on Linux and OpenBSD. +// Package godrop provides a simple privileges dropping library package godrop import ( @@ -16,6 +13,10 @@ import ( "syscall" ) +func errf(err error) error { + return fmt.Errorf("godrop: %s", err) +} + // Config represents the drop config type Config struct { // User is the user to drop privileges to. @@ -41,21 +42,21 @@ func Drop(c Config, f func() (net.Listener, error)) error { func MultiDrop(c Config, f func() ([]net.Listener, error)) error { uid, err := userID(c.User) if err != nil { - return errors.New("godrop: " + err.Error()) + return errf(err) } if uid == 0 { - return fmt.Errorf("godrop: you can't drop privileges to uid 0 (%s)", c.User) + return errf(errors.New("unable to drop privileges to uid 0 (root)")) } gid, err := groupID(c.Group) if err != nil { - return errors.New("godrop: " + err.Error()) + return errf(err) } switch os.Getuid() { case 0: cmd := exec.Command(os.Args[0], os.Args[1:]...) ln, err := f() if err != nil { - return errors.New("godrop: " + err.Error()) + return errf(err) } for i, v := range ln { var f *os.File @@ -67,14 +68,20 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error { f, err = l.File() l.Close() default: - return fmt.Errorf("godrop: index %d listener is not type of either *net.TCPListener or *net.UnixListener", i) + return errf(fmt.Errorf("index: %d listener is not type of either *net.TCPListener or *net.UnixListener", i)) } if err != nil { - return fmt.Errorf("godrop: index %d %s", i, err) + return errf(fmt.Errorf("index: %d %s", i, err)) } cmd.ExtraFiles = append(cmd.ExtraFiles, f) } + if c.Foreground { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + cmd.SysProcAttr = &syscall.SysProcAttr{ Chroot: c.Chroot, Credential: &syscall.Credential{ @@ -84,31 +91,16 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error { Setsid: true, } - if c.Foreground { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } - if err := cmd.Start(); err != nil { - return errors.New("godrop: " + err.Error()) + return errf(err) } if c.Foreground { go func() { - term := make(chan os.Signal) - signal.Notify(term, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) sigs := make(chan os.Signal) - signal.Notify(sigs, syscall.SIGSTOP, syscall.SIGCONT, syscall.SIGUSR1, syscall.SIGUSR2) - for { - select { - case sig := <-term: - cmd.Process.Signal(sig) - return - case sig := <-sigs: - cmd.Process.Signal(sig) - } - } + signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + sig := <-sigs + cmd.Process.Signal(sig) }() _ = cmd.Wait() os.Exit(int(cmd.ProcessState.Sys().(syscall.WaitStatus))) @@ -118,7 +110,7 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error { case uid: return nil } - return errors.New("godrop: dropping priviledges failed") + return errf(errors.New("dropping priviledges failed")) } // GetListener returns the listener socket of file descriptor 3 @@ -128,9 +120,6 @@ func GetListener() (net.Listener, error) { // GetListenerFd returns the listener socket of the given file descriptor func GetListenerFd(fd int) (net.Listener, error) { - if fd < 3 { - return nil, errors.New("godrop: fd is less than 3") - } f := os.NewFile(uintptr(fd), "") defer f.Close() return net.FileListener(f) diff --git a/lookup.go b/lookup.go index 725dc82..abd19e4 100644 --- a/lookup.go +++ b/lookup.go @@ -1,14 +1,8 @@ -// Copyright (C) 2022 Marius Schellenberger - -//go:build go1.11 -// +build go1.11 +// Copyright (C) 2017 Marius Schellenberger package godrop -import ( - "os/user" - "strconv" -) +import "strconv" func atoi(a string) (int, error) { i, err := strconv.Atoi(a) @@ -18,18 +12,18 @@ func atoi(a string) (int, error) { return i, nil } -func userID(username string) (int, error) { - u, err := user.Lookup(username) +func userID(username string) (uid int, err error) { + id, err := lookupUID(username) if err != nil { return -1, err } - return atoi(u.Uid) + return atoi(id) } func groupID(name string) (int, error) { - g, err := user.LookupGroup(name) + id, err := lookupGID(name) if err != nil { return -1, err } - return atoi(g.Gid) + return atoi(id) } diff --git a/lookup_cgo.go b/lookup_cgo.go new file mode 100644 index 0000000..5b0355c --- /dev/null +++ b/lookup_cgo.go @@ -0,0 +1,23 @@ +// Copyright (C) 2017 Marius Schellenberger + +// +build cgo + +package godrop + +import "os/user" + +func lookupUID(username string) (string, error) { + u, err := user.Lookup(username) + if err != nil { + return "", err + } + return u.Uid, err +} + +func lookupGID(name string) (string, error) { + g, err := user.LookupGroup(name) + if err != nil { + return "", err + } + return g.Gid, err +} diff --git a/lookup_nocgo.go b/lookup_nocgo.go new file mode 100644 index 0000000..01da896 --- /dev/null +++ b/lookup_nocgo.go @@ -0,0 +1,43 @@ +// Copyright (C) 2017 Marius Schellenberger + +// +build darwin dragonfly freebsd android linux netbsd openbsd solaris +// +build !cgo + +package godrop + +import ( + "io/ioutil" + "os" + "regexp" +) + +func readFile(name, file string) (id string, err error) { + id = "-1" + if name == "" { + return + } + f, err := os.Open(file) + if err != nil { + return + } + b, err := ioutil.ReadAll(f) + if err != nil { + return + } + r := regexp.MustCompile(name + `:.*?:(\d+):`) + m := r.FindAllStringSubmatch(string(b), 1) + if len(m) == 1 { + if len(m[0]) == 2 { + return m[0][1], nil + } + } + return +} + +func lookupUID(username string) (string, error) { + return readFile(username, "/etc/passwd") +} + +func lookupGID(name string) (string, error) { + return readFile(name, "/etc/group") +} diff --git a/lookup_test.go b/lookup_test.go index 49d2a78..6f69cb0 100644 --- a/lookup_test.go +++ b/lookup_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Marius Schellenberger +// Copyright (C) 2017 Marius Schellenberger package godrop diff --git a/pledge.go b/pledge.go deleted file mode 100644 index 8c97aba..0000000 --- a/pledge.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (C) 2022 Marius Schellenberger - -//go:build !openbsd -// +build !openbsd - -package godrop - -// Pledge is currently only supported on OpenBSD. -func Pledge(promises, execpromises string) error { return nil } - -// PledgePromises is currently only supported on OpenBSD. -func PledgePromises(promises string) error { return nil } - -// PledgeExecPromises is currently only supported on OpenBSD. -func PledgeExecpromises(execpromises string) error { return nil } diff --git a/pledge_openbsd.go b/pledge_openbsd.go deleted file mode 100644 index 0af656a..0000000 --- a/pledge_openbsd.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2022 Marius Schellenberger - -//go:build openbsd -// +build openbsd - -package godrop - -import ( - "errors" - "golang.org/x/sys/unix" -) - -// Pledge is a wrapper for x/sys/unix Pledge. -// -// See https://go.googlesource.com/sys/+/master/unix/openbsd_pledge.go for usage. -func Pledge(promises, execpromises string) (err error) { - err = unix.Pledge(promises, execpromises) - if err != nil { - err = errors.New("pledge: " + err.Error()) - } - return -} - -// PledgePromises is a wrapper for x/sys/unix PledgePromises. -// -// See https://go.googlesource.com/sys/+/master/unix/openbsd_pledge.go for usage. -func PledgePromises(promises string) (err error) { - err = unix.PledgePromises(promises) - if err != nil { - err = errors.New("pledge: " + err.Error()) - } - return -} - -// PledgeExecpromises is a wrapper for x/sys/unix PledgeExecpromises. -// -// See https://go.googlesource.com/sys/+/master/unix/openbsd_pledge.go for usage. -func PledgeExecpromises(execpromises string) (err error) { - err = unix.PledgeExecpromises(execpromises) - if err != nil { - err = errors.New("pledge: " + err.Error()) - } - return -} diff --git a/unveil.go b/unveil.go deleted file mode 100644 index 412851b..0000000 --- a/unveil.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2022 Marius Schellenberger - -//go:build !openbsd -// +build !openbsd - -package godrop - -// Unveil is currently only supported on OpenBSD. -func Unveil(path, flags string) error { return nil } - -// UnveilBlock is currently only supported on OpenBSD. -func UnveilBlock() error { return nil } diff --git a/unveil_openbsd.go b/unveil_openbsd.go deleted file mode 100644 index f1e4bbc..0000000 --- a/unveil_openbsd.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2022 Marius Schellenberger - -//go:build openbsd -// +build openbsd - -package godrop - -import ( - "errors" - "golang.org/x/sys/unix" -) - -// Unveil is a wrapper for x/sys/unix Unveil. -// -// See https://go.googlesource.com/sys/+/master/unix/openbsd_unveil.go for usage. -func Unveil(path, flags string) (err error) { - err = unix.Unveil(path, flags) - if err != nil { - err = errors.New("unveil: " + err.Error()) - } - return -} - -// UnveilBlock is a wrapper for x/sys/unix UnveilBlock. -// -// See https://go.googlesource.com/sys/+/master/unix/openbsd_unveil.go for usage. -func UnveilBlock() (err error) { - err = unix.UnveilBlock() - if err != nil { - err = errors.New("unveil: " + err.Error()) - } - return -}