• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package cap
2
3import (
4	"errors"
5	"fmt"
6	"syscall"
7	"unsafe"
8)
9
10// This file contains convenience functions for libcap, to help
11// users do the right thing with respect to capabilities for
12// common actions.
13
14// Secbits capture the prctl settable secure-bits of a process.
15type Secbits uint
16
17// SecbitNoRoot etc are the bitmasks associated with the supported
18// Secbit masks.  Source: uapi/linux/securebits.h
19const (
20	SecbitNoRoot Secbits = 1 << iota
21	SecbitNoRootLocked
22	SecbitNoSetUIDFixup
23	SecbitNoSetUIDFixupLocked
24	SecbitKeepCaps
25	SecbitKeepCapsLocked
26	SecbitNoCapAmbientRaise
27	SecbitNoCapAmbientRaiseLocked
28)
29
30const (
31	securedBasicBits   = SecbitNoRoot | SecbitNoRootLocked | SecbitNoSetUIDFixup | SecbitNoSetUIDFixupLocked | SecbitKeepCapsLocked
32	securedAmbientBits = securedBasicBits | SecbitNoCapAmbientRaise | SecbitNoCapAmbientRaiseLocked
33)
34
35// defines from uapi/linux/prctl.h
36const (
37	prGetKeepCaps   = 7
38	prSetKeepCaps   = 8
39	prGetSecureBits = 27
40	prSetSecureBits = 28
41	prSetNoNewPrivs = 38
42)
43
44// GetSecbits returns the current setting of the process' Secbits.
45func GetSecbits() Secbits {
46	v, err := multisc.prctlrcall(prGetSecureBits, 0, 0)
47	if err != nil {
48		panic(err)
49	}
50	return Secbits(v)
51}
52
53func (sc *syscaller) setSecbits(s Secbits) error {
54	_, err := sc.prctlwcall(prSetSecureBits, uintptr(s), 0)
55	return err
56}
57
58// Set attempts to force the process Secbits to a value. This function
59// will raise cap.SETPCAP in order to achieve this operation, and will
60// completely lower the Effective  vector of the process returning.
61func (s Secbits) Set() error {
62	state, sc := scwStateSC()
63	defer scwSetState(launchBlocked, state, -1)
64	return sc.setSecbits(s)
65}
66
67// Mode summarizes a complicated secure-bits and capability mode in a
68// libcap preferred way.
69type Mode uint
70
71// ModeUncertain etc are how libcap summarizes security modes
72// involving capabilities and secure-bits.
73const (
74	ModeUncertain Mode = iota
75	ModeNoPriv
76	ModePure1EInit
77	ModePure1E
78)
79
80// GetMode assesses the current process state and summarizes it as
81// a Mode. This function always succeeds. Unfamiliar modes are
82// declared ModeUncertain.
83func GetMode() Mode {
84	b := GetSecbits()
85	if b&securedBasicBits != securedBasicBits {
86		return ModeUncertain
87	}
88
89	for c := Value(0); ; c++ {
90		v, err := GetAmbient(c)
91		if err != nil {
92			if c != 0 && b != securedAmbientBits {
93				return ModeUncertain
94			}
95			break
96		}
97		if v {
98			return ModeUncertain
99		}
100	}
101
102	w := GetProc()
103	e := NewSet()
104	cf, _ := w.Compare(e)
105
106	if Differs(cf, Inheritable) {
107		return ModePure1E
108	}
109	if Differs(cf, Permitted) || Differs(cf, Effective) {
110		return ModePure1EInit
111	}
112
113	for c := Value(0); ; c++ {
114		v, err := GetBound(c)
115		if err != nil {
116			break
117		}
118		if v {
119			return ModePure1EInit
120		}
121	}
122
123	return ModeNoPriv
124}
125
126// ErrBadMode is the error returned when an attempt is made to set an
127// unrecognized libcap security mode.
128var ErrBadMode = errors.New("unsupported mode")
129
130func (sc *syscaller) setMode(m Mode) error {
131	w := GetProc()
132	defer func() {
133		w.ClearFlag(Effective)
134		sc.setProc(w)
135	}()
136
137	if err := w.SetFlag(Effective, true, SETPCAP); err != nil {
138		return err
139	}
140	if err := sc.setProc(w); err != nil {
141		return err
142	}
143
144	if m == ModeNoPriv || m == ModePure1EInit {
145		w.ClearFlag(Inheritable)
146	} else if m != ModePure1E {
147		return ErrBadMode
148	}
149
150	sb := securedAmbientBits
151	if _, err := GetAmbient(0); err != nil {
152		sb = securedBasicBits
153	} else if err := sc.resetAmbient(); err != nil {
154		return err
155	}
156
157	if err := sc.setSecbits(sb); err != nil {
158		return err
159	}
160
161	if m != ModeNoPriv {
162		return nil
163	}
164
165	for c := Value(0); sc.dropBound(c) == nil; c++ {
166	}
167	w.ClearFlag(Permitted)
168
169	// For good measure.
170	sc.prctlwcall6(prSetNoNewPrivs, 1, 0, 0, 0, 0)
171
172	return nil
173}
174
175// Set attempts to enter the specified mode. An attempt is made to
176// enter the mode, so if you prefer this operation to be a no-op if
177// entering the same mode, call only if CurrentMode() disagrees with
178// the desired mode.
179//
180// This function will raise cap.SETPCAP in order to achieve this
181// operation, and will completely lower the Effective Flag of the
182// process' Set before returning. This function may fail for lack of
183// permission or because (some of) the Secbits are already locked for
184// the current process.
185func (m Mode) Set() error {
186	state, sc := scwStateSC()
187	defer scwSetState(launchBlocked, state, -1)
188	return sc.setMode(m)
189}
190
191// String returns the libcap conventional string for this mode.
192func (m Mode) String() string {
193	switch m {
194	case ModeUncertain:
195		return "UNCERTAIN"
196	case ModeNoPriv:
197		return "NOPRIV"
198	case ModePure1EInit:
199		return "PURE1E_INIT"
200	case ModePure1E:
201		return "PURE1E"
202	default:
203		return "UNKNOWN"
204	}
205}
206
207func (sc *syscaller) setUID(uid int) error {
208	w := GetProc()
209	defer func() {
210		w.ClearFlag(Effective)
211		sc.setProc(w)
212	}()
213
214	if err := w.SetFlag(Effective, true, SETUID); err != nil {
215		return err
216	}
217
218	// these may or may not work depending on whether or not they
219	// are locked. We try them just in case.
220	sc.prctlwcall(prSetKeepCaps, 1, 0)
221	defer sc.prctlwcall(prSetKeepCaps, 0, 0)
222
223	if err := sc.setProc(w); err != nil {
224		return err
225	}
226
227	if _, _, err := sc.w3(syscall.SYS_SETUID, uintptr(uid), 0, 0); err != 0 {
228		return err
229	}
230	return nil
231}
232
233// SetUID is a convenience function for robustly setting the UID and
234// all other variants of UID (EUID etc) to the specified value without
235// dropping the privilege of the current process. This function will
236// raise cap.SETUID in order to achieve this operation, and will
237// completely lower the Effective vector of the process before
238// returning. Unlike the traditional method of dropping privilege when
239// changing from [E]UID=0 to some other UID, this function only
240// performs a change of UID cap.SETUID is available, and the action
241// does not alter the Permitted Flag of the process' Set.
242func SetUID(uid int) error {
243	state, sc := scwStateSC()
244	defer scwSetState(launchBlocked, state, -1)
245	return sc.setUID(uid)
246}
247
248//go:uintptrescapes
249func (sc *syscaller) setGroups(gid int, suppl []int) error {
250	w := GetProc()
251	defer func() {
252		w.ClearFlag(Effective)
253		sc.setProc(w)
254	}()
255
256	if err := w.SetFlag(Effective, true, SETGID); err != nil {
257		return err
258	}
259	if err := sc.setProc(w); err != nil {
260		return err
261	}
262
263	if _, _, err := sc.w3(syscall.SYS_SETGID, uintptr(gid), 0, 0); err != 0 {
264		return err
265	}
266	if len(suppl) == 0 {
267		if _, _, err := sc.w3(sysSetGroupsVariant, 0, 0, 0); err != 0 {
268			return err
269		}
270		return nil
271	}
272
273	// On linux gid values are 32-bits.
274	gs := make([]uint32, len(suppl))
275	for i, g := range suppl {
276		gs[i] = uint32(g)
277	}
278	if _, _, err := sc.w3(sysSetGroupsVariant, uintptr(len(suppl)), uintptr(unsafe.Pointer(&gs[0])), 0); err != 0 {
279		return err
280	}
281	return nil
282}
283
284// SetGroups is a convenience function for robustly setting the GID
285// and all other variants of GID (EGID etc) to the specified value, as
286// well as setting all of the supplementary groups. This function will
287// raise cap.SETGID in order to achieve this operation, and will
288// completely lower the Effective Flag of the process Set before
289// returning.
290func SetGroups(gid int, suppl ...int) error {
291	state, sc := scwStateSC()
292	defer scwSetState(launchBlocked, state, -1)
293	return sc.setGroups(gid, suppl)
294}
295
296//go:uintptrescapes
297
298// Prctlw is a convenience function for performing a syscall.Prctl()
299// call that executes on all the threads of the process. It is called
300// Prctlw because it is only appropriate to call this function when it
301// is writing thread state that the caller wants to set on all OS
302// threads of the process to observe POSIX semantics when Linux
303// doesn't natively honor them. (Check prctl documentation for when it
304// is appropriate to use this vs. a normal syscall.Prctl() call.)
305func Prctlw(prVal uintptr, args ...uintptr) (int, error) {
306	if n := len(args); n > 5 {
307		return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
308	}
309	state, sc := scwStateSC()
310	defer scwSetState(launchBlocked, state, -1)
311	as := make([]uintptr, 5)
312	copy(as, args)
313	return sc.prctlwcall6(prVal, as[0], as[1], as[2], as[3], as[4])
314}
315
316//go:uintptrescapes
317
318// Prctl is a convenience function that performs a syscall.Prctl()
319// that either reads state using a single OS thread, or performs a
320// Prctl that is treated as a process wide setting. It is provided for
321// symmetry reasons, but is equivalent to simply calling the
322// corresponding syscall function.
323func Prctl(prVal uintptr, args ...uintptr) (int, error) {
324	if n := len(args); n > 5 {
325		return -1, fmt.Errorf("prctl supports up to 5 arguments (not %d)", n)
326	}
327	as := make([]uintptr, 5)
328	copy(as, args)
329	return singlesc.prctlrcall6(prVal, as[0], as[1], as[2], as[3], as[4])
330}
331