Commit 7a4fde3f authored by Ben Laurie's avatar Ben Laurie Committed by Ian Lance Taylor

unix: add some FreeBSD Capsicum support.

Change-Id: Ibb1426ebd18d27ee9f144af0fd8c94b08c06f7d7
Reviewed-on: https://go-review.googlesource.com/47290Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent cd2c2764
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd
package unix
import (
errorspkg "errors"
"fmt"
)
// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
const (
// This is the version of CapRights this package understands. See C implementation for parallels.
capRightsGoVersion = CAP_RIGHTS_VERSION_00
capArSizeMin = CAP_RIGHTS_VERSION_00 + 2
capArSizeMax = capRightsGoVersion + 2
)
var (
bit2idx = []int{
-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
}
)
func capidxbit(right uint64) int {
return int((right >> 57) & 0x1f)
}
func rightToIndex(right uint64) (int, error) {
idx := capidxbit(right)
if idx < 0 || idx >= len(bit2idx) {
return -2, fmt.Errorf("index for right 0x%x out of range", right)
}
return bit2idx[idx], nil
}
func caprver(right uint64) int {
return int(right >> 62)
}
func capver(rights *CapRights) int {
return caprver(rights.Rights[0])
}
func caparsize(rights *CapRights) int {
return capver(rights) + 2
}
// CapRightsSet sets the permissions in setrights in rights.
func CapRightsSet(rights *CapRights, setrights []uint64) error {
// This is essentially a copy of cap_rights_vset()
if capver(rights) != CAP_RIGHTS_VERSION_00 {
return fmt.Errorf("bad rights version %d", capver(rights))
}
n := caparsize(rights)
if n < capArSizeMin || n > capArSizeMax {
return errorspkg.New("bad rights size")
}
for _, right := range setrights {
if caprver(right) != CAP_RIGHTS_VERSION_00 {
return errorspkg.New("bad right version")
}
i, err := rightToIndex(right)
if err != nil {
return err
}
if i >= n {
return errorspkg.New("index overflow")
}
if capidxbit(rights.Rights[i]) != capidxbit(right) {
return errorspkg.New("index mismatch")
}
rights.Rights[i] |= right
if capidxbit(rights.Rights[i]) != capidxbit(right) {
return errorspkg.New("index mismatch (after assign)")
}
}
return nil
}
// CapRightsClear clears the permissions in clearrights from rights.
func CapRightsClear(rights *CapRights, clearrights []uint64) error {
// This is essentially a copy of cap_rights_vclear()
if capver(rights) != CAP_RIGHTS_VERSION_00 {
return fmt.Errorf("bad rights version %d", capver(rights))
}
n := caparsize(rights)
if n < capArSizeMin || n > capArSizeMax {
return errorspkg.New("bad rights size")
}
for _, right := range clearrights {
if caprver(right) != CAP_RIGHTS_VERSION_00 {
return errorspkg.New("bad right version")
}
i, err := rightToIndex(right)
if err != nil {
return err
}
if i >= n {
return errorspkg.New("index overflow")
}
if capidxbit(rights.Rights[i]) != capidxbit(right) {
return errorspkg.New("index mismatch")
}
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
if capidxbit(rights.Rights[i]) != capidxbit(right) {
return errorspkg.New("index mismatch (after assign)")
}
}
return nil
}
// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
// This is essentially a copy of cap_rights_is_vset()
if capver(rights) != CAP_RIGHTS_VERSION_00 {
return false, fmt.Errorf("bad rights version %d", capver(rights))
}
n := caparsize(rights)
if n < capArSizeMin || n > capArSizeMax {
return false, errorspkg.New("bad rights size")
}
for _, right := range setrights {
if caprver(right) != CAP_RIGHTS_VERSION_00 {
return false, errorspkg.New("bad right version")
}
i, err := rightToIndex(right)
if err != nil {
return false, err
}
if i >= n {
return false, errorspkg.New("index overflow")
}
if capidxbit(rights.Rights[i]) != capidxbit(right) {
return false, errorspkg.New("index mismatch")
}
if (rights.Rights[i] & right) != right {
return false, nil
}
}
return true, nil
}
func capright(idx uint64, bit uint64) uint64 {
return ((1 << (57 + idx)) | bit)
}
// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
// See man cap_rights_init(3) and rights(4).
func CapRightsInit(rights []uint64) (*CapRights, error) {
var r CapRights
r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0)
r.Rights[1] = capright(1, 0)
err := CapRightsSet(&r, rights)
if err != nil {
return nil, err
}
return &r, nil
}
// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
// The capability rights on fd can never be increased by CapRightsLimit.
// See man cap_rights_limit(2) and rights(4).
func CapRightsLimit(fd uintptr, rights *CapRights) error {
return capRightsLimit(int(fd), rights)
}
// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
// See man cap_rights_get(3) and rights(4).
func CapRightsGet(fd uintptr) (*CapRights, error) {
r, err := CapRightsInit(nil)
if err != nil {
return nil, err
}
err = capRightsGet(capRightsGoVersion, int(fd), r)
if err != nil {
return nil, err
}
return r, nil
}
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unix
import (
"os"
"syscall"
)
// FIXME: unexported function from os
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i os.FileMode) (o uint32) {
o |= uint32(i.Perm())
if i&os.ModeSetuid != 0 {
o |= syscall.S_ISUID
}
if i&os.ModeSetgid != 0 {
o |= syscall.S_ISGID
}
if i&os.ModeSticky != 0 {
o |= syscall.S_ISVTX
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}
......@@ -75,6 +75,7 @@ includes_DragonFly='
'
includes_FreeBSD='
#include <sys/capability.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/event.h>
......@@ -401,6 +402,7 @@ ccflags="$@"
$2 ~ /^(BPF|DLT)_/ ||
$2 ~ /^CLOCK_/ ||
$2 ~ /^CAN_/ ||
$2 ~ /^CAP_/ ||
$2 ~ /^ALG_/ ||
$2 ~ /^FS_(POLICY_FLAGS|KEY_DESC|ENCRYPTION_MODE|[A-Z0-9_]+_KEY_SIZE|IOC_(GET|SET)_ENCRYPTION)/ ||
$2 ~ /^GRND_/ ||
......
......@@ -40,21 +40,8 @@ while(<>){
if($name eq 'SYS_SYS_EXIT'){
$name = 'SYS_EXIT';
}
if($name =~ /^SYS_CAP_+/ || $name =~ /^SYS___CAP_+/){
next
}
print " $name = $num; // $proto\n";
# We keep Capsicum syscall numbers for FreeBSD
# 9-STABLE here because we are not sure whether they
# are mature and stable.
if($num == 513){
print " SYS_CAP_NEW = 514 // { int cap_new(int fd, uint64_t rights); }\n";
print " SYS_CAP_GETRIGHTS = 515 // { int cap_getrights(int fd, \\\n";
print " SYS_CAP_ENTER = 516 // { int cap_enter(void); }\n";
print " SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); }\n";
}
}
}
......
......@@ -357,6 +357,9 @@ func Llistxattr(link string, dest []byte) (sz int, err error) {
*/
//sys Access(path string, mode uint32) (err error)
//sys Adjtime(delta *Timeval, olddelta *Timeval) (err error)
//sys CapEnter() (err error)
//sys capRightsGet(version int, fd int, rightsp *CapRights) (err error) = SYS___CAP_RIGHTS_GET
//sys capRightsLimit(fd int, rightsp *CapRights) (err error)
//sys Chdir(path string) (err error)
//sys Chflags(path string, flags int) (err error)
//sys Chmod(path string, mode uint32) (err error)
......@@ -421,6 +424,7 @@ func Llistxattr(link string, dest []byte) (sz int, err error) {
//sys Munlockall() (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error)
//sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
......
......@@ -7,6 +7,13 @@
package unix_test
import (
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"golang.org/x/sys/unix"
......@@ -18,3 +25,266 @@ func TestSysctlUint64(t *testing.T) {
t.Fatal(err)
}
}
// FIXME: Infrastructure for launching tests in subprocesses stolen from openbsd_test.go - refactor?
// testCmd generates a proper command that, when executed, runs the test
// corresponding to the given key.
type testProc struct {
fn func() // should always exit instead of returning
arg func(t *testing.T) string // generate argument for test
cleanup func(arg string) error // for instance, delete coredumps from testing pledge
success bool // whether zero-exit means success or failure
}
var (
testProcs = map[string]testProc{}
procName = ""
procArg = ""
)
const (
optName = "sys-unix-internal-procname"
optArg = "sys-unix-internal-arg"
)
func init() {
flag.StringVar(&procName, optName, "", "internal use only")
flag.StringVar(&procArg, optArg, "", "internal use only")
}
func testCmd(procName string, procArg string) (*exec.Cmd, error) {
exe, err := filepath.Abs(os.Args[0])
if err != nil {
return nil, err
}
cmd := exec.Command(exe, "-"+optName+"="+procName, "-"+optArg+"="+procArg)
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
return cmd, nil
}
// ExitsCorrectly is a comprehensive, one-line-of-use wrapper for testing
// a testProc with a key.
func ExitsCorrectly(t *testing.T, procName string) {
s := testProcs[procName]
arg := "-"
if s.arg != nil {
arg = s.arg(t)
}
c, err := testCmd(procName, arg)
defer func(arg string) {
if err := s.cleanup(arg); err != nil {
t.Fatalf("Failed to run cleanup for %s %s %#v", procName, err, err)
}
}(arg)
if err != nil {
t.Fatalf("Failed to construct command for %s", procName)
}
if (c.Run() == nil) != s.success {
result := "succeed"
if !s.success {
result = "fail"
}
t.Fatalf("Process did not %s when it was supposed to", result)
}
}
func TestMain(m *testing.M) {
flag.Parse()
if procName != "" {
t := testProcs[procName]
t.fn()
os.Stderr.WriteString("test function did not exit\n")
if t.success {
os.Exit(1)
} else {
os.Exit(0)
}
}
os.Exit(m.Run())
}
// end of infrastructure
const testfile = "gocapmodetest"
const testfile2 = testfile + "2"
func CapEnterTest() {
_, err := os.OpenFile(path.Join(procArg, testfile), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(fmt.Sprintf("OpenFile: %s", err))
}
err = unix.CapEnter()
if err != nil {
panic(fmt.Sprintf("CapEnter: %s", err))
}
_, err = os.OpenFile(path.Join(procArg, testfile2), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err == nil {
panic("OpenFile works!")
}
if err.(*os.PathError).Err != unix.ECAPMODE {
panic(fmt.Sprintf("OpenFile failed wrong: %s %#v", err, err))
}
os.Exit(0)
}
func makeTempDir(t *testing.T) string {
d, err := ioutil.TempDir("", "go_openat_test")
if err != nil {
t.Fatalf("TempDir failed: %s", err)
}
return d
}
func removeTempDir(arg string) error {
err := os.RemoveAll(arg)
if err != nil && err.(*os.PathError).Err == unix.ENOENT {
return nil
}
return err
}
func init() {
testProcs["cap_enter"] = testProc{
CapEnterTest,
makeTempDir,
removeTempDir,
true,
}
}
func TestCapEnter(t *testing.T) {
ExitsCorrectly(t, "cap_enter")
}
func OpenatTest() {
f, err := os.Open(procArg)
if err != nil {
panic(err)
}
err = unix.CapEnter()
if err != nil {
panic(fmt.Sprintf("CapEnter: %s", err))
}
fxx, err := unix.Openat(int(f.Fd()), "xx", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
unix.Close(fxx)
// The right to open BASE/xx is not ambient
_, err = os.OpenFile(procArg+"/xx", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err == nil {
panic("OpenFile succeeded")
}
if err.(*os.PathError).Err != unix.ECAPMODE {
panic(fmt.Sprintf("OpenFile failed wrong: %s %#v", err, err))
}
// Can't make a new directory either
err = os.Mkdir(procArg+"2", 0777)
if err == nil {
panic("MKdir succeeded")
}
if err.(*os.PathError).Err != unix.ECAPMODE {
panic(fmt.Sprintf("Mkdir failed wrong: %s %#v", err, err))
}
// Remove all caps except read and lookup.
r, err := unix.CapRightsInit([]uint64{unix.CAP_READ, unix.CAP_LOOKUP})
if err != nil {
panic(fmt.Sprintf("CapRightsInit failed: %s %#v", err, err))
}
err = unix.CapRightsLimit(f.Fd(), r)
if err != nil {
panic(fmt.Sprintf("CapRightsLimit failed: %s %#v", err, err))
}
// Check we can get the rights back again
r, err = unix.CapRightsGet(f.Fd())
if err != nil {
panic(fmt.Sprintf("CapRightsGet failed: %s %#v", err, err))
}
b, err := unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_LOOKUP})
if err != nil {
panic(fmt.Sprintf("CapRightsIsSet failed: %s %#v", err, err))
}
if !b {
panic(fmt.Sprintf("Unexpected rights"))
}
b, err = unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_LOOKUP, unix.CAP_WRITE})
if err != nil {
panic(fmt.Sprintf("CapRightsIsSet failed: %s %#v", err, err))
}
if b {
panic(fmt.Sprintf("Unexpected rights (2)"))
}
// Can no longer create a file
_, err = unix.Openat(int(f.Fd()), "xx2", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err == nil {
panic("Openat succeeded")
}
if err != unix.ENOTCAPABLE {
panic(fmt.Sprintf("OpenFileAt failed wrong: %s %#v", err, err))
}
// But can read an existing one
_, err = unix.Openat(int(f.Fd()), "xx", os.O_RDONLY, 0666)
if err != nil {
panic(fmt.Sprintf("Openat failed: %s %#v", err, err))
}
os.Exit(0)
}
func init() {
testProcs["openat"] = testProc{
OpenatTest,
makeTempDir,
removeTempDir,
true,
}
}
func TestOpenat(t *testing.T) {
ExitsCorrectly(t, "openat")
}
func TestCapRightsSetAndClear(t *testing.T) {
r, err := unix.CapRightsInit([]uint64{unix.CAP_READ, unix.CAP_WRITE, unix.CAP_PDWAIT})
if err != nil {
t.Fatalf("CapRightsInit failed: %s", err)
}
err = unix.CapRightsSet(r, []uint64{unix.CAP_EVENT, unix.CAP_LISTEN})
if err != nil {
t.Fatalf("CapRightsSet failed: %s", err)
}
b, err := unix.CapRightsIsSet(r, []uint64{unix.CAP_READ, unix.CAP_WRITE, unix.CAP_PDWAIT, unix.CAP_EVENT, unix.CAP_LISTEN})
if err != nil {
t.Fatalf("CapRightsIsSet failed: %s", err)
}
if !b {
t.Fatalf("Wrong rights set")
}
err = unix.CapRightsClear(r, []uint64{unix.CAP_READ, unix.CAP_PDWAIT})
if err != nil {
t.Fatalf("CapRightsClear failed: %s", err)
}
b, err = unix.CapRightsIsSet(r, []uint64{unix.CAP_WRITE, unix.CAP_EVENT, unix.CAP_LISTEN})
if err != nil {
t.Fatalf("CapRightsIsSet failed: %s", err)
}
if !b {
t.Fatalf("Wrong rights set")
}
}
......@@ -21,6 +21,7 @@ package unix
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/capability.h>
#include <sys/event.h>
#include <sys/mman.h>
#include <sys/mount.h>
......@@ -130,7 +131,10 @@ struct if_data8 {
u_long ifi_iqdrops;
u_long ifi_noproto;
u_long ifi_hwassist;
// FIXME: these are now unions, so maybe need to change definitions?
#undef ifi_epoch
time_t ifi_epoch;
#undef ifi_lastchange
struct timeval ifi_lastchange;
};
......@@ -351,3 +355,7 @@ type BpfZbufHeader C.struct_bpf_zbuf_header
// Terminal handling
type Termios C.struct_termios
// Capabilities
type CapRights C.struct_cap_rights
This source diff could not be displayed because it is too large. You can view the blob instead.
// mksyscall.pl -tags freebsd,amd64 syscall_bsd.go syscall_freebsd.go syscall_freebsd_amd64.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build freebsd,amd64
......@@ -303,6 +303,36 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func CapEnter() (err error) {
_, _, e1 := Syscall(SYS_CAP_ENTER, 0, 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func capRightsGet(version int, fd int, rightsp *CapRights) (err error) {
_, _, e1 := Syscall(SYS___CAP_RIGHTS_GET, uintptr(version), uintptr(fd), uintptr(unsafe.Pointer(rightsp)))
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func capRightsLimit(fd int, rightsp *CapRights) (err error) {
_, _, e1 := Syscall(SYS_CAP_RIGHTS_LIMIT, uintptr(fd), uintptr(unsafe.Pointer(rightsp)), 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
......@@ -1113,6 +1143,22 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Openat(fdat int, path string, mode int, perm uint32) (fd int, err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
if err != nil {
return
}
r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(fdat), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm), 0, 0)
fd = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
......
This diff is collapsed.
// cgo -godefs types_freebsd.go | go run mkpost.go
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build amd64,freebsd
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package unix
......@@ -85,7 +86,7 @@ type Stat_t struct {
Ctimespec Timespec
Size int64
Blocks int64
Blksize uint32
Blksize int32
Flags uint32
Gen uint32
Lspare int32
......@@ -324,31 +325,31 @@ type IfMsghdr struct {
}
type ifData struct {
Type uint8
Physical uint8
Addrlen uint8
Hdrlen uint8
Link_state uint8
Vhid uint8
Baudrate_pf uint8
Datalen uint8
Mtu uint64
Metric uint64
Baudrate uint64
Ipackets uint64
Ierrors uint64
Opackets uint64
Oerrors uint64
Collisions uint64
Ibytes uint64
Obytes uint64
Imcasts uint64
Omcasts uint64
Iqdrops uint64
Noproto uint64
Hwassist uint64
Epoch int64
Lastchange Timeval
Type uint8
Physical uint8
Addrlen uint8
Hdrlen uint8
Link_state uint8
Vhid uint8
Datalen uint16
Mtu uint32
Metric uint32
Baudrate uint64
Ipackets uint64
Ierrors uint64
Opackets uint64
Oerrors uint64
Collisions uint64
Ibytes uint64
Obytes uint64
Imcasts uint64
Omcasts uint64
Iqdrops uint64
Oqdrops uint64
Noproto uint64
Hwassist uint64
X__ifi_epoch [8]byte
X__ifi_lastchange [16]byte
}
type IfData struct {
......@@ -503,3 +504,7 @@ type Termios struct {
Ispeed uint32
Ospeed uint32
}
type CapRights struct {
Rights [2]uint64
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment