aboutsummaryrefslogtreecommitdiff
path: root/go
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-07-04 19:17:48 -0700
committerAndrew G. Morgan <morgan@kernel.org>2020-07-04 19:17:48 -0700
commit15d7ecae0cfc8dca1dd7e05a7301ecae096081f9 (patch)
treeb2d63248fc19c9ee51990dad55607d1c8e32a1e6 /go
parent1fe9b311268c2ccbbcb7230b7ffc4077289816b7 (diff)
downloadlibcap-15d7ecae0cfc8dca1dd7e05a7301ecae096081f9.tar.gz
Restructure the tree a little to provide a module example.
I've written up how to build web.go here: https://sites.google.com/site/fullycapable/building-go-programs-that-manipulate-capabilities But it struc me that the code itself does not explain about the CGO_LDFLAGS_ALLOW workaround, so I've relocated the web.go code and included a README as well as a pointer to the above explanation. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'go')
-rw-r--r--go/Makefile4
-rw-r--r--go/web.go139
2 files changed, 2 insertions, 141 deletions
diff --git a/go/Makefile b/go/Makefile
index d01fb05..c2864ab 100644
--- a/go/Makefile
+++ b/go/Makefile
@@ -53,8 +53,8 @@ $(CAPGOPACKAGE): src/$(IMPORTDIR)/cap ../cap/*.go good-names.go $(PSXGOPACKAGE)
compare-cap: compare-cap.go $(CAPGOPACKAGE)
CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(GOPATH) $(GO) build $<
-web: web.go $(CAPGOPACKAGE)
- CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build $(GOBUILDTAG) $<
+web: ../goapps/web/web.go $(CAPGOPACKAGE)
+ CGO_ENABLED="$(CGO_REQUIRED)" CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" GOPATH=$(GOPATH) $(GO) build -o $@ $(GOBUILDTAG) $<
ifeq ($(RAISE_GO_FILECAP),yes)
make -C ../progs setcap
sudo ../progs/setcap cap_setpcap,cap_net_bind_service=p web
diff --git a/go/web.go b/go/web.go
deleted file mode 100644
index d184e97..0000000
--- a/go/web.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Progam web provides an example of a webserver using capabilities to
-// bind to a privileged port, and then drop all capabilities before
-// handling the first web request.
-//
-// This program cannot work reliably as a pure Go application without
-// the equivalent of the Go runtime patch that adds a POSIX semantics
-// wrapper around the system calls that change per-thread security
-// state. A patch for the pure Go compiler/runtime to add this support
-// is available here [2019-12-14]:
-//
-// https://go-review.googlesource.com/c/go/+/210639/
-//
-// Until that patch, or something like it, is absorbed into the Go
-// runtime the only way to get capabilities to work reliably on the Go
-// runtime is to use something like libpsx via CGo to do capability
-// setting syscalls in C with POSIX semantics. As of this build of the
-// Go "kernel.org/pub/linux/libs/security/libcap/cap" package,
-// courtesy of the "kernel.org/pub/linux/libs/security/libcap/psx"
-// package, this is how things work.
-//
-// To set this up, compile and empower this binary as follows (read
-// over the detail in the psx package description if this doesn't
-// 'just' work):
-//
-// go build web.go
-// sudo setcap cap_setpcap,cap_net_bind_service=p web
-// ./web --port=80
-//
-// Make requests using wget and observe the log of web:
-//
-// wget -o/dev/null -O/dev/stdout localhost:80
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "net"
- "net/http"
- "runtime"
- "syscall"
-
- "kernel.org/pub/linux/libs/security/libcap/cap"
-)
-
-var (
- port = flag.Int("port", 0, "port to listen on")
- skipPriv = flag.Bool("skip", false, "skip raising the effective capability - will fail for low ports")
-)
-
-// ensureNotEUID aborts the program if it is running setuid something,
-// or being invoked by root. That is, the preparer isn't setting up
-// the program correctly.
-func ensureNotEUID() {
- euid := syscall.Geteuid()
- uid := syscall.Getuid()
- egid := syscall.Getegid()
- gid := syscall.Getgid()
- if uid != euid || gid != egid {
- log.Fatalf("go runtime is setuid uids:(%d vs %d), gids(%d vs %d)", uid, euid, gid, egid)
- }
- if uid == 0 {
- log.Fatalf("go runtime is running as root - cheating")
- }
-}
-
-// listen creates a listener by raising effective privilege only to
-// bind to address and then lowering that effective privilege.
-func listen(network, address string) (net.Listener, error) {
- if *skipPriv {
- return net.Listen(network, address)
- }
-
- orig := cap.GetProc()
- defer orig.SetProc() // restore original caps on exit.
-
- c, err := orig.Dup()
- if err != nil {
- return nil, fmt.Errorf("failed to dup caps: %v", err)
- }
-
- if on, _ := c.GetFlag(cap.Permitted, cap.NET_BIND_SERVICE); !on {
- return nil, fmt.Errorf("insufficient privilege to bind to low ports - want %q, have %q", cap.NET_BIND_SERVICE, c)
- }
-
- if err := c.SetFlag(cap.Effective, true, cap.NET_BIND_SERVICE); err != nil {
- return nil, fmt.Errorf("unable to set capability: %v", err)
- }
-
- if err := c.SetProc(); err != nil {
- return nil, fmt.Errorf("unable to raise capabilities %q: %v", c, err)
- }
- return net.Listen(network, address)
-}
-
-// Handler is used to abstract the ServeHTTP function.
-type Handler struct{}
-
-// ServeHTTP says hello from a single Go hardware thread and reveals
-// its capabilities.
-func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- runtime.LockOSThread()
- // Get some numbers consistent to the current execution, so
- // the returned web page demonstrates that the code execution
- // is bouncing around on different kernel thread ids.
- p := syscall.Getpid()
- t := syscall.Gettid()
- c := cap.GetProc()
- runtime.UnlockOSThread()
-
- log.Printf("Saying hello from proc: %d->%d, caps=%q", p, t, c)
- fmt.Fprintf(w, "Hello from proc: %d->%d, caps=%q\n", p, t, c)
-}
-
-func main() {
- flag.Parse()
-
- if *port == 0 {
- log.Fatal("please supply --port value")
- }
-
- ensureNotEUID()
-
- ls, err := listen("tcp", fmt.Sprintf(":%d", *port))
- if err != nil {
- log.Fatalf("aborting: %v", err)
- }
- defer ls.Close()
-
- if !*skipPriv {
- if err := cap.ModeNoPriv.Set(); err != nil {
- log.Fatalf("unable to drop all privilege: %v", err)
- }
- }
-
- if err := http.Serve(ls, &Handler{}); err != nil {
- log.Fatalf("server failed: %v", err)
- }
-}