diff --git a/libcontainer/keys/keyctl.go b/libcontainer/keys/keyctl.go new file mode 100644 index 000000000..c37ca2133 --- /dev/null +++ b/libcontainer/keys/keyctl.go @@ -0,0 +1,67 @@ +// +build linux + +package keyctl + +import ( + "fmt" + "syscall" + "strings" + "strconv" + "unsafe" +) + +const KEYCTL_JOIN_SESSION_KEYRING = 1 +const KEYCTL_SETPERM = 5 +const KEYCTL_DESCRIBE = 6 + +type KeySerial uint32 + +func JoinSessionKeyring(name string) (KeySerial, error) { + var _name *byte = nil + var err error + + if len(name) > 0 { + _name, err = syscall.BytePtrFromString(name) + if err != nil { + return KeySerial(0), err + } + } + + sessKeyId, _, errn := syscall.Syscall(syscall.SYS_KEYCTL, KEYCTL_JOIN_SESSION_KEYRING, uintptr(unsafe.Pointer(_name)), 0) + if errn != 0 { + return 0, fmt.Errorf("could not create session key: %v", errn) + } + return KeySerial(sessKeyId), nil +} + +// modify permissions on a keyring by reading the current permissions, +// anding the bits with the given mask (clearing permissions) and setting +// additional permission bits +func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error { + dest := make([]byte, 1024) + destBytes := unsafe.Pointer(&dest[0]) + + if _, _, err := syscall.Syscall6(syscall.SYS_KEYCTL, uintptr(KEYCTL_DESCRIBE), uintptr(ringId), uintptr(destBytes), uintptr(len(dest)), 0, 0); err != 0 { + return err + } + + res := strings.Split(string(dest), ";") + if len(res) < 5 { + return fmt.Errorf("Destination buffer for key description is too small") + } + + // parse permissions + perm64, err := strconv.ParseUint(res[3], 16, 32) + if err != nil { + return err + } + + perm := (uint32(perm64) & mask) | setbits + + if _, _, err := syscall.Syscall(syscall.SYS_KEYCTL, uintptr(KEYCTL_SETPERM), uintptr(ringId), uintptr(perm)); err != 0 { + return err + } + + return nil +} + diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index 2bde44ffb..cb9af7d7f 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -6,6 +6,7 @@ import ( "os" "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/label" "github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/system" @@ -18,6 +19,10 @@ type linuxSetnsInit struct { } func (l *linuxSetnsInit) Init() error { + // do not inherit the parent's session keyring + if _, err := keyctl.JoinSessionKeyring("_ses"); err != nil { + return err + } if err := setupRlimits(l.config.Config); err != nil { return err } diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index d3b50863b..c17031dc1 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -9,6 +9,7 @@ import ( "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/keys" "github.com/opencontainers/runc/libcontainer/label" "github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/system" @@ -21,6 +22,18 @@ type linuxStandardInit struct { } func (l *linuxStandardInit) Init() error { + // do not inherit the parent's session keyring + sessKeyId, err := keyctl.JoinSessionKeyring("") + if err != nil { + return err + } + // make session keyring searcheable + // without user ns we need 'UID' search permissions + // with user ns we need 'other' search permissions + if err := keyctl.ModKeyringPerm(sessKeyId, 0xffffffff, 0x080008); err != nil { + return err + } + // join any namespaces via a path to the namespace fd if provided if err := joinExistingNamespaces(l.config.Config.Namespaces); err != nil { return err