mirror of
https://git.kernel.org/pub/scm/libs/libcap/libcap.git
synced 2026-01-26 15:39:08 +00:00
Lessen the situations where cap.SETPCAP is required for IAB setting.
Discussion and explanation of what is up here is in: https://bugzilla.kernel.org/show_bug.cgi?id=219169 This gets the Go cap package to parity with the recent changes to libcap. This change will be live in cap/v1.2.71. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
This commit is contained in:
parent
676971a20a
commit
9e4b652f48
43
cap/cap.go
43
cap/cap.go
@ -7,7 +7,7 @@
|
||||
// from code. You can read more about how Capabilities are intended to
|
||||
// work here:
|
||||
//
|
||||
// https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33528.pdf
|
||||
// https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33528.pdf
|
||||
//
|
||||
// This package supports native Go bindings for all the features
|
||||
// described in that paper as well as supporting subsequent changes to
|
||||
@ -15,23 +15,23 @@
|
||||
//
|
||||
// Some simple things you can do with this package are:
|
||||
//
|
||||
// // Read and display the capabilities of the running process
|
||||
// c := cap.GetProc()
|
||||
// iab := cap.IABGetProc()
|
||||
// log.Printf("this process has these caps: %q [%v]", c, iab)
|
||||
// // Read and display the capabilities of the running process
|
||||
// c := cap.GetProc()
|
||||
// iab := cap.IABGetProc()
|
||||
// log.Printf("this process has these caps: %q [%v]", c, iab)
|
||||
//
|
||||
// // Drop any privilege a process might have (including for root,
|
||||
// // but note root 'owns' a lot of system files so a cap-limited
|
||||
// // root can still do considerable damage to a running system).
|
||||
// old := cap.GetProc()
|
||||
// empty := cap.NewSet()
|
||||
// if err := empty.SetProc(); err != nil {
|
||||
// log.Fatalf("failed to drop privilege: %q -> %q: %v", old, empty, err)
|
||||
// }
|
||||
// now := cap.GetProc()
|
||||
// if cf, _ := now.Cf(empty); cf != 0 {
|
||||
// log.Fatalf("failed to fully drop privilege: have=%q, wanted=%q", now, empty)
|
||||
// }
|
||||
// // Drop any privilege a process might have (including for root,
|
||||
// // but note root 'owns' a lot of system files so a cap-limited
|
||||
// // root can still do considerable damage to a running system).
|
||||
// old := cap.GetProc()
|
||||
// empty := cap.NewSet()
|
||||
// if err := empty.SetProc(); err != nil {
|
||||
// log.Fatalf("failed to drop privilege: %q -> %q: %v", old, empty, err)
|
||||
// }
|
||||
// now := cap.GetProc()
|
||||
// if cf, _ := now.Cf(empty); cf != 0 {
|
||||
// log.Fatalf("failed to fully drop privilege: have=%q, wanted=%q", now, empty)
|
||||
// }
|
||||
//
|
||||
// The "cap" package operates with POSIX semantics for security
|
||||
// state. That is all OS threads are kept in sync at all times. The
|
||||
@ -195,6 +195,7 @@ type syscaller struct {
|
||||
|
||||
// caprcall provides a pointer etc wrapper for the system calls
|
||||
// associated with getcap.
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func (sc *syscaller) caprcall(call uintptr, h *header, d []data) error {
|
||||
x := uintptr(0)
|
||||
@ -210,6 +211,7 @@ func (sc *syscaller) caprcall(call uintptr, h *header, d []data) error {
|
||||
|
||||
// capwcall provides a pointer etc wrapper for the system calls
|
||||
// associated with setcap.
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func (sc *syscaller) capwcall(call uintptr, h *header, d []data) error {
|
||||
x := uintptr(0)
|
||||
@ -467,9 +469,10 @@ func (sc *syscaller) setAmbient(enable bool, val ...Value) error {
|
||||
// permission is available to perform this task. The settings are
|
||||
// performed in order and the function returns immediately an error is
|
||||
// detected. Use GetAmbient() to unravel where things went
|
||||
// wrong. Note, the cap package manages an abstraction IAB that
|
||||
// captures all three inheritable vectors in a single type. Consider
|
||||
// using that.
|
||||
// wrong.
|
||||
//
|
||||
// Note, the cap package manages an abstraction IAB that captures all
|
||||
// three inheritable vectors in a single type. Consider using that.
|
||||
func SetAmbient(enable bool, val ...Value) error {
|
||||
state, sc := scwStateSC()
|
||||
defer scwSetState(launchBlocked, state, -1)
|
||||
|
||||
41
cap/iab.go
41
cap/iab.go
@ -109,11 +109,11 @@ func (iab *IAB) Dup() (*IAB, error) {
|
||||
//
|
||||
// Example, replace this:
|
||||
//
|
||||
// iab := IABInit()
|
||||
// iab := IABInit()
|
||||
//
|
||||
// with this:
|
||||
//
|
||||
// iab := NewIAB()
|
||||
// iab := NewIAB()
|
||||
func IABInit() *IAB {
|
||||
return NewIAB()
|
||||
}
|
||||
@ -212,25 +212,44 @@ func (iab *IAB) String() string {
|
||||
// The iab is known to be locked by the caller.
|
||||
func (sc *syscaller) iabSetProc(iab *IAB) (err error) {
|
||||
temp := GetProc()
|
||||
var raising uint32
|
||||
raising := false
|
||||
bounder := false
|
||||
for i := 0; i < words; i++ {
|
||||
newI := iab.i[i]
|
||||
oldIP := temp.flat[i][Inheritable] | temp.flat[i][Permitted]
|
||||
raising |= (newI & ^oldIP) | iab.a[i] | iab.nb[i]
|
||||
raising = raising || (newI & ^oldIP != 0)
|
||||
if iab.nb[i] != 0 {
|
||||
bounder = true
|
||||
}
|
||||
temp.flat[i][Inheritable] = newI
|
||||
}
|
||||
if bounder {
|
||||
bounder = false
|
||||
for c := Value(maxValues); c > 0; {
|
||||
c--
|
||||
offset, mask := omask(c)
|
||||
if iab.nb[offset]&mask == 0 {
|
||||
continue
|
||||
}
|
||||
if b, _ := GetBound(c); b {
|
||||
bounder = true
|
||||
raising = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
working, err2 := temp.Dup()
|
||||
if err2 != nil {
|
||||
err = err2
|
||||
return
|
||||
}
|
||||
if raising != 0 {
|
||||
if raising {
|
||||
if err = working.SetFlag(Effective, true, SETPCAP); err != nil {
|
||||
return
|
||||
}
|
||||
if err = sc.setProc(working); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = sc.setProc(working); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err2 := sc.setProc(temp); err == nil {
|
||||
@ -246,7 +265,7 @@ func (sc *syscaller) iabSetProc(iab *IAB) (err error) {
|
||||
if iab.a[offset]&mask != 0 {
|
||||
err = sc.setAmbient(true, c)
|
||||
}
|
||||
if err == nil && iab.nb[offset]&mask != 0 {
|
||||
if bounder && err == nil && iab.nb[offset]&mask != 0 {
|
||||
err = sc.dropBound(c)
|
||||
}
|
||||
if err != nil {
|
||||
@ -260,7 +279,9 @@ func (sc *syscaller) iabSetProc(iab *IAB) (err error) {
|
||||
// capability vectors of the current process using the content,
|
||||
// iab. The Bounding vector strongly affects the potential for setting
|
||||
// other bits, so this function carefully performs the combined
|
||||
// operation in the most flexible manner.
|
||||
// operation in the most flexible manner. If the desired IAB value
|
||||
// will change the Bounding value, cap.SETPCAP must be a Permitted
|
||||
// value.
|
||||
func (iab *IAB) SetProc() error {
|
||||
if err := iab.good(); err != nil {
|
||||
return err
|
||||
|
||||
1
go/.gitignore
vendored
1
go/.gitignore
vendored
@ -17,6 +17,7 @@ setid
|
||||
gowns
|
||||
captree
|
||||
captrace
|
||||
iaber
|
||||
ok
|
||||
vendor
|
||||
go.sum
|
||||
|
||||
12
go/Makefile
12
go/Makefile
@ -125,6 +125,9 @@ endif
|
||||
mismatch: mismatch.go PSXGOPACKAGE
|
||||
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
|
||||
|
||||
iaber: iaber.go CAPGOPACKAGE PSXGOPACKAGE
|
||||
CC="$(CC)" CGO_ENABLED="$(CGO_REQUIRED)" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor $<
|
||||
|
||||
ifeq ($(CGO_REQUIRED),0)
|
||||
mismatch-cgo: mismatch.go CAPGOPACKAGE
|
||||
CC="$(CC)" CGO_ENABLED="1" $(CGO_LDFLAGS_ALLOW) CGO_CFLAGS="$(CGO_CFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build $(GO_BUILD_FLAGS) -mod=vendor -o $@ $<
|
||||
@ -150,7 +153,7 @@ endif
|
||||
# Note, the user namespace doesn't require sudo, but I wanted to avoid
|
||||
# requiring that the hosting kernel supports user namespaces for the
|
||||
# regular test case.
|
||||
sudotest: test ../progs/tcapsh-static b210613 b215283
|
||||
sudotest: test ../progs/tcapsh-static b210613 b215283 iaber
|
||||
../progs/tcapsh-static --has-b=cap_sys_admin || exit 0 && ./gowns --ns -- -c "echo gowns runs with user namespace"
|
||||
./try-launching
|
||||
ifeq ($(CGO_REQUIRED),0)
|
||||
@ -166,7 +169,10 @@ ifeq ($(CGO_REQUIRED),0)
|
||||
$(MAKE) b215283-cgo
|
||||
$(SUDO) ./b215283-cgo
|
||||
endif
|
||||
|
||||
@echo should pass:
|
||||
$(SUDO) ./iaber '!^cap_setgid,^cap_setuid,^cap_setpcap' '!^cap_setuid,^cap_setgid'
|
||||
@echo should fail:
|
||||
$(SUDO) ./iaber '!^cap_setgid,^cap_setuid' '!^cap_setuid,^cap_setgid' || exit 0 ; exit 1
|
||||
|
||||
# As of libcap-2.55 We stopped installing the cap and psx packages as
|
||||
# part of the install. Most distribution's packagers skip the Go
|
||||
@ -185,7 +191,7 @@ install: all
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ mknames ok good-names.go
|
||||
rm -f web setid gowns captree captrace
|
||||
rm -f web setid gowns captree captrace iaber
|
||||
rm -f compare-cap try-launching try-launching-cgo
|
||||
rm -f $(topdir)/cap/*~ $(topdir)/psx/*~
|
||||
rm -f b210613 b215283 b215283-cgo psx-signals psx-signals-cgo
|
||||
|
||||
37
go/iaber.go
Normal file
37
go/iaber.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Program iaber attempts to set an iab value and then exec itself
|
||||
// with its remaining arguments. This is used to validate some
|
||||
// behavior of IAB setting.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"kernel.org/pub/linux/libs/security/libcap/cap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) <= 1 {
|
||||
log.Print("success")
|
||||
return
|
||||
}
|
||||
if syscall.Getuid() != 1 {
|
||||
if err := cap.SetUID(1); err != nil {
|
||||
log.Fatalf("failed to setuid(1): %v", err)
|
||||
}
|
||||
}
|
||||
caps, iab := cap.GetProc(), cap.IABGetProc()
|
||||
log.Printf("current: %q [%q]", caps, iab)
|
||||
iab, err := cap.IABFromText(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse %q: %v", os.Args[1], err)
|
||||
}
|
||||
if err := iab.SetProc(); err != nil {
|
||||
log.Fatalf("unable to set IAB=%q: %v", iab, err)
|
||||
}
|
||||
caps, iab = cap.GetProc(), cap.IABGetProc()
|
||||
log.Printf("pre-exec: %q [%q]", caps, iab)
|
||||
err = syscall.Exec(os.Args[0], append([]string{os.Args[0]}, os.Args[2:]...), nil)
|
||||
log.Fatalf("exec %q failed: %v", os.Args[2:], err)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user