]>
Commit | Line | Data |
---|---|---|
cc73685d | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
b3357a6f | 2 | |
d38dd64a CB |
3 | #ifndef _GNU_SOURCE |
4 | #define _GNU_SOURCE 1 | |
5 | #endif | |
ca364dc0 CB |
6 | #include <errno.h> |
7 | #include <limits.h> | |
20d81659 CS |
8 | #include <fcntl.h> |
9 | #include <stdlib.h> | |
ca364dc0 | 10 | #include <unistd.h> |
b3357a6f | 11 | #include <sys/prctl.h> |
b3357a6f | 12 | |
ca364dc0 | 13 | #include "caps.h" |
d38dd64a | 14 | #include "config.h" |
89882306 | 15 | #include "file_utils.h" |
b3357a6f | 16 | #include "log.h" |
4e60664a | 17 | #include "macro.h" |
1e22a683 | 18 | #include "memory_utils.h" |
b3357a6f | 19 | |
ac2cecc4 | 20 | lxc_log_define(caps, lxc); |
b3357a6f | 21 | |
e37dda71 | 22 | #if HAVE_LIBCAP |
495d2046 | 23 | |
1e22a683 CB |
24 | define_cleanup_function(cap_t, cap_free); |
25 | ||
b3357a6f DL |
26 | int lxc_caps_down(void) |
27 | { | |
1e22a683 | 28 | call_cleaner(cap_free) cap_t caps = NULL; |
4e60664a | 29 | int ret = -1; |
b3357a6f | 30 | |
4e60664a | 31 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
32 | if (!getuid()) |
33 | return 0; | |
34 | ||
b3357a6f | 35 | caps = cap_get_proc(); |
1e22a683 CB |
36 | if (!caps) |
37 | return log_error_errno(ret, errno, "Failed to retrieve capabilities"); | |
b3357a6f DL |
38 | |
39 | ret = cap_clear_flag(caps, CAP_EFFECTIVE); | |
1e22a683 CB |
40 | if (ret) |
41 | return log_error_errno(ret, errno, "Failed to clear effective capabilities"); | |
b3357a6f DL |
42 | |
43 | ret = cap_set_proc(caps); | |
1e22a683 CB |
44 | if (ret) |
45 | return log_error_errno(ret, errno, "Failed to change effective capabilities"); | |
4e60664a | 46 | |
1e22a683 | 47 | return 0; |
b3357a6f DL |
48 | } |
49 | ||
50 | int lxc_caps_up(void) | |
51 | { | |
1e22a683 | 52 | call_cleaner(cap_free) cap_t caps = NULL; |
b3357a6f | 53 | cap_value_t cap; |
4e60664a | 54 | int ret = -1; |
b3357a6f | 55 | |
4e60664a | 56 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
57 | if (!getuid()) |
58 | return 0; | |
59 | ||
b3357a6f | 60 | caps = cap_get_proc(); |
1e22a683 CB |
61 | if (!caps) |
62 | return log_error_errno(ret, errno, "Failed to retrieve capabilities"); | |
b3357a6f DL |
63 | |
64 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
b3357a6f DL |
65 | cap_flag_value_t flag; |
66 | ||
67 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
68 | if (ret) { | |
2b657f10 | 69 | if (errno == EINVAL) { |
4e60664a | 70 | INFO("Last supported cap was %d", cap - 1); |
2b657f10 SH |
71 | break; |
72 | } else { | |
1e22a683 | 73 | return log_error_errno(ret, errno, "Failed to retrieve setting for permitted capability %d", cap - 1); |
2b657f10 | 74 | } |
b3357a6f DL |
75 | } |
76 | ||
77 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
1e22a683 CB |
78 | if (ret) |
79 | return log_error_errno(ret, errno, "Failed to set effective capability %d", cap - 1); | |
b3357a6f DL |
80 | } |
81 | ||
82 | ret = cap_set_proc(caps); | |
1e22a683 CB |
83 | if (ret) |
84 | return log_error_errno(ret, errno, "Failed to change effective capabilities"); | |
4e60664a | 85 | |
1e22a683 | 86 | return 0; |
b3357a6f DL |
87 | } |
88 | ||
611ddd34 CB |
89 | int lxc_ambient_caps_up(void) |
90 | { | |
1e22a683 CB |
91 | call_cleaner(cap_free) cap_t caps = NULL; |
92 | __do_free char *cap_names = NULL; | |
611ddd34 | 93 | int ret; |
611ddd34 CB |
94 | cap_value_t cap; |
95 | int last_cap = CAP_LAST_CAP; | |
611ddd34 | 96 | |
df9bf8ca | 97 | if (!getuid() || geteuid()) |
611ddd34 CB |
98 | return 0; |
99 | ||
100 | caps = cap_get_proc(); | |
1e22a683 CB |
101 | if (!caps) |
102 | return log_error_errno(-1, errno, "Failed to retrieve capabilities"); | |
611ddd34 CB |
103 | |
104 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
105 | cap_flag_value_t flag; | |
106 | ||
107 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
108 | if (ret < 0) { | |
109 | if (errno == EINVAL) { | |
110 | last_cap = (cap - 1); | |
111 | INFO("Last supported cap was %d", last_cap); | |
112 | break; | |
113 | } | |
114 | ||
1e22a683 | 115 | return log_error_errno(ret, errno, "Failed to retrieve capability flag"); |
611ddd34 CB |
116 | } |
117 | ||
118 | ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag); | |
1e22a683 CB |
119 | if (ret < 0) |
120 | return log_error_errno(ret, errno, "Failed to set capability flag"); | |
611ddd34 CB |
121 | } |
122 | ||
123 | ret = cap_set_proc(caps); | |
1e22a683 CB |
124 | if (ret < 0) |
125 | return log_error_errno(ret, errno, "Failed to set capabilities"); | |
611ddd34 CB |
126 | |
127 | for (cap = 0; cap <= last_cap; cap++) { | |
b81689a1 CB |
128 | ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_RAISE), |
129 | prctl_arg(cap), prctl_arg(0), prctl_arg(0)); | |
1e22a683 CB |
130 | if (ret < 0) |
131 | return log_warn_errno(ret, errno, "Failed to raise ambient capability %d", cap); | |
611ddd34 CB |
132 | } |
133 | ||
134 | cap_names = cap_to_text(caps, NULL); | |
1e22a683 CB |
135 | if (!cap_names) |
136 | return log_warn_errno(0, errno, "Failed to convert capabilities %d", cap); | |
611ddd34 CB |
137 | |
138 | TRACE("Raised %s in inheritable and ambient capability set", cap_names); | |
611ddd34 CB |
139 | return 0; |
140 | } | |
141 | ||
142 | int lxc_ambient_caps_down(void) | |
143 | { | |
1e22a683 | 144 | call_cleaner(cap_free) cap_t caps = NULL; |
611ddd34 | 145 | int ret; |
611ddd34 CB |
146 | cap_value_t cap; |
147 | ||
df9bf8ca | 148 | if (!getuid() || geteuid()) |
611ddd34 CB |
149 | return 0; |
150 | ||
b81689a1 CB |
151 | ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_CLEAR_ALL), |
152 | prctl_arg(0), prctl_arg(0), prctl_arg(0)); | |
1e22a683 CB |
153 | if (ret < 0) |
154 | return log_error_errno(-1, errno, "Failed to clear ambient capability set"); | |
611ddd34 CB |
155 | |
156 | caps = cap_get_proc(); | |
1e22a683 CB |
157 | if (!caps) |
158 | return log_error_errno(-1, errno, "Failed to retrieve capabilities"); | |
611ddd34 CB |
159 | |
160 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
161 | ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR); | |
1e22a683 CB |
162 | if (ret < 0) |
163 | return log_error_errno(-1, errno, "Failed to clear capability"); | |
611ddd34 CB |
164 | } |
165 | ||
166 | ret = cap_set_proc(caps); | |
1e22a683 CB |
167 | if (ret < 0) |
168 | return log_error_errno(ret, errno, "Failed to set capabilities"); | |
611ddd34 | 169 | |
611ddd34 CB |
170 | return 0; |
171 | } | |
172 | ||
b3357a6f DL |
173 | int lxc_caps_init(void) |
174 | { | |
4e60664a | 175 | uid_t euid, uid; |
b3357a6f | 176 | |
4e60664a CB |
177 | uid = getuid(); |
178 | if (!uid) | |
b3357a6f | 179 | return 0; |
b3357a6f | 180 | |
4e60664a | 181 | euid = geteuid(); |
b3357a6f | 182 | if (uid && !euid) { |
4e60664a CB |
183 | int ret; |
184 | gid_t gid; | |
185 | ||
186 | INFO("Command is run as setuid root (uid: %d)", uid); | |
b3357a6f | 187 | |
b81689a1 | 188 | ret = prctl(PR_SET_KEEPCAPS, prctl_arg(1)); |
1e22a683 CB |
189 | if (ret < 0) |
190 | return log_error_errno(-1, errno, "Failed to set PR_SET_KEEPCAPS"); | |
b3357a6f | 191 | |
4e60664a CB |
192 | gid = getgid(); |
193 | ret = setresgid(gid, gid, gid); | |
1e22a683 CB |
194 | if (ret < 0) |
195 | return log_error_errno(-1, errno, "Failed to change rgid, egid, and sgid to %d", gid); | |
b3357a6f | 196 | |
4e60664a | 197 | ret = setresuid(uid, uid, uid); |
1e22a683 CB |
198 | if (ret < 0) |
199 | return log_error_errno(-1, errno, "Failed to change ruid, euid, and suid to %d", uid); | |
b3357a6f | 200 | |
4e60664a | 201 | ret = lxc_caps_up(); |
1e22a683 CB |
202 | if (ret < 0) |
203 | return log_error_errno(-1, errno, "Failed to restore capabilities"); | |
b3357a6f DL |
204 | } |
205 | ||
206 | if (uid == euid) | |
4e60664a | 207 | INFO("Command is run with uid %d", uid); |
b3357a6f DL |
208 | |
209 | return 0; | |
210 | } | |
20d81659 | 211 | |
92d5ea57 | 212 | static long int _real_caps_last_cap(void) |
20d81659 | 213 | { |
4e60664a | 214 | int fd, result = -1; |
20d81659 | 215 | |
4e60664a CB |
216 | /* Try to get the maximum capability over the kernel interface |
217 | * introduced in v3.2. | |
218 | */ | |
219 | fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY | O_CLOEXEC); | |
20d81659 | 220 | if (fd >= 0) { |
4e60664a | 221 | ssize_t n; |
20d81659 | 222 | char *ptr; |
22b67bfa | 223 | char buf[INTTYPE_TO_STRLEN(int)] = {0}; |
20d81659 | 224 | |
89882306 | 225 | n = lxc_read_nointr(fd, buf, STRARRAYLEN(buf)); |
226 | if (n >= 0) { | |
4e60664a | 227 | errno = 0; |
20d81659 | 228 | result = strtol(buf, &ptr, 10); |
09bbd745 | 229 | if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) |
20d81659 CS |
230 | result = -1; |
231 | } | |
232 | ||
233 | close(fd); | |
4e60664a | 234 | } else { |
20d81659 | 235 | int cap = 0; |
6f5e532f | 236 | |
4e60664a CB |
237 | /* Try to get it manually by trying to get the status of each |
238 | * capability individually from the kernel. | |
239 | */ | |
b81689a1 | 240 | while (prctl(PR_CAPBSET_READ, prctl_arg(cap)) >= 0) |
6f5e532f | 241 | cap++; |
242 | ||
20d81659 CS |
243 | result = cap - 1; |
244 | } | |
245 | ||
246 | return result; | |
247 | } | |
248 | ||
249 | int lxc_caps_last_cap(void) | |
250 | { | |
92d5ea57 | 251 | static long int last_cap = -1; |
6f5e532f | 252 | |
92d5ea57 | 253 | if (last_cap < 0) { |
6f5e532f | 254 | last_cap = _real_caps_last_cap(); |
92d5ea57 | 255 | if (last_cap < 0 || last_cap > INT_MAX) |
256 | last_cap = -1; | |
257 | } | |
20d81659 CS |
258 | |
259 | return last_cap; | |
260 | } | |
4a2ca8b2 | 261 | |
207c4c71 | 262 | static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) |
ca364dc0 CB |
263 | { |
264 | int ret; | |
ca364dc0 CB |
265 | cap_flag_value_t flagval; |
266 | ||
207c4c71 | 267 | ret = cap_get_flag(caps, cap, flag, &flagval); |
1e22a683 CB |
268 | if (ret < 0) |
269 | return log_error_errno(false, errno, "Failed to retrieve current setting for capability %d", cap); | |
207c4c71 CB |
270 | |
271 | return flagval == CAP_SET; | |
272 | } | |
273 | ||
274 | bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) | |
275 | { | |
4e60664a | 276 | #if LIBCAP_SUPPORTS_FILE_CAPABILITIES |
1e22a683 | 277 | call_cleaner(cap_free) cap_t caps = NULL; |
207c4c71 CB |
278 | |
279 | caps = cap_get_file(path); | |
ca364dc0 | 280 | if (!caps) { |
207c4c71 CB |
281 | /* This is undocumented in the manpage but the source code show |
282 | * that cap_get_file() may return NULL when successful for the | |
283 | * case where it didn't detect any file capabilities. In this | |
284 | * case errno will be set to ENODATA. | |
285 | */ | |
286 | if (errno != ENODATA) | |
4e60664a | 287 | SYSERROR("Failed to retrieve capabilities for file %s", path); |
6d1400b5 | 288 | |
ca364dc0 CB |
289 | return false; |
290 | } | |
291 | ||
1e22a683 | 292 | return lxc_cap_is_set(caps, cap, flag); |
4e60664a | 293 | #else |
69924fff CB |
294 | errno = ENODATA; |
295 | return false; | |
4e60664a | 296 | #endif |
207c4c71 CB |
297 | } |
298 | ||
299 | bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) | |
300 | { | |
1e22a683 | 301 | call_cleaner(cap_free) cap_t caps = NULL; |
207c4c71 CB |
302 | |
303 | caps = cap_get_proc(); | |
1e22a683 CB |
304 | if (!caps) |
305 | return log_error_errno(false, errno, "Failed to retrieve capabilities"); | |
ca364dc0 | 306 | |
1e22a683 | 307 | return lxc_cap_is_set(caps, cap, flag); |
ca364dc0 | 308 | } |
495d2046 | 309 | #endif |