]>
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" |
b3357a6f | 36 | #include "log.h" |
4e60664a | 37 | #include "macro.h" |
b3357a6f | 38 | |
ac2cecc4 | 39 | lxc_log_define(caps, lxc); |
b3357a6f | 40 | |
e37dda71 | 41 | #if HAVE_LIBCAP |
495d2046 | 42 | |
b3357a6f DL |
43 | int lxc_caps_down(void) |
44 | { | |
45 | cap_t caps; | |
4e60664a | 46 | int ret = -1; |
b3357a6f | 47 | |
4e60664a | 48 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
49 | if (!getuid()) |
50 | return 0; | |
51 | ||
b3357a6f DL |
52 | caps = cap_get_proc(); |
53 | if (!caps) { | |
4e60664a CB |
54 | SYSERROR("Failed to retrieve capabilities"); |
55 | return ret; | |
b3357a6f DL |
56 | } |
57 | ||
58 | ret = cap_clear_flag(caps, CAP_EFFECTIVE); | |
59 | if (ret) { | |
4e60664a CB |
60 | SYSERROR("Failed to clear effective capabilities"); |
61 | goto on_error; | |
b3357a6f DL |
62 | } |
63 | ||
64 | ret = cap_set_proc(caps); | |
65 | if (ret) { | |
4e60664a CB |
66 | SYSERROR("Failed to change effective capabilities"); |
67 | goto on_error; | |
b3357a6f DL |
68 | } |
69 | ||
4e60664a CB |
70 | ret = 0; |
71 | ||
72 | on_error: | |
b3357a6f | 73 | cap_free(caps); |
4e60664a CB |
74 | |
75 | return ret; | |
b3357a6f DL |
76 | } |
77 | ||
78 | int lxc_caps_up(void) | |
79 | { | |
80 | cap_t caps; | |
81 | cap_value_t cap; | |
4e60664a | 82 | int ret = -1; |
b3357a6f | 83 | |
4e60664a | 84 | /* When we are root, we don't want to play with capabilities. */ |
7ee895e4 DL |
85 | if (!getuid()) |
86 | return 0; | |
87 | ||
b3357a6f DL |
88 | caps = cap_get_proc(); |
89 | if (!caps) { | |
4e60664a CB |
90 | SYSERROR("Failed to retrieve capabilities"); |
91 | return ret; | |
b3357a6f DL |
92 | } |
93 | ||
94 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
b3357a6f DL |
95 | cap_flag_value_t flag; |
96 | ||
97 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
98 | if (ret) { | |
2b657f10 | 99 | if (errno == EINVAL) { |
4e60664a | 100 | INFO("Last supported cap was %d", cap - 1); |
2b657f10 SH |
101 | break; |
102 | } else { | |
4e60664a CB |
103 | SYSERROR("Failed to retrieve setting for " |
104 | "permitted capability %d", cap - 1); | |
105 | goto on_error; | |
2b657f10 | 106 | } |
b3357a6f DL |
107 | } |
108 | ||
109 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
110 | if (ret) { | |
4e60664a CB |
111 | SYSERROR("Failed to set effective capability %d", cap - 1); |
112 | goto on_error; | |
b3357a6f DL |
113 | } |
114 | } | |
115 | ||
116 | ret = cap_set_proc(caps); | |
117 | if (ret) { | |
4e60664a CB |
118 | SYSERROR("Failed to change effective capabilities"); |
119 | goto on_error; | |
b3357a6f DL |
120 | } |
121 | ||
4e60664a CB |
122 | ret = 0; |
123 | ||
124 | on_error: | |
b3357a6f | 125 | cap_free(caps); |
4e60664a CB |
126 | |
127 | return ret; | |
b3357a6f DL |
128 | } |
129 | ||
611ddd34 CB |
130 | int lxc_ambient_caps_up(void) |
131 | { | |
132 | int ret; | |
133 | cap_t caps; | |
134 | cap_value_t cap; | |
135 | int last_cap = CAP_LAST_CAP; | |
136 | char *cap_names = NULL; | |
137 | ||
4e60664a | 138 | /* When we are root, we don't want to play with capabilities. */ |
611ddd34 CB |
139 | if (!getuid()) |
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 | ||
4e60664a | 206 | /* When we are root, we don't want to play with capabilities. */ |
611ddd34 CB |
207 | if (!getuid()) |
208 | return 0; | |
209 | ||
b81689a1 CB |
210 | ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_CLEAR_ALL), |
211 | prctl_arg(0), prctl_arg(0), prctl_arg(0)); | |
611ddd34 CB |
212 | if (ret < 0) { |
213 | SYSERROR("Failed to clear ambient capability set"); | |
214 | return -1; | |
215 | } | |
216 | ||
217 | caps = cap_get_proc(); | |
218 | if (!caps) { | |
219 | SYSERROR("Failed to retrieve capabilities"); | |
220 | return -1; | |
221 | } | |
222 | ||
223 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
224 | ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR); | |
225 | if (ret < 0) { | |
226 | SYSERROR("Failed to remove capability from inheritable set"); | |
227 | goto out; | |
228 | } | |
229 | } | |
230 | ||
231 | ret = cap_set_proc(caps); | |
232 | if (ret < 0) { | |
233 | SYSERROR("Failed to set capabilities"); | |
234 | goto out; | |
235 | } | |
236 | ||
237 | out: | |
238 | cap_free(caps); | |
239 | return 0; | |
240 | } | |
241 | ||
b3357a6f DL |
242 | int lxc_caps_init(void) |
243 | { | |
4e60664a | 244 | uid_t euid, uid; |
b3357a6f | 245 | |
4e60664a CB |
246 | uid = getuid(); |
247 | if (!uid) | |
b3357a6f | 248 | return 0; |
b3357a6f | 249 | |
4e60664a | 250 | euid = geteuid(); |
b3357a6f | 251 | if (uid && !euid) { |
4e60664a CB |
252 | int ret; |
253 | gid_t gid; | |
254 | ||
255 | INFO("Command is run as setuid root (uid: %d)", uid); | |
b3357a6f | 256 | |
b81689a1 | 257 | ret = prctl(PR_SET_KEEPCAPS, prctl_arg(1)); |
4e60664a CB |
258 | if (ret < 0) { |
259 | SYSERROR("Failed to set PR_SET_KEEPCAPS"); | |
b3357a6f DL |
260 | return -1; |
261 | } | |
262 | ||
4e60664a CB |
263 | gid = getgid(); |
264 | ret = setresgid(gid, gid, gid); | |
265 | if (ret < 0) { | |
266 | SYSERROR("Failed to change rgid, egid, and sgid to %d", gid); | |
b3357a6f DL |
267 | return -1; |
268 | } | |
269 | ||
4e60664a CB |
270 | ret = setresuid(uid, uid, uid); |
271 | if (ret < 0) { | |
272 | SYSERROR("Failed to change ruid, euid, and suid to %d", uid); | |
b3357a6f DL |
273 | return -1; |
274 | } | |
275 | ||
4e60664a CB |
276 | ret = lxc_caps_up(); |
277 | if (ret < 0) { | |
6d1400b5 | 278 | SYSERROR("Failed to restore capabilities"); |
b3357a6f DL |
279 | return -1; |
280 | } | |
281 | } | |
282 | ||
283 | if (uid == euid) | |
4e60664a | 284 | INFO("Command is run with uid %d", uid); |
b3357a6f DL |
285 | |
286 | return 0; | |
287 | } | |
20d81659 | 288 | |
92d5ea57 | 289 | static long int _real_caps_last_cap(void) |
20d81659 | 290 | { |
4e60664a | 291 | int fd, result = -1; |
20d81659 | 292 | |
4e60664a CB |
293 | /* Try to get the maximum capability over the kernel interface |
294 | * introduced in v3.2. | |
295 | */ | |
296 | fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY | O_CLOEXEC); | |
20d81659 | 297 | if (fd >= 0) { |
4e60664a | 298 | ssize_t n; |
20d81659 | 299 | char *ptr; |
22b67bfa | 300 | char buf[INTTYPE_TO_STRLEN(int)] = {0}; |
20d81659 | 301 | |
81f87066 | 302 | again: |
6333c915 | 303 | n = read(fd, buf, STRARRAYLEN(buf)); |
81f87066 CB |
304 | if (n < 0 && errno == EINTR) { |
305 | goto again; | |
306 | } else if (n >= 0) { | |
4e60664a | 307 | errno = 0; |
20d81659 | 308 | result = strtol(buf, &ptr, 10); |
09bbd745 | 309 | if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) |
20d81659 CS |
310 | result = -1; |
311 | } | |
312 | ||
313 | close(fd); | |
4e60664a | 314 | } else { |
20d81659 | 315 | int cap = 0; |
6f5e532f | 316 | |
4e60664a CB |
317 | /* Try to get it manually by trying to get the status of each |
318 | * capability individually from the kernel. | |
319 | */ | |
b81689a1 | 320 | while (prctl(PR_CAPBSET_READ, prctl_arg(cap)) >= 0) |
6f5e532f | 321 | cap++; |
322 | ||
20d81659 CS |
323 | result = cap - 1; |
324 | } | |
325 | ||
326 | return result; | |
327 | } | |
328 | ||
329 | int lxc_caps_last_cap(void) | |
330 | { | |
92d5ea57 | 331 | static long int last_cap = -1; |
6f5e532f | 332 | |
92d5ea57 | 333 | if (last_cap < 0) { |
6f5e532f | 334 | last_cap = _real_caps_last_cap(); |
92d5ea57 | 335 | if (last_cap < 0 || last_cap > INT_MAX) |
336 | last_cap = -1; | |
337 | } | |
20d81659 CS |
338 | |
339 | return last_cap; | |
340 | } | |
4a2ca8b2 | 341 | |
207c4c71 | 342 | static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) |
ca364dc0 CB |
343 | { |
344 | int ret; | |
ca364dc0 CB |
345 | cap_flag_value_t flagval; |
346 | ||
207c4c71 CB |
347 | ret = cap_get_flag(caps, cap, flag, &flagval); |
348 | if (ret < 0) { | |
4e60664a | 349 | SYSERROR("Failed to retrieve current setting for capability %d", cap); |
207c4c71 CB |
350 | return false; |
351 | } | |
352 | ||
353 | return flagval == CAP_SET; | |
354 | } | |
355 | ||
356 | bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) | |
357 | { | |
4e60664a | 358 | #if LIBCAP_SUPPORTS_FILE_CAPABILITIES |
207c4c71 CB |
359 | bool cap_is_set; |
360 | cap_t caps; | |
361 | ||
362 | caps = cap_get_file(path); | |
ca364dc0 | 363 | if (!caps) { |
207c4c71 CB |
364 | /* This is undocumented in the manpage but the source code show |
365 | * that cap_get_file() may return NULL when successful for the | |
366 | * case where it didn't detect any file capabilities. In this | |
367 | * case errno will be set to ENODATA. | |
368 | */ | |
369 | if (errno != ENODATA) | |
4e60664a | 370 | SYSERROR("Failed to retrieve capabilities for file %s", path); |
6d1400b5 | 371 | |
ca364dc0 CB |
372 | return false; |
373 | } | |
374 | ||
207c4c71 CB |
375 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
376 | cap_free(caps); | |
377 | return cap_is_set; | |
4e60664a | 378 | #else |
69924fff CB |
379 | errno = ENODATA; |
380 | return false; | |
4e60664a | 381 | #endif |
207c4c71 CB |
382 | } |
383 | ||
384 | bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) | |
385 | { | |
386 | bool cap_is_set; | |
387 | cap_t caps; | |
388 | ||
389 | caps = cap_get_proc(); | |
390 | if (!caps) { | |
4e60664a | 391 | SYSERROR("Failed to retrieve capabilities"); |
ca364dc0 CB |
392 | return false; |
393 | } | |
394 | ||
207c4c71 | 395 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
ca364dc0 | 396 | cap_free(caps); |
207c4c71 | 397 | return cap_is_set; |
ca364dc0 | 398 | } |
495d2046 | 399 | #endif |