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