]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/caps.c
utils: use SYSTRACE() when logging stdio permission fixup failures
[mirror_lxc.git] / src / lxc / caps.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
b3357a6f 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
ca364dc0
CB
6#include <errno.h>
7#include <limits.h>
20d81659
CS
8#include <fcntl.h>
9#include <stdlib.h>
ca364dc0 10#include <unistd.h>
b3357a6f 11#include <sys/prctl.h>
b3357a6f 12
ca364dc0 13#include "caps.h"
d38dd64a 14#include "config.h"
89882306 15#include "file_utils.h"
b3357a6f 16#include "log.h"
4e60664a 17#include "macro.h"
1e22a683 18#include "memory_utils.h"
b3357a6f 19
ac2cecc4 20lxc_log_define(caps, lxc);
b3357a6f 21
e37dda71 22#if HAVE_LIBCAP
495d2046 23
1e22a683
CB
24define_cleanup_function(cap_t, cap_free);
25
b3357a6f
DL
26int lxc_caps_down(void)
27{
1e22a683 28 call_cleaner(cap_free) cap_t caps = NULL;
4e60664a 29 int ret = -1;
b3357a6f 30
4e60664a 31 /* When we are root, we don't want to play with capabilities. */
7ee895e4
DL
32 if (!getuid())
33 return 0;
34
b3357a6f 35 caps = cap_get_proc();
1e22a683
CB
36 if (!caps)
37 return log_error_errno(ret, errno, "Failed to retrieve capabilities");
b3357a6f
DL
38
39 ret = cap_clear_flag(caps, CAP_EFFECTIVE);
1e22a683
CB
40 if (ret)
41 return log_error_errno(ret, errno, "Failed to clear effective capabilities");
b3357a6f
DL
42
43 ret = cap_set_proc(caps);
1e22a683
CB
44 if (ret)
45 return log_error_errno(ret, errno, "Failed to change effective capabilities");
4e60664a 46
1e22a683 47 return 0;
b3357a6f
DL
48}
49
50int lxc_caps_up(void)
51{
1e22a683 52 call_cleaner(cap_free) cap_t caps = NULL;
b3357a6f 53 cap_value_t cap;
4e60664a 54 int ret = -1;
b3357a6f 55
4e60664a 56 /* When we are root, we don't want to play with capabilities. */
7ee895e4
DL
57 if (!getuid())
58 return 0;
59
b3357a6f 60 caps = cap_get_proc();
1e22a683
CB
61 if (!caps)
62 return log_error_errno(ret, errno, "Failed to retrieve capabilities");
b3357a6f
DL
63
64 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
b3357a6f
DL
65 cap_flag_value_t flag;
66
67 ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag);
68 if (ret) {
2b657f10 69 if (errno == EINVAL) {
4e60664a 70 INFO("Last supported cap was %d", cap - 1);
2b657f10
SH
71 break;
72 } else {
1e22a683 73 return log_error_errno(ret, errno, "Failed to retrieve setting for permitted capability %d", cap - 1);
2b657f10 74 }
b3357a6f
DL
75 }
76
77 ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
1e22a683
CB
78 if (ret)
79 return log_error_errno(ret, errno, "Failed to set effective capability %d", cap - 1);
b3357a6f
DL
80 }
81
82 ret = cap_set_proc(caps);
1e22a683
CB
83 if (ret)
84 return log_error_errno(ret, errno, "Failed to change effective capabilities");
4e60664a 85
1e22a683 86 return 0;
b3357a6f
DL
87}
88
611ddd34
CB
89int lxc_ambient_caps_up(void)
90{
1e22a683
CB
91 call_cleaner(cap_free) cap_t caps = NULL;
92 __do_free char *cap_names = NULL;
611ddd34 93 int ret;
611ddd34
CB
94 cap_value_t cap;
95 int last_cap = CAP_LAST_CAP;
611ddd34 96
df9bf8ca 97 if (!getuid() || geteuid())
611ddd34
CB
98 return 0;
99
100 caps = cap_get_proc();
1e22a683
CB
101 if (!caps)
102 return log_error_errno(-1, errno, "Failed to retrieve capabilities");
611ddd34
CB
103
104 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
105 cap_flag_value_t flag;
106
107 ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag);
108 if (ret < 0) {
109 if (errno == EINVAL) {
110 last_cap = (cap - 1);
111 INFO("Last supported cap was %d", last_cap);
112 break;
113 }
114
1e22a683 115 return log_error_errno(ret, errno, "Failed to retrieve capability flag");
611ddd34
CB
116 }
117
118 ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, flag);
1e22a683
CB
119 if (ret < 0)
120 return log_error_errno(ret, errno, "Failed to set capability flag");
611ddd34
CB
121 }
122
123 ret = cap_set_proc(caps);
1e22a683
CB
124 if (ret < 0)
125 return log_error_errno(ret, errno, "Failed to set capabilities");
611ddd34
CB
126
127 for (cap = 0; cap <= last_cap; cap++) {
b81689a1
CB
128 ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_RAISE),
129 prctl_arg(cap), prctl_arg(0), prctl_arg(0));
1e22a683
CB
130 if (ret < 0)
131 return log_warn_errno(ret, errno, "Failed to raise ambient capability %d", cap);
611ddd34
CB
132 }
133
134 cap_names = cap_to_text(caps, NULL);
1e22a683
CB
135 if (!cap_names)
136 return log_warn_errno(0, errno, "Failed to convert capabilities %d", cap);
611ddd34
CB
137
138 TRACE("Raised %s in inheritable and ambient capability set", cap_names);
611ddd34
CB
139 return 0;
140}
141
142int lxc_ambient_caps_down(void)
143{
1e22a683 144 call_cleaner(cap_free) cap_t caps = NULL;
611ddd34 145 int ret;
611ddd34
CB
146 cap_value_t cap;
147
df9bf8ca 148 if (!getuid() || geteuid())
611ddd34
CB
149 return 0;
150
b81689a1
CB
151 ret = prctl(PR_CAP_AMBIENT, prctl_arg(PR_CAP_AMBIENT_CLEAR_ALL),
152 prctl_arg(0), prctl_arg(0), prctl_arg(0));
1e22a683
CB
153 if (ret < 0)
154 return log_error_errno(-1, errno, "Failed to clear ambient capability set");
611ddd34
CB
155
156 caps = cap_get_proc();
1e22a683
CB
157 if (!caps)
158 return log_error_errno(-1, errno, "Failed to retrieve capabilities");
611ddd34
CB
159
160 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
161 ret = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR);
1e22a683
CB
162 if (ret < 0)
163 return log_error_errno(-1, errno, "Failed to clear capability");
611ddd34
CB
164 }
165
166 ret = cap_set_proc(caps);
1e22a683
CB
167 if (ret < 0)
168 return log_error_errno(ret, errno, "Failed to set capabilities");
611ddd34 169
611ddd34
CB
170 return 0;
171}
172
b3357a6f
DL
173int lxc_caps_init(void)
174{
4e60664a 175 uid_t euid, uid;
b3357a6f 176
4e60664a
CB
177 uid = getuid();
178 if (!uid)
b3357a6f 179 return 0;
b3357a6f 180
4e60664a 181 euid = geteuid();
b3357a6f 182 if (uid && !euid) {
4e60664a
CB
183 int ret;
184 gid_t gid;
185
186 INFO("Command is run as setuid root (uid: %d)", uid);
b3357a6f 187
b81689a1 188 ret = prctl(PR_SET_KEEPCAPS, prctl_arg(1));
1e22a683
CB
189 if (ret < 0)
190 return log_error_errno(-1, errno, "Failed to set PR_SET_KEEPCAPS");
b3357a6f 191
4e60664a
CB
192 gid = getgid();
193 ret = setresgid(gid, gid, gid);
1e22a683
CB
194 if (ret < 0)
195 return log_error_errno(-1, errno, "Failed to change rgid, egid, and sgid to %d", gid);
b3357a6f 196
4e60664a 197 ret = setresuid(uid, uid, uid);
1e22a683
CB
198 if (ret < 0)
199 return log_error_errno(-1, errno, "Failed to change ruid, euid, and suid to %d", uid);
b3357a6f 200
4e60664a 201 ret = lxc_caps_up();
1e22a683
CB
202 if (ret < 0)
203 return log_error_errno(-1, errno, "Failed to restore capabilities");
b3357a6f
DL
204 }
205
206 if (uid == euid)
4e60664a 207 INFO("Command is run with uid %d", uid);
b3357a6f
DL
208
209 return 0;
210}
20d81659 211
92d5ea57 212static long int _real_caps_last_cap(void)
20d81659 213{
4e60664a 214 int fd, result = -1;
20d81659 215
4e60664a
CB
216 /* Try to get the maximum capability over the kernel interface
217 * introduced in v3.2.
218 */
219 fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY | O_CLOEXEC);
20d81659 220 if (fd >= 0) {
4e60664a 221 ssize_t n;
20d81659 222 char *ptr;
22b67bfa 223 char buf[INTTYPE_TO_STRLEN(int)] = {0};
20d81659 224
89882306 225 n = lxc_read_nointr(fd, buf, STRARRAYLEN(buf));
226 if (n >= 0) {
4e60664a 227 errno = 0;
20d81659 228 result = strtol(buf, &ptr, 10);
09bbd745 229 if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0)
20d81659
CS
230 result = -1;
231 }
232
233 close(fd);
4e60664a 234 } else {
20d81659 235 int cap = 0;
6f5e532f 236
4e60664a
CB
237 /* Try to get it manually by trying to get the status of each
238 * capability individually from the kernel.
239 */
b81689a1 240 while (prctl(PR_CAPBSET_READ, prctl_arg(cap)) >= 0)
6f5e532f 241 cap++;
242
20d81659
CS
243 result = cap - 1;
244 }
245
246 return result;
247}
248
249int lxc_caps_last_cap(void)
250{
92d5ea57 251 static long int last_cap = -1;
6f5e532f 252
92d5ea57 253 if (last_cap < 0) {
6f5e532f 254 last_cap = _real_caps_last_cap();
92d5ea57 255 if (last_cap < 0 || last_cap > INT_MAX)
256 last_cap = -1;
257 }
20d81659
CS
258
259 return last_cap;
260}
4a2ca8b2 261
207c4c71 262static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag)
ca364dc0
CB
263{
264 int ret;
ca364dc0
CB
265 cap_flag_value_t flagval;
266
207c4c71 267 ret = cap_get_flag(caps, cap, flag, &flagval);
1e22a683
CB
268 if (ret < 0)
269 return log_error_errno(false, errno, "Failed to retrieve current setting for capability %d", cap);
207c4c71
CB
270
271 return flagval == CAP_SET;
272}
273
274bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag)
275{
4e60664a 276#if LIBCAP_SUPPORTS_FILE_CAPABILITIES
1e22a683 277 call_cleaner(cap_free) cap_t caps = NULL;
207c4c71
CB
278
279 caps = cap_get_file(path);
ca364dc0 280 if (!caps) {
207c4c71
CB
281 /* This is undocumented in the manpage but the source code show
282 * that cap_get_file() may return NULL when successful for the
283 * case where it didn't detect any file capabilities. In this
284 * case errno will be set to ENODATA.
285 */
286 if (errno != ENODATA)
4e60664a 287 SYSERROR("Failed to retrieve capabilities for file %s", path);
6d1400b5 288
ca364dc0
CB
289 return false;
290 }
291
1e22a683 292 return lxc_cap_is_set(caps, cap, flag);
4e60664a 293#else
69924fff
CB
294 errno = ENODATA;
295 return false;
4e60664a 296#endif
207c4c71
CB
297}
298
299bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag)
300{
1e22a683 301 call_cleaner(cap_free) cap_t caps = NULL;
207c4c71
CB
302
303 caps = cap_get_proc();
1e22a683
CB
304 if (!caps)
305 return log_error_errno(false, errno, "Failed to retrieve capabilities");
ca364dc0 306
1e22a683 307 return lxc_cap_is_set(caps, cap, flag);
ca364dc0 308}
495d2046 309#endif