/src/systemd/src/basic/capability-util.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <stdatomic.h> |
4 | | #include <stdio.h> |
5 | | #include <sys/prctl.h> /* IWYU pragma: keep */ |
6 | | #include <sys/syscall.h> |
7 | | #include <unistd.h> |
8 | | |
9 | | #include "alloc-util.h" |
10 | | #include "bitfield.h" |
11 | | #include "capability-list.h" |
12 | | #include "capability-util.h" |
13 | | #include "errno-util.h" |
14 | | #include "fd-util.h" |
15 | | #include "fileio.h" |
16 | | #include "log.h" |
17 | | #include "parse-util.h" |
18 | | #include "pidref.h" |
19 | | #include "process-util.h" |
20 | | #include "stat-util.h" |
21 | | #include "user-util.h" |
22 | | |
23 | 0 | int capability_get(CapabilityQuintet *ret) { |
24 | 0 | assert(ret); |
25 | |
|
26 | 0 | struct __user_cap_header_struct hdr = { |
27 | 0 | .version = _LINUX_CAPABILITY_VERSION_3, |
28 | 0 | .pid = getpid_cached(), |
29 | 0 | }; |
30 | |
|
31 | 0 | assert_cc(_LINUX_CAPABILITY_U32S_3 == 2); |
32 | 0 | struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3]; |
33 | 0 | if (syscall(SYS_capget, &hdr, data) < 0) |
34 | 0 | return -errno; |
35 | | |
36 | 0 | *ret = (CapabilityQuintet) { |
37 | 0 | .effective = (uint64_t) data[0].effective | ((uint64_t) data[1].effective << 32), |
38 | 0 | .bounding = UINT64_MAX, |
39 | 0 | .inheritable = (uint64_t) data[0].inheritable | ((uint64_t) data[1].inheritable << 32), |
40 | 0 | .permitted = (uint64_t) data[0].permitted | ((uint64_t) data[1].permitted << 32), |
41 | 0 | .ambient = UINT64_MAX, |
42 | 0 | }; |
43 | 0 | return 0; |
44 | 0 | } |
45 | | |
46 | 0 | static int capability_apply(const CapabilityQuintet *q) { |
47 | 0 | assert(q); |
48 | |
|
49 | 0 | struct __user_cap_header_struct hdr = { |
50 | 0 | .version = _LINUX_CAPABILITY_VERSION_3, |
51 | 0 | .pid = getpid_cached(), |
52 | 0 | }; |
53 | |
|
54 | 0 | struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3] = { |
55 | 0 | { |
56 | 0 | .effective = (uint32_t) (q->effective & UINT32_MAX), |
57 | 0 | .inheritable = (uint32_t) (q->inheritable & UINT32_MAX), |
58 | 0 | .permitted = (uint32_t) (q->permitted & UINT32_MAX), |
59 | 0 | }, |
60 | 0 | { |
61 | 0 | .effective = (uint32_t) (q->effective >> 32), |
62 | 0 | .inheritable = (uint32_t) (q->inheritable >> 32), |
63 | 0 | .permitted = (uint32_t) (q->permitted >> 32), |
64 | 0 | }, |
65 | 0 | }; |
66 | 0 | return RET_NERRNO(syscall(SYS_capset, &hdr, data)); |
67 | 0 | } |
68 | | |
69 | 872 | unsigned cap_last_cap(void) { |
70 | 872 | static atomic_int saved = INT_MAX; |
71 | 872 | int r, c; |
72 | | |
73 | 872 | c = saved; |
74 | 872 | if (c != INT_MAX) |
75 | 871 | return c; |
76 | | |
77 | | /* Available since linux-3.2 */ |
78 | 1 | _cleanup_free_ char *content = NULL; |
79 | 1 | r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); |
80 | 1 | if (r < 0) |
81 | 1 | log_debug_errno(r, "Failed to read /proc/sys/kernel/cap_last_cap, ignoring: %m"); |
82 | 1 | else { |
83 | 1 | r = safe_atoi(content, &c); |
84 | 1 | if (r < 0) |
85 | 1 | log_debug_errno(r, "Failed to parse /proc/sys/kernel/cap_last_cap, ignoring: %m"); |
86 | 1 | else { |
87 | 1 | if (c > CAP_LIMIT) /* Safety for the future: if one day the kernel learns more than |
88 | | * 64 caps, then we are in trouble (since we, as much userspace |
89 | | * and kernel space store capability masks in uint64_t types). We |
90 | | * also want to use UINT64_MAX as marker for "unset". Hence let's |
91 | | * hence protect ourselves against that and always cap at 62 for |
92 | | * now. */ |
93 | 0 | c = CAP_LIMIT; |
94 | | |
95 | 1 | saved = c; |
96 | 1 | return c; |
97 | 1 | } |
98 | 1 | } |
99 | | |
100 | | /* Fall back to syscall-probing for pre linux-3.2, or where /proc/ is not mounted */ |
101 | 0 | unsigned long p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT); |
102 | |
|
103 | 0 | if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) < 0) { |
104 | | |
105 | | /* Hmm, look downwards, until we find one that works */ |
106 | 0 | for (p--; p > 0; p--) |
107 | 0 | if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) >= 0) |
108 | 0 | break; |
109 | |
|
110 | 0 | } else { |
111 | | |
112 | | /* Hmm, look upwards, until we find one that doesn't work */ |
113 | 0 | for (; p < CAP_LIMIT; p++) |
114 | 0 | if (prctl_safe(PR_CAPBSET_READ, p+1, 0, 0, 0) < 0) |
115 | 0 | break; |
116 | 0 | } |
117 | |
|
118 | 0 | c = (int) p; |
119 | 0 | saved = c; |
120 | 0 | return c; |
121 | 1 | } |
122 | | |
123 | 0 | int have_effective_cap(unsigned cap) { |
124 | 0 | CapabilityQuintet q; |
125 | 0 | int r; |
126 | |
|
127 | 0 | assert(cap <= CAP_LIMIT); |
128 | |
|
129 | 0 | r = capability_get(&q); |
130 | 0 | if (r < 0) |
131 | 0 | return r; |
132 | | |
133 | 0 | return BIT_SET(q.effective, cap); |
134 | 0 | } |
135 | | |
136 | 0 | int have_inheritable_cap(unsigned cap) { |
137 | 0 | CapabilityQuintet q; |
138 | 0 | int r; |
139 | |
|
140 | 0 | assert(cap <= CAP_LIMIT); |
141 | |
|
142 | 0 | r = capability_get(&q); |
143 | 0 | if (r < 0) |
144 | 0 | return r; |
145 | | |
146 | 0 | return BIT_SET(q.inheritable, cap); |
147 | 0 | } |
148 | | |
149 | 0 | int capability_ambient_set_apply(uint64_t set, bool also_inherit) { |
150 | 0 | int r; |
151 | | |
152 | | /* Remove capabilities requested in ambient set, but not in the bounding set */ |
153 | 0 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
154 | 0 | if (!BIT_SET(set, i)) |
155 | 0 | continue; |
156 | | |
157 | 0 | r = prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0); |
158 | 0 | if (r < 0) |
159 | 0 | return r; |
160 | 0 | if (r != 1) { |
161 | 0 | log_debug("Ambient capability %s requested but missing from bounding set, suppressing automatically.", |
162 | 0 | capability_to_name(i)); |
163 | 0 | CLEAR_BIT(set, i); |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | /* Add the capabilities to the ambient set (an possibly also the inheritable set) */ |
168 | | |
169 | 0 | if (also_inherit) { |
170 | 0 | CapabilityQuintet q; |
171 | |
|
172 | 0 | r = capability_get(&q); |
173 | 0 | if (r < 0) |
174 | 0 | return r; |
175 | | |
176 | 0 | q.inheritable = set; |
177 | |
|
178 | 0 | r = capability_apply(&q); |
179 | 0 | if (r < 0) |
180 | 0 | return r; |
181 | 0 | } |
182 | | |
183 | 0 | for (unsigned i = 0; i <= cap_last_cap(); i++) |
184 | 0 | if (BIT_SET(set, i)) { |
185 | | /* Add the capability to the ambient set. */ |
186 | 0 | r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0); |
187 | 0 | if (r < 0) |
188 | 0 | return r; |
189 | 0 | } else { |
190 | | /* Drop the capability so we don't inherit capabilities we didn't ask for. */ |
191 | 0 | r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); |
192 | 0 | if (r < 0) |
193 | 0 | return r; |
194 | 0 | if (r > 0) { |
195 | 0 | r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0); |
196 | 0 | if (r < 0) |
197 | 0 | return r; |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | 0 | return 0; |
202 | 0 | } |
203 | | |
204 | 0 | int capability_gain_cap_setpcap(void) { |
205 | 0 | CapabilityQuintet q; |
206 | 0 | int r; |
207 | |
|
208 | 0 | r = capability_get(&q); |
209 | 0 | if (r < 0) |
210 | 0 | return r; |
211 | | |
212 | 0 | if (BIT_SET(q.effective, CAP_SETPCAP)) |
213 | 0 | return 1; /* We already have capability. */ |
214 | | |
215 | 0 | SET_BIT(q.effective, CAP_SETPCAP); |
216 | |
|
217 | 0 | r = capability_apply(&q); |
218 | 0 | if (r < 0) { |
219 | | /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this |
220 | | * just means we'll fail later, when we actually intend to drop some capabilities or try to |
221 | | * set securebits. */ |
222 | 0 | log_debug_errno(r, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); |
223 | 0 | return 0; |
224 | 0 | } |
225 | | |
226 | 0 | return 1; /* acquired */ |
227 | 0 | } |
228 | | |
229 | 0 | int capability_bounding_set_drop(uint64_t keep, bool right_now) { |
230 | 0 | int k, r; |
231 | | |
232 | | /* If we are run as PID 1 we will lack CAP_SETPCAP by default in the effective set (yes, the kernel |
233 | | * drops that when executing init!), so get it back temporarily so that we can call PR_CAPBSET_DROP. */ |
234 | |
|
235 | 0 | CapabilityQuintet q; |
236 | 0 | r = capability_get(&q); |
237 | 0 | if (r < 0) |
238 | 0 | return r; |
239 | 0 | CapabilityQuintet saved = q; |
240 | |
|
241 | 0 | r = capability_gain_cap_setpcap(); |
242 | 0 | if (r < 0) |
243 | 0 | return r; |
244 | | |
245 | 0 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
246 | 0 | if (BIT_SET(keep, i)) |
247 | 0 | continue; |
248 | | |
249 | | /* Drop it from the bounding set */ |
250 | 0 | r = prctl_safe(PR_CAPBSET_DROP, i, 0, 0, 0); |
251 | 0 | if (r < 0) { |
252 | | /* If dropping the capability failed, let's see if we didn't have it in the first |
253 | | * place. If so, continue anyway, as dropping a capability we didn't have in the |
254 | | * first place doesn't really matter anyway. */ |
255 | 0 | if (prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0) != 0) |
256 | 0 | goto finish; |
257 | 0 | } |
258 | | |
259 | | /* Also drop it from the inheritable set, so that anything we exec() loses the capability for |
260 | | * good. */ |
261 | 0 | CLEAR_BIT(q.inheritable, i); |
262 | | |
263 | | /* If we shall apply this right now drop it also from our own capability sets. */ |
264 | 0 | if (right_now) { |
265 | 0 | CLEAR_BIT(q.effective, i); |
266 | 0 | CLEAR_BIT(q.permitted, i); |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | 0 | r = 0; |
271 | |
|
272 | 0 | finish: |
273 | 0 | k = capability_apply(&q); |
274 | 0 | if (k < 0) |
275 | | /* If there are no actual changes anyway then let's ignore this error. */ |
276 | 0 | if (!capability_quintet_equal(&q, &saved)) |
277 | 0 | return k; |
278 | | |
279 | 0 | return r; |
280 | 0 | } |
281 | | |
282 | 0 | static int drop_from_file(const char *fn, uint64_t keep) { |
283 | 0 | _cleanup_free_ char *p = NULL; |
284 | 0 | uint64_t current, after; |
285 | 0 | uint32_t hi, lo; |
286 | 0 | int r, k; |
287 | |
|
288 | 0 | r = read_one_line_file(fn, &p); |
289 | 0 | if (r < 0) |
290 | 0 | return r; |
291 | | |
292 | 0 | k = sscanf(p, "%" PRIu32 " %" PRIu32, &lo, &hi); |
293 | 0 | if (k != 2) |
294 | 0 | return -EIO; |
295 | | |
296 | 0 | current = (uint64_t) lo | ((uint64_t) hi << 32); |
297 | 0 | after = current & keep; |
298 | |
|
299 | 0 | if (current == after) |
300 | 0 | return 0; |
301 | | |
302 | 0 | lo = after & UINT32_MAX; |
303 | 0 | hi = (after >> 32) & UINT32_MAX; |
304 | |
|
305 | 0 | return write_string_filef(fn, 0, "%" PRIu32 " %" PRIu32, lo, hi); |
306 | 0 | } |
307 | | |
308 | 0 | int capability_bounding_set_drop_usermode(uint64_t keep) { |
309 | 0 | int r; |
310 | |
|
311 | 0 | r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); |
312 | 0 | if (r < 0) |
313 | 0 | return r; |
314 | | |
315 | 0 | r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); |
316 | 0 | if (r < 0) |
317 | 0 | return r; |
318 | | |
319 | 0 | return r; |
320 | 0 | } |
321 | | |
322 | 0 | int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { |
323 | 0 | int r; |
324 | | |
325 | | /* Unfortunately we cannot leave privilege dropping to PID 1 here, since we want to run as user but |
326 | | * want to keep some capabilities. Since file capabilities have been introduced this cannot be done |
327 | | * across exec() anymore, unless our binary has the capability configured in the file system, which |
328 | | * we want to avoid. */ |
329 | |
|
330 | 0 | if (setresgid(gid, gid, gid) < 0) |
331 | 0 | return log_error_errno(errno, "Failed to change group ID: %m"); |
332 | | |
333 | 0 | r = maybe_setgroups(/* size= */ 0, /* list= */ NULL); |
334 | 0 | if (r < 0) |
335 | 0 | return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); |
336 | | |
337 | | /* Ensure we keep the permitted caps across the setresuid(). Note that we do this even if we actually |
338 | | * don't want to keep any capabilities, since we want to be able to drop them from the bounding set |
339 | | * too, and we can only do that if we have capabilities. */ |
340 | 0 | r = prctl_safe(PR_SET_KEEPCAPS, 1, 0, 0, 0); |
341 | 0 | if (r < 0) |
342 | 0 | return log_error_errno(r, "Failed to enable keep capabilities flag: %m"); |
343 | | |
344 | 0 | if (setresuid(uid, uid, uid) < 0) |
345 | 0 | return log_error_errno(errno, "Failed to change user ID: %m"); |
346 | | |
347 | 0 | r = prctl_safe(PR_SET_KEEPCAPS, 0, 0, 0, 0); |
348 | 0 | if (r < 0) |
349 | 0 | return log_error_errno(r, "Failed to disable keep capabilities flag: %m"); |
350 | | |
351 | | /* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except |
352 | | * the ones we want to keep */ |
353 | 0 | r = capability_bounding_set_drop(keep_capabilities, /* right_now= */ true); |
354 | 0 | if (r < 0) |
355 | 0 | return log_error_errno(r, "Failed to drop capabilities: %m"); |
356 | | |
357 | | /* Now upgrade the permitted caps we still kept to effective caps */ |
358 | 0 | if (keep_capabilities != 0) { |
359 | 0 | CapabilityQuintet q = { |
360 | 0 | .effective = keep_capabilities, |
361 | 0 | .permitted = keep_capabilities, |
362 | 0 | }; |
363 | |
|
364 | 0 | r = capability_apply(&q); |
365 | 0 | if (r < 0) |
366 | 0 | return log_error_errno(r, "Failed to increase capabilities: %m"); |
367 | 0 | } |
368 | | |
369 | 0 | return 0; |
370 | 0 | } |
371 | | |
372 | 0 | static int change_capability(unsigned cap, bool b) { |
373 | 0 | CapabilityQuintet q; |
374 | 0 | int r; |
375 | |
|
376 | 0 | assert(cap <= CAP_LIMIT); |
377 | |
|
378 | 0 | r = capability_get(&q); |
379 | 0 | if (r < 0) |
380 | 0 | return r; |
381 | | |
382 | 0 | if (b) { |
383 | 0 | SET_BIT(q.effective, cap); |
384 | 0 | SET_BIT(q.permitted, cap); |
385 | 0 | SET_BIT(q.inheritable, cap); |
386 | 0 | } else { |
387 | 0 | CLEAR_BIT(q.effective, cap); |
388 | 0 | CLEAR_BIT(q.permitted, cap); |
389 | 0 | CLEAR_BIT(q.inheritable, cap); |
390 | 0 | } |
391 | |
|
392 | 0 | return capability_apply(&q); |
393 | 0 | } |
394 | | |
395 | 0 | int drop_capability(unsigned cap) { |
396 | 0 | return change_capability(cap, false); |
397 | 0 | } |
398 | | |
399 | 0 | int keep_capability(unsigned cap) { |
400 | 0 | return change_capability(cap, true); |
401 | 0 | } |
402 | | |
403 | 0 | bool capability_quintet_mangle(CapabilityQuintet *q) { |
404 | 0 | uint64_t combined, drop = 0; |
405 | |
|
406 | 0 | assert(q); |
407 | |
|
408 | 0 | combined = q->effective | q->bounding | q->inheritable | q->permitted | q->ambient; |
409 | |
|
410 | 0 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
411 | 0 | if (!BIT_SET(combined, i)) |
412 | 0 | continue; |
413 | | |
414 | 0 | if (prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0) > 0) |
415 | 0 | continue; |
416 | | |
417 | 0 | SET_BIT(drop, i); |
418 | |
|
419 | 0 | log_debug("Dropping capability not in the current bounding set: %s", capability_to_name(i)); |
420 | 0 | } |
421 | |
|
422 | 0 | q->effective &= ~drop; |
423 | 0 | q->bounding &= ~drop; |
424 | 0 | q->inheritable &= ~drop; |
425 | 0 | q->permitted &= ~drop; |
426 | 0 | q->ambient &= ~drop; |
427 | |
|
428 | 0 | return drop != 0; /* Let the caller know we changed something */ |
429 | 0 | } |
430 | | |
431 | 0 | int capability_quintet_enforce(const CapabilityQuintet *q) { |
432 | 0 | CapabilityQuintet c; |
433 | 0 | bool modified = false; |
434 | 0 | int r; |
435 | |
|
436 | 0 | assert(q); |
437 | |
|
438 | 0 | if (q->ambient != CAP_MASK_UNSET || |
439 | 0 | q->inheritable != CAP_MASK_UNSET || |
440 | 0 | q->permitted != CAP_MASK_UNSET || |
441 | 0 | q->effective != CAP_MASK_UNSET) { |
442 | 0 | r = capability_get(&c); |
443 | 0 | if (r < 0) |
444 | 0 | return r; |
445 | 0 | } |
446 | | |
447 | 0 | if (q->ambient != CAP_MASK_UNSET) { |
448 | | /* In order to raise the ambient caps set we first need to raise the matching |
449 | | * inheritable + permitted cap */ |
450 | 0 | if (!FLAGS_SET(c.permitted, q->ambient) || |
451 | 0 | !FLAGS_SET(c.inheritable, q->ambient)) { |
452 | |
|
453 | 0 | c.permitted |= q->ambient; |
454 | 0 | c.inheritable |= q->ambient; |
455 | |
|
456 | 0 | r = capability_apply(&c); |
457 | 0 | if (r < 0) |
458 | 0 | return r; |
459 | 0 | } |
460 | | |
461 | 0 | r = capability_ambient_set_apply(q->ambient, /* also_inherit= */ false); |
462 | 0 | if (r < 0) |
463 | 0 | return r; |
464 | 0 | } |
465 | | |
466 | 0 | if (q->inheritable != CAP_MASK_UNSET || q->permitted != CAP_MASK_UNSET || q->effective != CAP_MASK_UNSET) { |
467 | 0 | if (!FLAGS_SET(c.effective, q->effective) || |
468 | 0 | !FLAGS_SET(c.permitted, q->permitted) || |
469 | 0 | !FLAGS_SET(c.inheritable, q->inheritable)) { |
470 | |
|
471 | 0 | c.effective |= q->effective; |
472 | 0 | c.permitted |= q->permitted; |
473 | 0 | c.inheritable |= q->inheritable; |
474 | | |
475 | | /* Now, let's enforce the caps for the first time. Note that this is where we acquire |
476 | | * caps in any of the sets we currently don't have. We have to do this before |
477 | | * dropping the bounding caps below, since at that point we can never acquire new |
478 | | * caps in inherited/permitted/effective anymore, but only lose them. |
479 | | * |
480 | | * In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit |
481 | | * longer. Let's add it to our list hence for now. */ |
482 | 0 | if (q->bounding != CAP_MASK_UNSET && |
483 | 0 | (!BIT_SET(c.effective, CAP_SETPCAP) || !BIT_SET(c.permitted, CAP_SETPCAP))) { |
484 | 0 | CapabilityQuintet tmp = c; |
485 | |
|
486 | 0 | SET_BIT(c.effective, CAP_SETPCAP); |
487 | 0 | SET_BIT(c.permitted, CAP_SETPCAP); |
488 | |
|
489 | 0 | modified = true; |
490 | |
|
491 | 0 | r = capability_apply(&tmp); |
492 | 0 | } else |
493 | 0 | r = capability_apply(&c); |
494 | 0 | if (r < 0) |
495 | 0 | return r; |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | 0 | if (q->bounding != CAP_MASK_UNSET) { |
500 | 0 | r = capability_bounding_set_drop(q->bounding, /* right_now= */ false); |
501 | 0 | if (r < 0) |
502 | 0 | return r; |
503 | 0 | } |
504 | | |
505 | | /* If needed, let's now set the caps again, this time in the final version, which differs from what |
506 | | * we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding |
507 | | * bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't |
508 | | * matter. */ |
509 | 0 | if (modified) { |
510 | 0 | r = capability_apply(&c); |
511 | 0 | if (r < 0) |
512 | 0 | return r; |
513 | 0 | } |
514 | | |
515 | 0 | return 0; |
516 | 0 | } |
517 | | |
518 | 0 | int capability_get_ambient(uint64_t *ret) { |
519 | 0 | uint64_t a = 0; |
520 | 0 | int r; |
521 | |
|
522 | 0 | assert(ret); |
523 | |
|
524 | 0 | for (unsigned i = 0; i <= cap_last_cap(); i++) { |
525 | 0 | r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); |
526 | 0 | if (r < 0) |
527 | 0 | return r; |
528 | 0 | if (r > 0) |
529 | 0 | SET_BIT(a, i); |
530 | 0 | } |
531 | | |
532 | 0 | *ret = a; |
533 | 0 | return 1; |
534 | 0 | } |
535 | | |
536 | 1.69k | int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret) { |
537 | 1.69k | int r; |
538 | | |
539 | 1.69k | if (!pidref_is_set(pidref)) |
540 | 0 | return -ESRCH; |
541 | 1.69k | if (pidref_is_remote(pidref)) |
542 | 0 | return -EREMOTE; |
543 | | |
544 | 1.69k | const char *path = procfs_file_alloca(pidref->pid, "status"); |
545 | 1.69k | _cleanup_fclose_ FILE *f = fopen(path, "re"); |
546 | 1.69k | if (!f) { |
547 | 0 | if (errno == ENOENT && proc_mounted() == 0) |
548 | 0 | return -ENOSYS; |
549 | | |
550 | 0 | return -errno; |
551 | 0 | } |
552 | | |
553 | 1.69k | CapabilityQuintet q = CAPABILITY_QUINTET_NULL; |
554 | 96.6k | for (;;) { |
555 | 96.6k | _cleanup_free_ char *line = NULL; |
556 | | |
557 | 96.6k | r = read_line(f, LONG_LINE_MAX, &line); |
558 | 96.6k | if (r < 0) |
559 | 0 | return r; |
560 | 96.6k | if (r == 0) |
561 | 1.69k | break; |
562 | | |
563 | 94.9k | static const struct { |
564 | 94.9k | const char *field; |
565 | 94.9k | size_t offset; |
566 | 94.9k | } fields[] = { |
567 | 94.9k | { "CapBnd:", offsetof(CapabilityQuintet, bounding) }, |
568 | 94.9k | { "CapInh:", offsetof(CapabilityQuintet, inheritable) }, |
569 | 94.9k | { "CapPrm:", offsetof(CapabilityQuintet, permitted) }, |
570 | 94.9k | { "CapEff:", offsetof(CapabilityQuintet, effective) }, |
571 | 94.9k | { "CapAmb:", offsetof(CapabilityQuintet, ambient) }, |
572 | 94.9k | }; |
573 | | |
574 | 474k | FOREACH_ELEMENT(i, fields) { |
575 | | |
576 | 474k | const char *p = first_word(line, i->field); |
577 | 474k | if (!p) |
578 | 466k | continue; |
579 | | |
580 | 8.48k | uint64_t *v = (uint64_t*) ((uint8_t*) &q + i->offset); |
581 | | |
582 | 8.48k | if (*v != CAP_MASK_UNSET) |
583 | 0 | return -EBADMSG; |
584 | | |
585 | 8.48k | r = safe_atoux64(p, v); |
586 | 8.48k | if (r < 0) |
587 | 0 | return r; |
588 | | |
589 | 8.48k | if (*v == CAP_MASK_UNSET) |
590 | 0 | return -EBADMSG; |
591 | 8.48k | } |
592 | 94.9k | } |
593 | | |
594 | 1.69k | if (!capability_quintet_is_fully_set(&q)) |
595 | 0 | return -EBADMSG; |
596 | | |
597 | 1.69k | r = pidref_verify(pidref); |
598 | 1.69k | if (r < 0) |
599 | 0 | return r; |
600 | | |
601 | 1.69k | if (ret) |
602 | 1.69k | *ret = q; |
603 | | |
604 | 1.69k | return 0; |
605 | 1.69k | } |