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}
197