diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2019-11-30 18:33:42 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2019-12-06 23:06:47 -0800 |
commit | b2b267ef1c83f1f3d3105a4bb84f8bebbc130dec (patch) | |
tree | d0be8e0daca097a3911006b9eb85fcf4d2607182 /go | |
parent | e9f55d90e482f680504487be6b3afb80865691d6 (diff) | |
download | libcap-b2b267ef1c83f1f3d3105a4bb84f8bebbc130dec.tar.gz |
Add support to libcap for overriding system call functions.
Note, this override only supports the system calls that
libcap uses to change kernel state associated with the
current process. This is primarily intended to permit the
user to use libpsx to force all pthreads to mirror capability
and other security relevant state.
Use a weak function definition feature of libpsx share_psx_syscall()
to transparently arrange for libcap to so force itself to use the
psx_syscall() abstraction when linked against -lpsx. This has the
effect of using linker magic to make libcap transparently observe
POSIX semantics for security state setting operations. That is, when
linked as follows:
gcc .... -lcap -lpsx -lpthread -Wl,-wrap,pthread_create
all pthreads maintain a common security state with respect to the
libcap API.
This also adds full capability setting support to the Go package
libcap/cap via a libcap/psx package which uses cgo+libpsx syscalls
that share capabilities over all pthreads including those of the
Go runtime.
Finally, if Go supports syscall.PosixSyscall() etc. then provide
a non-psx mechanism for libcap/cap to "just work" in all Go code.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'go')
-rw-r--r-- | go/Makefile | 49 | ||||
-rwxr-xr-x | go/syscalls.sh | 67 | ||||
-rw-r--r-- | go/web.go | 20 |
3 files changed, 101 insertions, 35 deletions
diff --git a/go/Makefile b/go/Makefile index 0791768..363b664 100644 --- a/go/Makefile +++ b/go/Makefile @@ -4,15 +4,33 @@ topdir=$(realpath ..) include ../Make.Rules -all: - $(MAKE) compare-cap - $(MAKE) web - ./compare-cap +GOPATH="$(realpath .)" +PSXGOPACKAGE=pkg/$(GOOSARCH)/libcap/psx.a +CAPGOPACKAGE=pkg/$(GOOSARCH)/libcap/cap.a + +all: $(PSXGOPACKAGE) $(CAPGOPACKAGE) web compare-cap + +# $(MAKE) compare-cap +# $(MAKE) web +# ./compare-cap + +src/libcap/psx: + mkdir -p src/libcap + ln -s $(realpath ..)/psx src/libcap/ src/libcap/cap: mkdir -p src/libcap ln -s $(realpath ..)/cap src/libcap/ +$(PSXGOPACKAGE): src/libcap/psx ../psx/psx.go ../psx/psx_test.go + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH="$(GOPATH)" go test libcap/psx + mkdir -p pkg + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH="$(GOPATH)" go build libcap/psx + +$(CAPGOPACKAGE): src/libcap/cap/syscalls.go src/libcap/cap/names.go src/libcap/cap/cap.go src/libcap/cap/text.go + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(realpath .) go test libcap/cap + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(realpath .) go build libcap/cap + install: all # TODO - install the Go package somewhere useful (ex. /usr/share/gocode/src/libcap/cap/ ) @@ -22,25 +40,20 @@ install: all src/libcap/cap/names.go: ../libcap/cap_names.h src/libcap/cap mknames.go go run mknames.go --header=$< | gofmt > $@ || rm -f $@ -src/libcap/cap/syscalls.go: src/libcap/cap ./syscalls.sh - ./syscalls.sh > $@ - -GOPACKAGE=pkg/$(GOOSARCH)/libcap/cap.a -$(GOPACKAGE): src/libcap/cap/syscalls.go src/libcap/cap/names.go src/libcap/cap/cap.go src/libcap/cap/text.go - echo testing Go package - GOPATH=$(realpath .) go test libcap/cap - echo building $(GOPACKAGE) - mkdir -p pkg +src/libcap/cap/syscalls.go: ./syscalls.sh src/libcap/cap + ./syscalls.sh src/libcap/cap # Compile and run something with this package and compare it to libcap. -compare-cap: compare-cap.go $(GOPACKAGE) - GOPATH=$(realpath .) go build $< +compare-cap: compare-cap.go $(CAPGOPACKAGE) + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(realpath .) go build $< -web: web.go $(GOPACKAGE) - GOPATH=$(realpath .) go build $< +web: web.go $(CAPGOPACKAGE) + CGO_LDFLAGS_ALLOW="$(CGO_LDFLAGS_ALLOW)" CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" GOPATH=$(realpath .) go build $< clean: GOPATH=$(realpath .) go clean -x -i libcap/cap 2> /dev/null || exit 0 + GOPATH=$(realpath .) go clean -x -i libcap/psx 2> /dev/null || exit 0 rm -f *.o *.so mknames web compare-cap *~ - rm -f ../cap/*~ ../cap/names.go ../cap/syscalls.go + rm -f ../cap/*~ ../cap/names.go ../cap/syscalls*.go + rm -f ../psx/*~ rm -fr pkg src diff --git a/go/syscalls.sh b/go/syscalls.sh index 4966742..eeba450 100755 --- a/go/syscalls.sh +++ b/go/syscalls.sh @@ -1,27 +1,72 @@ #!/bin/bash -cat <<EOF +dir="$1" +if [[ -z "$dir" ]]; then + echo need an argument directory + exit 1 +fi + +# This is something that we should revisit if golang adopts my +# syscall.PosixSyscall patch. At that stage, we won't need cgo to +# support a pure Go program. However, we will need a to use the cgo +# version if the program being compiled actually needs cgo. That is, +# we should have two permenant files that use +build lines to control +# which one is built based on cgo or not. + +if [ -z "$(go doc syscall 2>/dev/null|grep PosixSyscall)" ]; then + rm -f "${dir}/syscalls_cgo.go" + cat > "${dir}/syscalls.go" <<EOF +// +build linux + package cap -import "syscall" +import ( + "libcap/psx" + "syscall" +) // callKernel variables overridable for testing purposes. +// (Go build tree has no syscall.PosixSyscall support.) +var callWKernel = psx.Syscall3 +var callWKernel6 = psx.Syscall6 +var callRKernel = syscall.RawSyscall +var callRKernel6 = syscall.RawSyscall6 EOF -if [ -n "$(go doc syscall 2>/dev/null|grep PosixSyscall)" ]; then - cat <<EOF -// (Go build tree contains PosixSyscall support.) + exit 0 +fi + +# pure Go support. +cat > "${dir}/syscalls.go" <<EOF +// +build linux,!cgo + +package cap + +import "syscall" + +// callKernel variables overridable for testing purposes. +// (Go build tree contains syscall.PosixSyscall support.) var callWKernel = syscall.PosixSyscall var callWKernel6 = syscall.PosixSyscall6 var callRKernel = syscall.RawSyscall var callRKernel6 = syscall.RawSyscall6 EOF -else - cat <<EOF -// (Go build tree does not contain PosixSyscall support.) -var callWKernel = syscall.RawSyscall -var callWKernel6 = syscall.RawSyscall6 + +cat > "${dir}/syscalls_cgo.go" <<EOF +// +build linux,cgo + +package cap + +import ( + "libcap/psx" + "syscall" +) + +// callKernel variables overridable for testing purposes. +// We use this version when we are cgo compiling because +// we need to manage the native C pthreads too. +var callWKernel = psx.Syscall3 +var callWKernel6 = psx.Syscall6 var callRKernel = syscall.RawSyscall var callRKernel6 = syscall.RawSyscall6 EOF -fi @@ -1,15 +1,23 @@ // Progam web provides an example of a webserver using capabilities to -// bind to a privileged port. +// bind to a privileged port, and then drop all capabilities before +// handling the first web request. // -// This program will not work reliably without the equivalent of -// the Go runtime patch that adds a POSIX semantics wrappers around -// the system calls that change kernel state. A patch for the Go -// compiler/runtime to add this support is available here [2019-11-16]: +// 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 kernel state. A patch +// for the pure Go compiler/runtime to add this support is available +// here [2019-11-16]: // // https://git.kernel.org/pub/scm/libs/libcap/libcap.git/tree/contrib/golang/go.patch // +// 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 to do capability setting +// syscalls in C with POSIX semantics. As of this build of the Go +// libcap/cap package, this is how things work. +// // To set this up, compile and empower this binary as follows (package -// libcap/cap should be installed): +// libcap/cap should be installed, as must libpsx.a): // // go build web.go // sudo setcap cap_net_bind_service=p web |