]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
d7832d2c | 2 | |
d7832d2c KS |
3 | #include <errno.h> |
4 | #include <stdio.h> | |
11c3a366 | 5 | #include <stdlib.h> |
d7832d2c | 6 | #include <sys/prctl.h> |
3ffd4af2 | 7 | #include <unistd.h> |
d7832d2c | 8 | |
b5efdb8a | 9 | #include "alloc-util.h" |
430f0182 | 10 | #include "capability-util.h" |
f66ad460 | 11 | #include "cap-list.h" |
3ffd4af2 LP |
12 | #include "fileio.h" |
13 | #include "log.h" | |
d7832d2c | 14 | #include "macro.h" |
a22692d7 | 15 | #include "missing_prctl.h" |
6bedfcbb | 16 | #include "parse-util.h" |
36d85478 | 17 | #include "user-util.h" |
d7832d2c | 18 | #include "util.h" |
d7832d2c KS |
19 | |
20 | int have_effective_cap(int value) { | |
a779cf30 | 21 | _cleanup_cap_free_ cap_t cap = NULL; |
d7832d2c | 22 | cap_flag_value_t fv; |
d7832d2c | 23 | |
ec8927ca LP |
24 | cap = cap_get_proc(); |
25 | if (!cap) | |
d7832d2c KS |
26 | return -errno; |
27 | ||
28 | if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) | |
5ce70e5b | 29 | return -errno; |
15ce3f26 LP |
30 | |
31 | return fv == CAP_SET; | |
d7832d2c KS |
32 | } |
33 | ||
864a25d9 ZJS |
34 | unsigned cap_last_cap(void) { |
35 | static thread_local unsigned saved; | |
ec202eae | 36 | static thread_local bool valid = false; |
80b43783 | 37 | _cleanup_free_ char *content = NULL; |
a7f7d1bd | 38 | unsigned long p = 0; |
80b43783 | 39 | int r; |
d7832d2c KS |
40 | |
41 | if (valid) | |
42 | return saved; | |
43 | ||
80b43783 DH |
44 | /* available since linux-3.2 */ |
45 | r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); | |
46 | if (r >= 0) { | |
47 | r = safe_atolu(content, &p); | |
48 | if (r >= 0) { | |
5211445e LP |
49 | |
50 | if (p > 63) /* Safety for the future: if one day the kernel learns more than 64 caps, | |
51 | * then we are in trouble (since we, as much userspace and kernel space | |
d09a2585 | 52 | * store capability masks in uint64_t types). Let's hence protect |
5211445e LP |
53 | * ourselves against that and always cap at 63 for now. */ |
54 | p = 63; | |
55 | ||
80b43783 DH |
56 | saved = p; |
57 | valid = true; | |
58 | return p; | |
59 | } | |
60 | } | |
61 | ||
62 | /* fall back to syscall-probing for pre linux-3.2 */ | |
46eda043 | 63 | p = MIN((unsigned long) CAP_LAST_CAP, 63U); |
d7832d2c KS |
64 | |
65 | if (prctl(PR_CAPBSET_READ, p) < 0) { | |
66 | ||
5211445e | 67 | /* Hmm, look downwards, until we find one that works */ |
864a25d9 | 68 | for (p--; p > 0; p--) |
d7832d2c KS |
69 | if (prctl(PR_CAPBSET_READ, p) >= 0) |
70 | break; | |
71 | ||
72 | } else { | |
73 | ||
5211445e LP |
74 | /* Hmm, look upwards, until we find one that doesn't work */ |
75 | for (; p < 63; p++) | |
d7832d2c KS |
76 | if (prctl(PR_CAPBSET_READ, p+1) < 0) |
77 | break; | |
78 | } | |
79 | ||
80 | saved = p; | |
81 | valid = true; | |
82 | ||
83 | return p; | |
84 | } | |
ec8927ca | 85 | |
755d4b67 | 86 | int capability_update_inherited_set(cap_t caps, uint64_t set) { |
82d832b4 KK |
87 | /* Add capabilities in the set to the inherited caps, drops capabilities not in the set. |
88 | * Do not apply them yet. */ | |
755d4b67 | 89 | |
864a25d9 | 90 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
82d832b4 KK |
91 | cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR; |
92 | cap_value_t v; | |
755d4b67 | 93 | |
82d832b4 | 94 | v = (cap_value_t) i; |
755d4b67 | 95 | |
82d832b4 KK |
96 | if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, flag) < 0) |
97 | return -errno; | |
755d4b67 IP |
98 | } |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | int capability_ambient_set_apply(uint64_t set, bool also_inherit) { | |
755d4b67 | 104 | _cleanup_cap_free_ cap_t caps = NULL; |
15ce3f26 | 105 | int r; |
755d4b67 | 106 | |
8acb11a6 | 107 | /* Remove capabilities requested in ambient set, but not in the bounding set */ |
864a25d9 | 108 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
8acb11a6 | 109 | if (set == 0) |
110 | break; | |
111 | ||
112 | if (FLAGS_SET(set, (UINT64_C(1) << i)) && prctl(PR_CAPBSET_READ, i) != 1) { | |
113 | log_debug("Ambient capability %s requested but missing from bounding set," | |
114 | " suppressing automatically.", capability_to_name(i)); | |
115 | set &= ~(UINT64_C(1) << i); | |
116 | } | |
117 | } | |
118 | ||
c4b23712 LP |
119 | /* Add the capabilities to the ambient set (an possibly also the inheritable set) */ |
120 | ||
7ea4392f KK |
121 | /* Check that we can use PR_CAP_AMBIENT or quit early. */ |
122 | if (!ambient_capabilities_supported()) | |
c4b23712 LP |
123 | return (set & all_capabilities()) == 0 ? |
124 | 0 : -EOPNOTSUPP; /* if actually no ambient caps are to be set, be silent, | |
125 | * otherwise fail recognizably */ | |
755d4b67 IP |
126 | |
127 | if (also_inherit) { | |
755d4b67 IP |
128 | caps = cap_get_proc(); |
129 | if (!caps) | |
130 | return -errno; | |
131 | ||
132 | r = capability_update_inherited_set(caps, set); | |
133 | if (r < 0) | |
134 | return -errno; | |
135 | ||
136 | if (cap_set_proc(caps) < 0) | |
137 | return -errno; | |
138 | } | |
139 | ||
864a25d9 | 140 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
755d4b67 IP |
141 | |
142 | if (set & (UINT64_C(1) << i)) { | |
143 | ||
144 | /* Add the capability to the ambient set. */ | |
145 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) | |
146 | return -errno; | |
82d832b4 KK |
147 | } else { |
148 | ||
149 | /* Drop the capability so we don't inherit capabilities we didn't ask for. */ | |
150 | r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); | |
151 | if (r < 0) | |
152 | return -errno; | |
153 | ||
154 | if (r) | |
155 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0) | |
156 | return -errno; | |
157 | ||
755d4b67 IP |
158 | } |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
57d4d284 TK |
164 | int capability_gain_cap_setpcap(cap_t *ret_before_caps) { |
165 | _cleanup_cap_free_ cap_t caps = NULL; | |
ec8927ca | 166 | cap_flag_value_t fv; |
57d4d284 TK |
167 | caps = cap_get_proc(); |
168 | if (!caps) | |
ec8927ca LP |
169 | return -errno; |
170 | ||
57d4d284 | 171 | if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) |
ec8927ca | 172 | return -errno; |
ec8927ca LP |
173 | |
174 | if (fv != CAP_SET) { | |
6a010ac9 | 175 | _cleanup_cap_free_ cap_t temp_cap = NULL; |
ec8927ca LP |
176 | static const cap_value_t v = CAP_SETPCAP; |
177 | ||
57d4d284 | 178 | temp_cap = cap_dup(caps); |
6067611a LP |
179 | if (!temp_cap) |
180 | return -errno; | |
ec8927ca | 181 | |
6067611a LP |
182 | if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) |
183 | return -errno; | |
ec8927ca | 184 | |
6067611a LP |
185 | if (cap_set_proc(temp_cap) < 0) |
186 | log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); | |
187 | ||
188 | /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means | |
57d4d284 | 189 | * we'll fail later, when we actually intend to drop some capabilities or try to set securebits. */ |
ec8927ca | 190 | } |
57d4d284 TK |
191 | if (ret_before_caps) |
192 | /* Return the capabilities as they have been before setting CAP_SETPCAP */ | |
193 | *ret_before_caps = TAKE_PTR(caps); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | int capability_bounding_set_drop(uint64_t keep, bool right_now) { | |
199 | _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; | |
200 | int r; | |
201 | ||
202 | /* If we are run as PID 1 we will lack CAP_SETPCAP by default | |
203 | * in the effective set (yes, the kernel drops that when | |
204 | * executing init!), so get it back temporarily so that we can | |
205 | * call PR_CAPBSET_DROP. */ | |
206 | ||
207 | r = capability_gain_cap_setpcap(&before_cap); | |
208 | if (r < 0) | |
209 | return r; | |
ec8927ca | 210 | |
6067611a LP |
211 | after_cap = cap_dup(before_cap); |
212 | if (!after_cap) | |
213 | return -errno; | |
214 | ||
864a25d9 | 215 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
6067611a | 216 | cap_value_t v; |
ec8927ca | 217 | |
6067611a LP |
218 | if ((keep & (UINT64_C(1) << i))) |
219 | continue; | |
ec8927ca | 220 | |
6067611a LP |
221 | /* Drop it from the bounding set */ |
222 | if (prctl(PR_CAPBSET_DROP, i) < 0) { | |
223 | r = -errno; | |
224 | ||
225 | /* If dropping the capability failed, let's see if we didn't have it in the first place. If so, | |
226 | * continue anyway, as dropping a capability we didn't have in the first place doesn't really | |
227 | * matter anyway. */ | |
228 | if (prctl(PR_CAPBSET_READ, i) != 0) | |
ec8927ca | 229 | goto finish; |
6067611a LP |
230 | } |
231 | v = (cap_value_t) i; | |
232 | ||
233 | /* Also drop it from the inheritable set, so | |
234 | * that anything we exec() loses the | |
235 | * capability for good. */ | |
236 | if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { | |
237 | r = -errno; | |
238 | goto finish; | |
239 | } | |
ec8927ca | 240 | |
6067611a LP |
241 | /* If we shall apply this right now drop it |
242 | * also from our own capability sets. */ | |
243 | if (right_now) { | |
244 | if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || | |
245 | cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { | |
ec8927ca LP |
246 | r = -errno; |
247 | goto finish; | |
248 | } | |
ec8927ca LP |
249 | } |
250 | } | |
251 | ||
252 | r = 0; | |
253 | ||
254 | finish: | |
6067611a LP |
255 | if (cap_set_proc(after_cap) < 0) { |
256 | /* If there are no actual changes anyway then let's ignore this error. */ | |
257 | if (cap_compare(before_cap, after_cap) != 0) | |
258 | r = -errno; | |
259 | } | |
ec8927ca LP |
260 | |
261 | return r; | |
262 | } | |
939b8f14 | 263 | |
a103496c | 264 | static int drop_from_file(const char *fn, uint64_t keep) { |
3130fca5 | 265 | _cleanup_free_ char *p = NULL; |
939b8f14 | 266 | uint64_t current, after; |
3130fca5 LP |
267 | uint32_t hi, lo; |
268 | int r, k; | |
939b8f14 LP |
269 | |
270 | r = read_one_line_file(fn, &p); | |
271 | if (r < 0) | |
272 | return r; | |
273 | ||
7345109e | 274 | k = sscanf(p, "%" PRIu32 " %" PRIu32, &lo, &hi); |
939b8f14 LP |
275 | if (k != 2) |
276 | return -EIO; | |
277 | ||
7345109e | 278 | current = (uint64_t) lo | ((uint64_t) hi << 32); |
a103496c | 279 | after = current & keep; |
939b8f14 LP |
280 | |
281 | if (current == after) | |
282 | return 0; | |
283 | ||
7345109e LP |
284 | lo = after & UINT32_C(0xFFFFFFFF); |
285 | hi = (after >> 32) & UINT32_C(0xFFFFFFFF); | |
939b8f14 | 286 | |
7345109e | 287 | return write_string_filef(fn, 0, "%" PRIu32 " %" PRIu32, lo, hi); |
939b8f14 LP |
288 | } |
289 | ||
a103496c | 290 | int capability_bounding_set_drop_usermode(uint64_t keep) { |
939b8f14 LP |
291 | int r; |
292 | ||
a103496c | 293 | r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); |
939b8f14 LP |
294 | if (r < 0) |
295 | return r; | |
296 | ||
a103496c | 297 | r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); |
939b8f14 LP |
298 | if (r < 0) |
299 | return r; | |
300 | ||
301 | return r; | |
302 | } | |
966bff26 | 303 | |
ed617ec2 | 304 | int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { |
966bff26 LP |
305 | int r; |
306 | ||
bff74941 LP |
307 | /* Unfortunately we cannot leave privilege dropping to PID 1 here, since we want to run as user but |
308 | * want to keep some capabilities. Since file capabilities have been introduced this cannot be done | |
309 | * across exec() anymore, unless our binary has the capability configured in the file system, which | |
310 | * we want to avoid. */ | |
966bff26 | 311 | |
4a62c710 MS |
312 | if (setresgid(gid, gid, gid) < 0) |
313 | return log_error_errno(errno, "Failed to change group ID: %m"); | |
966bff26 | 314 | |
97f0e76f LP |
315 | r = maybe_setgroups(0, NULL); |
316 | if (r < 0) | |
317 | return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); | |
966bff26 | 318 | |
bff74941 LP |
319 | /* Ensure we keep the permitted caps across the setresuid(). Note that we do this even if we actually |
320 | * don't want to keep any capabilities, since we want to be able to drop them from the bounding set | |
321 | * too, and we can only do that if we have capabilities. */ | |
51ddf615 | 322 | if (prctl(PR_SET_KEEPCAPS, 1) < 0) |
4a62c710 | 323 | return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); |
966bff26 | 324 | |
b1c05b98 | 325 | if (setresuid(uid, uid, uid) < 0) |
4a62c710 | 326 | return log_error_errno(errno, "Failed to change user ID: %m"); |
966bff26 | 327 | |
4a62c710 MS |
328 | if (prctl(PR_SET_KEEPCAPS, 0) < 0) |
329 | return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); | |
966bff26 | 330 | |
bff74941 LP |
331 | /* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except |
332 | * the ones we want to keep */ | |
a103496c | 333 | r = capability_bounding_set_drop(keep_capabilities, true); |
f647962d MS |
334 | if (r < 0) |
335 | return log_error_errno(r, "Failed to drop capabilities: %m"); | |
966bff26 | 336 | |
f11943c5 | 337 | /* Now upgrade the permitted caps we still kept to effective caps */ |
bff74941 | 338 | if (keep_capabilities != 0) { |
057255fb | 339 | cap_value_t bits[u64log2(keep_capabilities) + 1]; |
bff74941 LP |
340 | _cleanup_cap_free_ cap_t d = NULL; |
341 | unsigned i, j = 0; | |
342 | ||
343 | d = cap_init(); | |
344 | if (!d) | |
345 | return log_oom(); | |
6a010ac9 | 346 | |
7d328b54 | 347 | for (i = 0; i < ELEMENTSOF(bits); i++) |
51ddf615 ZJS |
348 | if (keep_capabilities & (1ULL << i)) |
349 | bits[j++] = i; | |
057255fb | 350 | |
2c9fc266 ZJS |
351 | /* use enough bits */ |
352 | assert(i == 64 || (keep_capabilities >> i) == 0); | |
353 | /* don't use too many bits */ | |
246bb370 | 354 | assert(keep_capabilities & (UINT64_C(1) << (i - 1))); |
966bff26 | 355 | |
51ddf615 | 356 | if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || |
e1427b13 MS |
357 | cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) |
358 | return log_error_errno(errno, "Failed to enable capabilities bits: %m"); | |
51ddf615 ZJS |
359 | |
360 | if (cap_set_proc(d) < 0) | |
361 | return log_error_errno(errno, "Failed to increase capabilities: %m"); | |
362 | } | |
966bff26 LP |
363 | |
364 | return 0; | |
365 | } | |
dd5ae4c3 PK |
366 | |
367 | int drop_capability(cap_value_t cv) { | |
368 | _cleanup_cap_free_ cap_t tmp_cap = NULL; | |
369 | ||
370 | tmp_cap = cap_get_proc(); | |
371 | if (!tmp_cap) | |
372 | return -errno; | |
373 | ||
374 | if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || | |
375 | (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || | |
376 | (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) | |
377 | return -errno; | |
378 | ||
379 | if (cap_set_proc(tmp_cap) < 0) | |
380 | return -errno; | |
381 | ||
382 | return 0; | |
383 | } | |
39f608e4 LP |
384 | |
385 | bool ambient_capabilities_supported(void) { | |
386 | static int cache = -1; | |
387 | ||
388 | if (cache >= 0) | |
389 | return cache; | |
390 | ||
391 | /* If PR_CAP_AMBIENT returns something valid, or an unexpected error code we assume that ambient caps are | |
392 | * available. */ | |
393 | ||
394 | cache = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_KILL, 0, 0) >= 0 || | |
395 | !IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS); | |
396 | ||
397 | return cache; | |
398 | } | |
d7391698 | 399 | |
f66ad460 | 400 | bool capability_quintet_mangle(CapabilityQuintet *q) { |
f66ad460 AZ |
401 | uint64_t combined, drop = 0; |
402 | bool ambient_supported; | |
403 | ||
404 | assert(q); | |
405 | ||
406 | combined = q->effective | q->bounding | q->inheritable | q->permitted; | |
407 | ||
f5fbe71d | 408 | ambient_supported = q->ambient != UINT64_MAX; |
f66ad460 AZ |
409 | if (ambient_supported) |
410 | combined |= q->ambient; | |
411 | ||
864a25d9 | 412 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
f66ad460 AZ |
413 | unsigned long bit = UINT64_C(1) << i; |
414 | if (!FLAGS_SET(combined, bit)) | |
415 | continue; | |
416 | ||
417 | if (prctl(PR_CAPBSET_READ, i) > 0) | |
418 | continue; | |
419 | ||
420 | drop |= bit; | |
421 | ||
422 | log_debug("Not in the current bounding set: %s", capability_to_name(i)); | |
423 | } | |
424 | ||
425 | q->effective &= ~drop; | |
426 | q->bounding &= ~drop; | |
427 | q->inheritable &= ~drop; | |
428 | q->permitted &= ~drop; | |
429 | ||
430 | if (ambient_supported) | |
431 | q->ambient &= ~drop; | |
432 | ||
433 | return drop != 0; /* Let the caller know we changed something */ | |
434 | } | |
435 | ||
d7391698 | 436 | int capability_quintet_enforce(const CapabilityQuintet *q) { |
6757a013 | 437 | _cleanup_cap_free_ cap_t c = NULL, modified = NULL; |
d7391698 LP |
438 | int r; |
439 | ||
f5fbe71d | 440 | if (q->ambient != UINT64_MAX) { |
d7391698 LP |
441 | bool changed = false; |
442 | ||
443 | c = cap_get_proc(); | |
444 | if (!c) | |
445 | return -errno; | |
446 | ||
864a25d9 ZJS |
447 | /* In order to raise the ambient caps set we first need to raise the matching |
448 | * inheritable + permitted cap */ | |
449 | for (unsigned i = 0; i <= cap_last_cap(); i++) { | |
d7391698 LP |
450 | uint64_t m = UINT64_C(1) << i; |
451 | cap_value_t cv = (cap_value_t) i; | |
452 | cap_flag_value_t old_value_inheritable, old_value_permitted; | |
453 | ||
454 | if ((q->ambient & m) == 0) | |
455 | continue; | |
456 | ||
457 | if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value_inheritable) < 0) | |
458 | return -errno; | |
459 | if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value_permitted) < 0) | |
460 | return -errno; | |
461 | ||
462 | if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET) | |
463 | continue; | |
464 | ||
465 | if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0) | |
466 | return -errno; | |
d7391698 LP |
467 | if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0) |
468 | return -errno; | |
469 | ||
470 | changed = true; | |
471 | } | |
472 | ||
473 | if (changed) | |
474 | if (cap_set_proc(c) < 0) | |
475 | return -errno; | |
476 | ||
477 | r = capability_ambient_set_apply(q->ambient, false); | |
478 | if (r < 0) | |
479 | return r; | |
480 | } | |
481 | ||
f5fbe71d | 482 | if (q->inheritable != UINT64_MAX || q->permitted != UINT64_MAX || q->effective != UINT64_MAX) { |
d7391698 | 483 | bool changed = false; |
d7391698 LP |
484 | |
485 | if (!c) { | |
486 | c = cap_get_proc(); | |
487 | if (!c) | |
488 | return -errno; | |
489 | } | |
490 | ||
864a25d9 | 491 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
d7391698 LP |
492 | uint64_t m = UINT64_C(1) << i; |
493 | cap_value_t cv = (cap_value_t) i; | |
494 | ||
f5fbe71d | 495 | if (q->inheritable != UINT64_MAX) { |
d7391698 LP |
496 | cap_flag_value_t old_value, new_value; |
497 | ||
248dd941 LP |
498 | if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0) { |
499 | if (errno == EINVAL) /* If the kernel knows more caps than this | |
500 | * version of libcap, then this will return | |
501 | * EINVAL. In that case, simply ignore it, | |
502 | * pretend it doesn't exist. */ | |
503 | continue; | |
504 | ||
d7391698 | 505 | return -errno; |
248dd941 | 506 | } |
d7391698 LP |
507 | |
508 | new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR; | |
509 | ||
510 | if (old_value != new_value) { | |
511 | changed = true; | |
512 | ||
513 | if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0) | |
514 | return -errno; | |
515 | } | |
516 | } | |
517 | ||
f5fbe71d | 518 | if (q->permitted != UINT64_MAX) { |
d7391698 LP |
519 | cap_flag_value_t old_value, new_value; |
520 | ||
248dd941 LP |
521 | if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0) { |
522 | if (errno == EINVAL) | |
523 | continue; | |
524 | ||
d7391698 | 525 | return -errno; |
248dd941 | 526 | } |
d7391698 LP |
527 | |
528 | new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR; | |
529 | ||
530 | if (old_value != new_value) { | |
531 | changed = true; | |
532 | ||
533 | if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0) | |
534 | return -errno; | |
535 | } | |
536 | } | |
537 | ||
f5fbe71d | 538 | if (q->effective != UINT64_MAX) { |
d7391698 LP |
539 | cap_flag_value_t old_value, new_value; |
540 | ||
248dd941 LP |
541 | if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0) { |
542 | if (errno == EINVAL) | |
543 | continue; | |
544 | ||
d7391698 | 545 | return -errno; |
248dd941 | 546 | } |
d7391698 LP |
547 | |
548 | new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR; | |
549 | ||
550 | if (old_value != new_value) { | |
551 | changed = true; | |
552 | ||
553 | if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0) | |
554 | return -errno; | |
555 | } | |
556 | } | |
557 | } | |
558 | ||
9a2c5911 | 559 | if (changed) { |
9a2c5911 LP |
560 | /* In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit |
561 | * longer. Let's add it to our list hence for now. */ | |
f5fbe71d | 562 | if (q->bounding != UINT64_MAX) { |
9a2c5911 LP |
563 | cap_value_t cv = CAP_SETPCAP; |
564 | ||
565 | modified = cap_dup(c); | |
566 | if (!modified) | |
567 | return -ENOMEM; | |
568 | ||
569 | if (cap_set_flag(modified, CAP_PERMITTED, 1, &cv, CAP_SET) < 0) | |
570 | return -errno; | |
571 | if (cap_set_flag(modified, CAP_EFFECTIVE, 1, &cv, CAP_SET) < 0) | |
572 | return -errno; | |
573 | ||
574 | if (cap_compare(modified, c) == 0) { | |
575 | /* No change? then drop this nonsense again */ | |
576 | cap_free(modified); | |
577 | modified = NULL; | |
578 | } | |
579 | } | |
580 | ||
581 | /* Now, let's enforce the caps for the first time. Note that this is where we acquire | |
582 | * caps in any of the sets we currently don't have. We have to do this before | |
664ff517 LP |
583 | * dropping the bounding caps below, since at that point we can never acquire new |
584 | * caps in inherited/permitted/effective anymore, but only lose them. */ | |
9a2c5911 | 585 | if (cap_set_proc(modified ?: c) < 0) |
d7391698 | 586 | return -errno; |
9a2c5911 | 587 | } |
d7391698 LP |
588 | } |
589 | ||
f5fbe71d | 590 | if (q->bounding != UINT64_MAX) { |
d7391698 LP |
591 | r = capability_bounding_set_drop(q->bounding, false); |
592 | if (r < 0) | |
593 | return r; | |
594 | } | |
595 | ||
9a2c5911 LP |
596 | /* If needed, let's now set the caps again, this time in the final version, which differs from what |
597 | * we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding | |
598 | * bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't | |
599 | * matter. */ | |
6757a013 | 600 | if (modified) |
9a2c5911 LP |
601 | if (cap_set_proc(c) < 0) |
602 | return -errno; | |
603 | ||
d7391698 LP |
604 | return 0; |
605 | } |