1package caps // import "github.com/docker/docker/oci/caps"
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/docker/docker/errdefs"
8	"github.com/syndtr/gocapability/capability"
9)
10
11var capabilityList Capabilities
12
13func init() {
14	last := capability.CAP_LAST_CAP
15	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
16	if last == capability.Cap(63) {
17		last = capability.CAP_BLOCK_SUSPEND
18	}
19	if last > capability.CAP_AUDIT_READ {
20		// Prevents docker from setting CAP_PERFMON, CAP_BPF, and CAP_CHECKPOINT_RESTORE
21		// capabilities on privileged (or CAP_ALL) containers on Kernel 5.8 and up.
22		// While these kernels support these capabilities, the current release of
23		// runc ships with an older version of /gocapability/capability, and does
24		// not know about them, causing an error to be produced.
25		//
26		// FIXME remove once https://github.com/opencontainers/runc/commit/6dfbe9b80707b1ca188255e8def15263348e0f9a
27		//       is included in a runc release and once we stop supporting containerd 1.3.x
28		//       (which ships with runc v1.0.0-rc92)
29		last = capability.CAP_AUDIT_READ
30	}
31	for _, cap := range capability.List() {
32		if cap > last {
33			continue
34		}
35		capabilityList = append(capabilityList,
36			&CapabilityMapping{
37				Key:   "CAP_" + strings.ToUpper(cap.String()),
38				Value: cap,
39			},
40		)
41	}
42}
43
44type (
45	// CapabilityMapping maps linux capability name to its value of capability.Cap type
46	// Capabilities is one of the security systems in Linux Security Module (LSM)
47	// framework provided by the kernel.
48	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
49	CapabilityMapping struct {
50		Key   string         `json:"key,omitempty"`
51		Value capability.Cap `json:"value,omitempty"`
52	}
53	// Capabilities contains all CapabilityMapping
54	Capabilities []*CapabilityMapping
55)
56
57// String returns <key> of CapabilityMapping
58func (c *CapabilityMapping) String() string {
59	return c.Key
60}
61
62// GetCapability returns CapabilityMapping which contains specific key
63func GetCapability(key string) *CapabilityMapping {
64	for _, capp := range capabilityList {
65		if capp.Key == key {
66			cpy := *capp
67			return &cpy
68		}
69	}
70	return nil
71}
72
73// GetAllCapabilities returns all of the capabilities
74func GetAllCapabilities() []string {
75	output := make([]string, len(capabilityList))
76	for i, capability := range capabilityList {
77		output[i] = capability.String()
78	}
79	return output
80}
81
82// inSlice tests whether a string is contained in a slice of strings or not.
83func inSlice(slice []string, s string) bool {
84	for _, ss := range slice {
85		if s == ss {
86			return true
87		}
88	}
89	return false
90}
91
92const allCapabilities = "ALL"
93
94// NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
95// by upper-casing them, and adding a CAP_ prefix (if not yet present).
96//
97// This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
98func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
99	var normalized []string
100
101	valids := GetAllCapabilities()
102	for _, c := range caps {
103		c = strings.ToUpper(c)
104		if c == allCapabilities {
105			normalized = append(normalized, c)
106			continue
107		}
108		if !strings.HasPrefix(c, "CAP_") {
109			c = "CAP_" + c
110		}
111		if !inSlice(valids, c) {
112			return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
113		}
114		normalized = append(normalized, c)
115	}
116	return normalized, nil
117}
118
119// ValidateCapabilities validates if caps only contains valid capabilities
120func ValidateCapabilities(caps []string) error {
121	valids := GetAllCapabilities()
122	for _, c := range caps {
123		if !inSlice(valids, c) {
124			return errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
125		}
126	}
127	return nil
128}
129
130// TweakCapabilities tweaks capabilities by adding, dropping, or overriding
131// capabilities in the basics capabilities list.
132func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) {
133	switch {
134	case privileged:
135		// Privileged containers get all capabilities
136		return GetAllCapabilities(), nil
137	case len(adds) == 0 && len(drops) == 0:
138		// Nothing to tweak; we're done
139		return basics, nil
140	}
141
142	capDrop, err := NormalizeLegacyCapabilities(drops)
143	if err != nil {
144		return nil, err
145	}
146	capAdd, err := NormalizeLegacyCapabilities(adds)
147	if err != nil {
148		return nil, err
149	}
150
151	var caps []string
152
153	switch {
154	case inSlice(capAdd, allCapabilities):
155		// Add all capabilities except ones on capDrop
156		for _, c := range GetAllCapabilities() {
157			if !inSlice(capDrop, c) {
158				caps = append(caps, c)
159			}
160		}
161	case inSlice(capDrop, allCapabilities):
162		// "Drop" all capabilities; use what's in capAdd instead
163		caps = capAdd
164	default:
165		// First drop some capabilities
166		for _, c := range basics {
167			if !inSlice(capDrop, c) {
168				caps = append(caps, c)
169			}
170		}
171		// Then add the list of capabilities from capAdd
172		caps = append(caps, capAdd...)
173	}
174	return caps, nil
175}
176