1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17// Package cap provides Linux capability utility
18package cap
19
20import (
21	"bufio"
22	"io"
23	"os"
24	"strconv"
25	"strings"
26
27	"github.com/pkg/errors"
28)
29
30// FromNumber returns a cap string like "CAP_SYS_ADMIN"
31// that corresponds to the given number like 21.
32//
33// FromNumber returns an empty string for unknown cap number.
34func FromNumber(num int) string {
35	if num < 0 || num > len(capsLatest)-1 {
36		return ""
37	}
38	return capsLatest[num]
39}
40
41// FromBitmap parses an uint64 bitmap into string slice like
42// []{"CAP_SYS_ADMIN", ...}.
43//
44// Unknown cap numbers are returned as []int.
45func FromBitmap(v uint64) ([]string, []int) {
46	var (
47		res     []string
48		unknown []int
49	)
50	for i := 0; i <= 63; i++ {
51		if b := (v >> i) & 0x1; b == 0x1 {
52			if s := FromNumber(i); s != "" {
53				res = append(res, s)
54			} else {
55				unknown = append(unknown, i)
56			}
57		}
58	}
59	return res, unknown
60}
61
62// Type is the type of capability
63type Type int
64
65const (
66	// Effective is CapEff
67	Effective Type = 1 << iota
68	// Permitted is CapPrm
69	Permitted
70	// Inheritable is CapInh
71	Inheritable
72	// Bounding is CapBnd
73	Bounding
74	// Ambient is CapAmb
75	Ambient
76)
77
78// ParseProcPIDStatus returns uint64 bitmap value from /proc/<PID>/status file
79func ParseProcPIDStatus(r io.Reader) (map[Type]uint64, error) {
80	res := make(map[Type]uint64)
81	scanner := bufio.NewScanner(r)
82	for scanner.Scan() {
83		line := scanner.Text()
84		pair := strings.SplitN(line, ":", 2)
85		if len(pair) != 2 {
86			continue
87		}
88		k := strings.TrimSpace(pair[0])
89		v := strings.TrimSpace(pair[1])
90		switch k {
91		case "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb":
92			ui64, err := strconv.ParseUint(v, 16, 64)
93			if err != nil {
94				return nil, errors.Errorf("failed to parse line %q", line)
95			}
96			switch k {
97			case "CapInh":
98				res[Inheritable] = ui64
99			case "CapPrm":
100				res[Permitted] = ui64
101			case "CapEff":
102				res[Effective] = ui64
103			case "CapBnd":
104				res[Bounding] = ui64
105			case "CapAmb":
106				res[Ambient] = ui64
107			}
108		}
109	}
110	if err := scanner.Err(); err != nil {
111		return nil, err
112	}
113	return res, nil
114}
115
116// Current returns the list of the effective and the known caps of
117// the current process.
118//
119// The result is like []string{"CAP_SYS_ADMIN", ...}.
120//
121// The result does not contain caps that are not recognized by
122// the "github.com/syndtr/gocapability" library.
123func Current() ([]string, error) {
124	f, err := os.Open("/proc/self/status")
125	if err != nil {
126		return nil, err
127	}
128	defer f.Close()
129	caps, err := ParseProcPIDStatus(f)
130	if err != nil {
131		return nil, err
132	}
133	capEff := caps[Effective]
134	names, _ := FromBitmap(capEff)
135	return names, nil
136}
137
138var (
139	// caps35 is the caps of kernel 3.5 (37 entries)
140	caps35 = []string{
141		"CAP_CHOWN",            // 2.2
142		"CAP_DAC_OVERRIDE",     // 2.2
143		"CAP_DAC_READ_SEARCH",  // 2.2
144		"CAP_FOWNER",           // 2.2
145		"CAP_FSETID",           // 2.2
146		"CAP_KILL",             // 2.2
147		"CAP_SETGID",           // 2.2
148		"CAP_SETUID",           // 2.2
149		"CAP_SETPCAP",          // 2.2
150		"CAP_LINUX_IMMUTABLE",  // 2.2
151		"CAP_NET_BIND_SERVICE", // 2.2
152		"CAP_NET_BROADCAST",    // 2.2
153		"CAP_NET_ADMIN",        // 2.2
154		"CAP_NET_RAW",          // 2.2
155		"CAP_IPC_LOCK",         // 2.2
156		"CAP_IPC_OWNER",        // 2.2
157		"CAP_SYS_MODULE",       // 2.2
158		"CAP_SYS_RAWIO",        // 2.2
159		"CAP_SYS_CHROOT",       // 2.2
160		"CAP_SYS_PTRACE",       // 2.2
161		"CAP_SYS_PACCT",        // 2.2
162		"CAP_SYS_ADMIN",        // 2.2
163		"CAP_SYS_BOOT",         // 2.2
164		"CAP_SYS_NICE",         // 2.2
165		"CAP_SYS_RESOURCE",     // 2.2
166		"CAP_SYS_TIME",         // 2.2
167		"CAP_SYS_TTY_CONFIG",   // 2.2
168		"CAP_MKNOD",            // 2.4
169		"CAP_LEASE",            // 2.4
170		"CAP_AUDIT_WRITE",      // 2.6.11
171		"CAP_AUDIT_CONTROL",    // 2.6.11
172		"CAP_SETFCAP",          // 2.6.24
173		"CAP_MAC_OVERRIDE",     // 2.6.25
174		"CAP_MAC_ADMIN",        // 2.6.25
175		"CAP_SYSLOG",           // 2.6.37
176		"CAP_WAKE_ALARM",       // 3.0
177		"CAP_BLOCK_SUSPEND",    // 3.5
178	}
179	// caps316 is the caps of kernel 3.16 (38 entries)
180	caps316 = append(caps35, "CAP_AUDIT_READ")
181	// caps58 is the caps of kernel 5.8 (40 entries)
182	caps58 = append(caps316, []string{"CAP_PERFMON", "CAP_BPF"}...)
183	// caps59 is the caps of kernel 5.9 (41 entries)
184	caps59     = append(caps58, "CAP_CHECKPOINT_RESTORE")
185	capsLatest = caps59
186)
187
188// Known returns the known cap strings of the latest kernel.
189// The current latest kernel is 5.9.
190func Known() []string {
191	return capsLatest
192}
193