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