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