Tawesoft Logo

tawesoft.co.uk/go/drop example: drop.go

// Opens privileged files and ports as root, then drops privileges
package main

import (
    "fmt"
    "os"

    "tawesoft.co.uk/go/drop"
)

// Define structures and methods that meets the drop.Inheritable interface
// by implementing Name(), Open(), Inherit() and Close()...

// These are implemented here for example purposes - but you can use
// the builtins from drop instead.

// InheritableFile is a file handle that survives a dropping of
// privileges.
type InheritableFile struct {
    Path  string
    Flags int // e.g.  os.O_RDWR|os.O_CREATE
    Perm  os.FileMode // for os.O_CREATE, e.g. 0600

    handle *os.File
}

func (h InheritableFile) String() string {
    return h.Path
}

func (h *InheritableFile) Open() (*os.File, error) {
    f, err := os.OpenFile(h.Path, h.Flags, h.Perm)
    h.handle = f
    return f, err
}

func (h *InheritableFile) Inherit(f *os.File) error {
    f.Seek(0, 0)
    h.handle = f
    return nil
}

func (h *InheritableFile) Close() error {
    return h.handle.Close()
}

func main() {
    if len(os.Args) < 2 {
        panic(fmt.Sprintf("USAGE: sudo %s username\n", os.Args[0]))
    }

    // what user to drop to
    username := os.Args[1]

    // resources to be opened as root and persist after privileges are dropped
    privilegedFile := &InheritableFile{"/tmp/privileged-file-example", os.O_RDWR|os.O_CREATE, 0600, nil}
    privilegedPort := drop.NewInheritableTCPListener(":81")

    // If the program is run as root, open privileged resources as root, then
    // start a child process as `username` that inherits these resources and
    // the parent process's stdio, and immediately exit.
    //
    // If the program is run as non-root, inherit these resources and continue.
    shouldExit, closer, err := drop.Drop(username, privilegedFile, privilegedPort)
    if err != nil {
        panic(fmt.Sprintf("error dropping privileges (try running as root): %v", err))
    }
    defer closer()
    if shouldExit { return }

    // At this point, the program is no longer running as root, but it still
    // has access to these privileged resources.

    // do things with privilegedFile
    privilegedFile.handle.WriteString("hello world\n")
    privilegedFile.Close()

    // do things with privilegedPort
    // privilegedPort.handle ....
    privilegedPort.Close()
}