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