]>
Commit | Line | Data |
---|---|---|
b3357a6f DL |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
b3357a6f DL |
8 | * |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
250b1eec | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
b3357a6f DL |
22 | */ |
23 | ||
d38dd64a CB |
24 | #ifndef _GNU_SOURCE |
25 | #define _GNU_SOURCE 1 | |
26 | #endif | |
ca364dc0 CB |
27 | #include <errno.h> |
28 | #include <limits.h> | |
20d81659 CS |
29 | #include <fcntl.h> |
30 | #include <stdlib.h> | |
ca364dc0 | 31 | #include <unistd.h> |
b3357a6f | 32 | #include <sys/prctl.h> |
b3357a6f | 33 | |
ca364dc0 | 34 | #include "caps.h" |
d38dd64a | 35 | #include "config.h" |
89882306 | 36 | #include "file_utils.h" |
b3357a6f | 37 | #include "log.h" |
4e60664a | 38 | #include "macro.h" |
b3357a6f | 39 | |
ac2cecc4 | 40 | lxc_log_define(caps, lxc); |
b3357a6f | 41 | |
e37dda71 | 42 | #if HAVE_LIBCAP |
495d2046 | 43 | |
b3357a6f DL |
44 | int lxc_caps_down(void) |
45 | { | |
46 | cap_t caps; | |
4e60664a | 47 | int ret = -1; |
b3357a6f | 48 | |
4e60664a | 49 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
50 | if (!getuid()) |
51 | return 0; | |
52 | ||
b3357a6f DL |
53 | caps = cap_get_proc(); |
54 | if (!caps) { | |
4e60664a CB |
55 | SYSERROR("Failed to retrieve capabilities"); |
56 | return ret; | |
b3357a6f DL |
57 | } |
58 | ||
59 | ret = cap_clear_flag(caps, CAP_EFFECTIVE); | |
60 | if (ret) { | |
4e60664a CB |
61 | SYSERROR("Failed to clear effective capabilities"); |
62 | goto on_error; | |
b3357a6f DL |
63 | } |
64 | ||
65 | ret = cap_set_proc(caps); | |
66 | if (ret) { | |
4e60664a CB |
67 | SYSERROR("Failed to change effective capabilities"); |
68 | goto on_error; | |
b3357a6f DL |
69 | } |
70 | ||
4e60664a CB |
71 | ret = 0; |
72 | ||
73 | on_error: | |
b3357a6f | 74 | cap_free(caps); |
4e60664a CB |
75 | |
76 | return ret; | |
b3357a6f DL |
77 | } |
78 | ||
79 | int lxc_caps_up(void) | |
80 | { | |
81 | cap_t caps; | |
82 | cap_value_t cap; | |
4e60664a | 83 | int ret = -1; |
b3357a6f | 84 | |
4e60664a | 85 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
86 | if (!getuid()) |
87 | return 0; | |
88 | ||
b3357a6f DL |
89 | caps = cap_get_proc(); |
90 | if (!caps) { | |
4e60664a CB |
91 | SYSERROR("Failed to retrieve capabilities"); |
92 | return ret; | |
b3357a6f DL |
93 | } |
94 | ||
95 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
b3357a6f DL |
96 | cap_flag_value_t flag; |
97 | ||
98 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
99 | if (ret) { | |
2b657f10 | 100 | if (errno == EINVAL) { |
4e60664a | 101 | INFO("Last supported cap was %d", cap - 1); |
2b657f10 SH |
102 | break; |
103 | } else { | |
4e60664a CB |
104 | SYSERROR("Failed to retrieve setting for " |
105 | "permitted capability %d", cap - 1); | |
106 | goto on_error; | |
2b657f10 | 107 | } |
b3357a6f DL |
108 | } |
109 | ||
110 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
111 | if (ret) { | |
4e60664a CB |
112 | SYSERROR("Failed to set effective capability %d", cap - 1); |
113 | goto on_error; | |
b3357a6f DL |
114 | } |
115 | } | |
116 | ||
117 | ret = cap_set_proc(caps); | |
118 | if (ret) { | |
4e60664a CB |
119 | SYSERROR("Failed to change effective capabilities"); |
120 | goto on_error; | |
b3357a6f DL |
121 | } |
122 | ||
4e60664a CB |
123 | ret = 0; |
124 | ||
125 | on_error: | |
b3357a6f | 126 | cap_free(caps); |
4e60664a CB |
127 | |
128 | return ret; | |
b3357a6f DL |
129 | } |
130 | ||
611ddd34 CB |
131 | int lxc_ambient_caps_up(void) |
132 | { | |
133 | int ret; | |
134 | cap_t caps; | |
135 | cap_value_t cap; | |
136 | int last_cap = CAP_LAST_CAP; | |
137 | char *cap_names = NULL; | |
138 | ||
df9bf8ca | 139 | if (!getuid() || geteuid()) |
611ddd34 CB |
140 | return 0; |
141 | ||
142 | caps = cap_get_proc(); | |
143 | if (!caps) { | |
144 | SYSERROR("Failed to retrieve capabilities"); | |
145 | return -1; | |
146 | } | |
147 | ||
148 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
149 | cap_flag_value_t flag; | |
150 | ||
151 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
152 | if (ret < 0) { | |
153 | if (errno == EINVAL) { | |
154 | last_cap = (cap - 1); | |
155 | INFO("Last supported cap was %d", last_cap); | |
156 | break; | |
157 | } | |
158 | ||
159 | SYSERROR("Failed to retrieve capability flag"); | |
160 | goto out; | |
161 | } | |
162 | ||
163 | ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag); | |
164 | if (ret < 0) { | |
165 | SYSERROR("Failed to set capability flag"); | |
166 | goto out; | |
167 | } | |
168 | } | |
169 | ||
170 | ret = cap_set_proc(caps); | |
171 | if (ret < 0) { | |
172 | SYSERROR("Failed to set capabilities"); | |
173 | goto out; | |
174 | } | |
175 | ||
176 | for (cap = 0; cap <= last_cap; cap++) { | |
b81689a1 CB |
177 | ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_RAISE), |
178 | prctl_arg(cap), prctl_arg(0), prctl_arg(0)); | |
611ddd34 | 179 | if (ret < 0) { |
a24c5678 | 180 | SYSWARN("Failed to raise ambient capability %d", cap); |
611ddd34 CB |
181 | goto out; |
182 | } | |
183 | } | |
184 | ||
185 | cap_names = cap_to_text(caps, NULL); | |
6f5e532f | 186 | if (!cap_names) { |
187 | SYSWARN("Failed to convert capabilities %d", cap); | |
611ddd34 | 188 | goto out; |
6f5e532f | 189 | } |
611ddd34 CB |
190 | |
191 | TRACE("Raised %s in inheritable and ambient capability set", cap_names); | |
192 | ||
193 | out: | |
194 | ||
195 | cap_free(cap_names); | |
196 | cap_free(caps); | |
197 | return 0; | |
198 | } | |
199 | ||
200 | int lxc_ambient_caps_down(void) | |
201 | { | |
202 | int ret; | |
203 | cap_t caps; | |
204 | cap_value_t cap; | |
205 | ||
df9bf8ca | 206 | if (!getuid() || geteuid()) |
611ddd34 CB |
207 | return 0; |
208 | ||
b81689a1 CB |
209 | ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_CLEAR_ALL), |
210 | prctl_arg(0), prctl_arg(0), prctl_arg(0)); | |
611ddd34 CB |
211 | if (ret < 0) { |
212 | SYSERROR("Failed to clear ambient capability set"); | |
213 | return -1; | |
214 | } | |
215 | ||
216 | caps = cap_get_proc(); | |
217 | if (!caps) { | |
218 | SYSERROR("Failed to retrieve capabilities"); | |
219 | return -1; | |
220 | } | |
221 | ||
222 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
223 | ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR); | |
224 | if (ret < 0) { | |
225 | SYSERROR("Failed to remove capability from inheritable set"); | |
226 | goto out; | |
227 | } | |
228 | } | |
229 | ||
230 | ret = cap_set_proc(caps); | |
231 | if (ret < 0) { | |
232 | SYSERROR("Failed to set capabilities"); | |
233 | goto out; | |
234 | } | |
235 | ||
236 | out: | |
237 | cap_free(caps); | |
238 | return 0; | |
239 | } | |
240 | ||
b3357a6f DL |
241 | int lxc_caps_init(void) |
242 | { | |
4e60664a | 243 | uid_t euid, uid; |
b3357a6f | 244 | |
4e60664a CB |
245 | uid = getuid(); |
246 | if (!uid) | |
b3357a6f | 247 | return 0; |
b3357a6f | 248 | |
4e60664a | 249 | euid = geteuid(); |
b3357a6f | 250 | if (uid && !euid) { |
4e60664a CB |
251 | int ret; |
252 | gid_t gid; | |
253 | ||
254 | INFO("Command is run as setuid root (uid: %d)", uid); | |
b3357a6f | 255 | |
b81689a1 | 256 | ret = prctl(PR_SET_KEEPCAPS, prctl_arg(1)); |
4e60664a CB |
257 | if (ret < 0) { |
258 | SYSERROR("Failed to set PR_SET_KEEPCAPS"); | |
b3357a6f DL |
259 | return -1; |
260 | } | |
261 | ||
4e60664a CB |
262 | gid = getgid(); |
263 | ret = setresgid(gid, gid, gid); | |
264 | if (ret < 0) { | |
265 | SYSERROR("Failed to change rgid, egid, and sgid to %d", gid); | |
b3357a6f DL |
266 | return -1; |
267 | } | |
268 | ||
4e60664a CB |
269 | ret = setresuid(uid, uid, uid); |
270 | if (ret < 0) { | |
271 | SYSERROR("Failed to change ruid, euid, and suid to %d", uid); | |
b3357a6f DL |
272 | return -1; |
273 | } | |
274 | ||
4e60664a CB |
275 | ret = lxc_caps_up(); |
276 | if (ret < 0) { | |
6d1400b5 | 277 | SYSERROR("Failed to restore capabilities"); |
b3357a6f DL |
278 | return -1; |
279 | } | |
280 | } | |
281 | ||
282 | if (uid == euid) | |
4e60664a | 283 | INFO("Command is run with uid %d", uid); |
b3357a6f DL |
284 | |
285 | return 0; | |
286 | } | |
20d81659 | 287 | |
92d5ea57 | 288 | static long int _real_caps_last_cap(void) |
20d81659 | 289 | { |
4e60664a | 290 | int fd, result = -1; |
20d81659 | 291 | |
4e60664a CB |
292 | /* Try to get the maximum capability over the kernel interface |
293 | * introduced in v3.2. | |
294 | */ | |
295 | fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY | O_CLOEXEC); | |
20d81659 | 296 | if (fd >= 0) { |
4e60664a | 297 | ssize_t n; |
20d81659 | 298 | char *ptr; |
22b67bfa | 299 | char buf[INTTYPE_TO_STRLEN(int)] = {0}; |
20d81659 | 300 | |
89882306 | 301 | n = lxc_read_nointr(fd, buf, STRARRAYLEN(buf)); |
302 | if (n >= 0) { | |
4e60664a | 303 | errno = 0; |
20d81659 | 304 | result = strtol(buf, &ptr, 10); |
09bbd745 | 305 | if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) |
20d81659 CS |
306 | result = -1; |
307 | } | |
308 | ||
309 | close(fd); | |
4e60664a | 310 | } else { |
20d81659 | 311 | int cap = 0; |
6f5e532f | 312 | |
4e60664a CB |
313 | /* Try to get it manually by trying to get the status of each |
314 | * capability individually from the kernel. | |
315 | */ | |
b81689a1 | 316 | while (prctl(PR_CAPBSET_READ, prctl_arg(cap)) >= 0) |
6f5e532f | 317 | cap++; |
318 | ||
20d81659 CS |
319 | result = cap - 1; |
320 | } | |
321 | ||
322 | return result; | |
323 | } | |
324 | ||
325 | int lxc_caps_last_cap(void) | |
326 | { | |
92d5ea57 | 327 | static long int last_cap = -1; |
6f5e532f | 328 | |
92d5ea57 | 329 | if (last_cap < 0) { |
6f5e532f | 330 | last_cap = _real_caps_last_cap(); |
92d5ea57 | 331 | if (last_cap < 0 || last_cap > INT_MAX) |
332 | last_cap = -1; | |
333 | } | |
20d81659 CS |
334 | |
335 | return last_cap; | |
336 | } | |
4a2ca8b2 | 337 | |
207c4c71 | 338 | static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) |
ca364dc0 CB |
339 | { |
340 | int ret; | |
ca364dc0 CB |
341 | cap_flag_value_t flagval; |
342 | ||
207c4c71 CB |
343 | ret = cap_get_flag(caps, cap, flag, &flagval); |
344 | if (ret < 0) { | |
4e60664a | 345 | SYSERROR("Failed to retrieve current setting for capability %d", cap); |
207c4c71 CB |
346 | return false; |
347 | } | |
348 | ||
349 | return flagval == CAP_SET; | |
350 | } | |
351 | ||
352 | bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) | |
353 | { | |
4e60664a | 354 | #if LIBCAP_SUPPORTS_FILE_CAPABILITIES |
207c4c71 CB |
355 | bool cap_is_set; |
356 | cap_t caps; | |
357 | ||
358 | caps = cap_get_file(path); | |
ca364dc0 | 359 | if (!caps) { |
207c4c71 CB |
360 | /* This is undocumented in the manpage but the source code show |
361 | * that cap_get_file() may return NULL when successful for the | |
362 | * case where it didn't detect any file capabilities. In this | |
363 | * case errno will be set to ENODATA. | |
364 | */ | |
365 | if (errno != ENODATA) | |
4e60664a | 366 | SYSERROR("Failed to retrieve capabilities for file %s", path); |
6d1400b5 | 367 | |
ca364dc0 CB |
368 | return false; |
369 | } | |
370 | ||
207c4c71 CB |
371 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
372 | cap_free(caps); | |
373 | return cap_is_set; | |
4e60664a | 374 | #else |
69924fff CB |
375 | errno = ENODATA; |
376 | return false; | |
4e60664a | 377 | #endif |
207c4c71 CB |
378 | } |
379 | ||
380 | bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) | |
381 | { | |
382 | bool cap_is_set; | |
383 | cap_t caps; | |
384 | ||
385 | caps = cap_get_proc(); | |
386 | if (!caps) { | |
4e60664a | 387 | SYSERROR("Failed to retrieve capabilities"); |
ca364dc0 CB |
388 | return false; |
389 | } | |
390 | ||
207c4c71 | 391 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
ca364dc0 | 392 | cap_free(caps); |
207c4c71 | 393 | return cap_is_set; |
ca364dc0 | 394 | } |
495d2046 | 395 | #endif |