]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/caps.c
start: check event loop type before closing fd
[mirror_lxc.git] / src / lxc / caps.c
CommitLineData
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 18lxc_log_define(caps, lxc);
b3357a6f 19
e37dda71 20#if HAVE_LIBCAP
495d2046 21
1e22a683
CB
22define_cleanup_function(cap_t, cap_free);
23
b3357a6f
DL
24int 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
48int 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
87int 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
140int 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
171int 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 210static 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 259int 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 277static 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
289bool 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
314bool 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