1// Copyright 2017 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5//go:build freebsd
  6// +build freebsd
  7
  8package unix
  9
 10import (
 11	"errors"
 12	"fmt"
 13)
 14
 15// Go implementation of C mostly found in /usr/src/sys/kern/subr_capability.c
 16
 17const (
 18	// This is the version of CapRights this package understands. See C implementation for parallels.
 19	capRightsGoVersion = CAP_RIGHTS_VERSION_00
 20	capArSizeMin       = CAP_RIGHTS_VERSION_00 + 2
 21	capArSizeMax       = capRightsGoVersion + 2
 22)
 23
 24var (
 25	bit2idx = []int{
 26		-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,
 27		4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 28	}
 29)
 30
 31func capidxbit(right uint64) int {
 32	return int((right >> 57) & 0x1f)
 33}
 34
 35func rightToIndex(right uint64) (int, error) {
 36	idx := capidxbit(right)
 37	if idx < 0 || idx >= len(bit2idx) {
 38		return -2, fmt.Errorf("index for right 0x%x out of range", right)
 39	}
 40	return bit2idx[idx], nil
 41}
 42
 43func caprver(right uint64) int {
 44	return int(right >> 62)
 45}
 46
 47func capver(rights *CapRights) int {
 48	return caprver(rights.Rights[0])
 49}
 50
 51func caparsize(rights *CapRights) int {
 52	return capver(rights) + 2
 53}
 54
 55// CapRightsSet sets the permissions in setrights in rights.
 56func CapRightsSet(rights *CapRights, setrights []uint64) error {
 57	// This is essentially a copy of cap_rights_vset()
 58	if capver(rights) != CAP_RIGHTS_VERSION_00 {
 59		return fmt.Errorf("bad rights version %d", capver(rights))
 60	}
 61
 62	n := caparsize(rights)
 63	if n < capArSizeMin || n > capArSizeMax {
 64		return errors.New("bad rights size")
 65	}
 66
 67	for _, right := range setrights {
 68		if caprver(right) != CAP_RIGHTS_VERSION_00 {
 69			return errors.New("bad right version")
 70		}
 71		i, err := rightToIndex(right)
 72		if err != nil {
 73			return err
 74		}
 75		if i >= n {
 76			return errors.New("index overflow")
 77		}
 78		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 79			return errors.New("index mismatch")
 80		}
 81		rights.Rights[i] |= right
 82		if capidxbit(rights.Rights[i]) != capidxbit(right) {
 83			return errors.New("index mismatch (after assign)")
 84		}
 85	}
 86
 87	return nil
 88}
 89
 90// CapRightsClear clears the permissions in clearrights from rights.
 91func CapRightsClear(rights *CapRights, clearrights []uint64) error {
 92	// This is essentially a copy of cap_rights_vclear()
 93	if capver(rights) != CAP_RIGHTS_VERSION_00 {
 94		return fmt.Errorf("bad rights version %d", capver(rights))
 95	}
 96
 97	n := caparsize(rights)
 98	if n < capArSizeMin || n > capArSizeMax {
 99		return errors.New("bad rights size")
100	}
101
102	for _, right := range clearrights {
103		if caprver(right) != CAP_RIGHTS_VERSION_00 {
104			return errors.New("bad right version")
105		}
106		i, err := rightToIndex(right)
107		if err != nil {
108			return err
109		}
110		if i >= n {
111			return errors.New("index overflow")
112		}
113		if capidxbit(rights.Rights[i]) != capidxbit(right) {
114			return errors.New("index mismatch")
115		}
116		rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
117		if capidxbit(rights.Rights[i]) != capidxbit(right) {
118			return errors.New("index mismatch (after assign)")
119		}
120	}
121
122	return nil
123}
124
125// CapRightsIsSet checks whether all the permissions in setrights are present in rights.
126func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
127	// This is essentially a copy of cap_rights_is_vset()
128	if capver(rights) != CAP_RIGHTS_VERSION_00 {
129		return false, fmt.Errorf("bad rights version %d", capver(rights))
130	}
131
132	n := caparsize(rights)
133	if n < capArSizeMin || n > capArSizeMax {
134		return false, errors.New("bad rights size")
135	}
136
137	for _, right := range setrights {
138		if caprver(right) != CAP_RIGHTS_VERSION_00 {
139			return false, errors.New("bad right version")
140		}
141		i, err := rightToIndex(right)
142		if err != nil {
143			return false, err
144		}
145		if i >= n {
146			return false, errors.New("index overflow")
147		}
148		if capidxbit(rights.Rights[i]) != capidxbit(right) {
149			return false, errors.New("index mismatch")
150		}
151		if (rights.Rights[i] & right) != right {
152			return false, nil
153		}
154	}
155
156	return true, nil
157}
158
159func capright(idx uint64, bit uint64) uint64 {
160	return ((1 << (57 + idx)) | bit)
161}
162
163// CapRightsInit returns a pointer to an initialised CapRights structure filled with rights.
164// See man cap_rights_init(3) and rights(4).
165func CapRightsInit(rights []uint64) (*CapRights, error) {
166	var r CapRights
167	r.Rights[0] = (capRightsGoVersion << 62) | capright(0, 0)
168	r.Rights[1] = capright(1, 0)
169
170	err := CapRightsSet(&r, rights)
171	if err != nil {
172		return nil, err
173	}
174	return &r, nil
175}
176
177// CapRightsLimit reduces the operations permitted on fd to at most those contained in rights.
178// The capability rights on fd can never be increased by CapRightsLimit.
179// See man cap_rights_limit(2) and rights(4).
180func CapRightsLimit(fd uintptr, rights *CapRights) error {
181	return capRightsLimit(int(fd), rights)
182}
183
184// CapRightsGet returns a CapRights structure containing the operations permitted on fd.
185// See man cap_rights_get(3) and rights(4).
186func CapRightsGet(fd uintptr) (*CapRights, error) {
187	r, err := CapRightsInit(nil)
188	if err != nil {
189		return nil, err
190	}
191	err = capRightsGet(capRightsGoVersion, int(fd), r)
192	if err != nil {
193		return nil, err
194	}
195	return r, nil
196}