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