]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
d7832d2c KS |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2010 Lennart Poettering | |
d7832d2c KS |
6 | ***/ |
7 | ||
d7832d2c | 8 | #include <errno.h> |
3ffd4af2 | 9 | #include <grp.h> |
d7832d2c | 10 | #include <stdio.h> |
11c3a366 | 11 | #include <stdlib.h> |
d7832d2c KS |
12 | #include <sys/capability.h> |
13 | #include <sys/prctl.h> | |
3ffd4af2 | 14 | #include <unistd.h> |
d7832d2c | 15 | |
b5efdb8a | 16 | #include "alloc-util.h" |
430f0182 | 17 | #include "capability-util.h" |
3ffd4af2 LP |
18 | #include "fileio.h" |
19 | #include "log.h" | |
d7832d2c | 20 | #include "macro.h" |
6bedfcbb | 21 | #include "parse-util.h" |
36d85478 | 22 | #include "user-util.h" |
d7832d2c | 23 | #include "util.h" |
d7832d2c KS |
24 | |
25 | int have_effective_cap(int value) { | |
5ce70e5b | 26 | _cleanup_cap_free_ cap_t cap; |
d7832d2c | 27 | cap_flag_value_t fv; |
d7832d2c | 28 | |
ec8927ca LP |
29 | cap = cap_get_proc(); |
30 | if (!cap) | |
d7832d2c KS |
31 | return -errno; |
32 | ||
33 | if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) | |
5ce70e5b | 34 | return -errno; |
d7832d2c | 35 | else |
5ce70e5b | 36 | return fv == CAP_SET; |
d7832d2c KS |
37 | } |
38 | ||
39 | unsigned long cap_last_cap(void) { | |
ec202eae SL |
40 | static thread_local unsigned long saved; |
41 | static thread_local bool valid = false; | |
80b43783 | 42 | _cleanup_free_ char *content = NULL; |
a7f7d1bd | 43 | unsigned long p = 0; |
80b43783 | 44 | int r; |
d7832d2c KS |
45 | |
46 | if (valid) | |
47 | return saved; | |
48 | ||
80b43783 DH |
49 | /* available since linux-3.2 */ |
50 | r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); | |
51 | if (r >= 0) { | |
52 | r = safe_atolu(content, &p); | |
53 | if (r >= 0) { | |
54 | saved = p; | |
55 | valid = true; | |
56 | return p; | |
57 | } | |
58 | } | |
59 | ||
60 | /* fall back to syscall-probing for pre linux-3.2 */ | |
d7832d2c KS |
61 | p = (unsigned long) CAP_LAST_CAP; |
62 | ||
63 | if (prctl(PR_CAPBSET_READ, p) < 0) { | |
64 | ||
65 | /* Hmm, look downwards, until we find one that | |
66 | * works */ | |
67 | for (p--; p > 0; p --) | |
68 | if (prctl(PR_CAPBSET_READ, p) >= 0) | |
69 | break; | |
70 | ||
71 | } else { | |
72 | ||
73 | /* Hmm, look upwards, until we find one that doesn't | |
74 | * work */ | |
75 | for (;; p++) | |
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 IP |
86 | int capability_update_inherited_set(cap_t caps, uint64_t set) { |
87 | unsigned long i; | |
88 | ||
89 | /* Add capabilities in the set to the inherited caps. Do not apply | |
90 | * them yet. */ | |
91 | ||
92 | for (i = 0; i < cap_last_cap(); i++) { | |
93 | ||
94 | if (set & (UINT64_C(1) << i)) { | |
95 | cap_value_t v; | |
96 | ||
97 | v = (cap_value_t) i; | |
98 | ||
99 | /* Make the capability inheritable. */ | |
100 | if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0) | |
101 | return -errno; | |
102 | } | |
103 | } | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
108 | int capability_ambient_set_apply(uint64_t set, bool also_inherit) { | |
109 | unsigned long i; | |
110 | _cleanup_cap_free_ cap_t caps = NULL; | |
111 | ||
112 | /* Add the capabilities to the ambient set. */ | |
113 | ||
114 | if (also_inherit) { | |
115 | int r; | |
116 | caps = cap_get_proc(); | |
117 | if (!caps) | |
118 | return -errno; | |
119 | ||
120 | r = capability_update_inherited_set(caps, set); | |
121 | if (r < 0) | |
122 | return -errno; | |
123 | ||
124 | if (cap_set_proc(caps) < 0) | |
125 | return -errno; | |
126 | } | |
127 | ||
128 | for (i = 0; i < cap_last_cap(); i++) { | |
129 | ||
130 | if (set & (UINT64_C(1) << i)) { | |
131 | ||
132 | /* Add the capability to the ambient set. */ | |
133 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) | |
134 | return -errno; | |
135 | } | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
a103496c | 141 | int capability_bounding_set_drop(uint64_t keep, bool right_now) { |
6067611a | 142 | _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; |
ec8927ca | 143 | cap_flag_value_t fv; |
6a010ac9 | 144 | unsigned long i; |
ec8927ca LP |
145 | int r; |
146 | ||
147 | /* If we are run as PID 1 we will lack CAP_SETPCAP by default | |
148 | * in the effective set (yes, the kernel drops that when | |
149 | * executing init!), so get it back temporarily so that we can | |
150 | * call PR_CAPBSET_DROP. */ | |
151 | ||
6067611a LP |
152 | before_cap = cap_get_proc(); |
153 | if (!before_cap) | |
ec8927ca LP |
154 | return -errno; |
155 | ||
6067611a | 156 | if (cap_get_flag(before_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) |
ec8927ca | 157 | return -errno; |
ec8927ca LP |
158 | |
159 | if (fv != CAP_SET) { | |
6a010ac9 | 160 | _cleanup_cap_free_ cap_t temp_cap = NULL; |
ec8927ca LP |
161 | static const cap_value_t v = CAP_SETPCAP; |
162 | ||
6067611a LP |
163 | temp_cap = cap_dup(before_cap); |
164 | if (!temp_cap) | |
165 | return -errno; | |
ec8927ca | 166 | |
6067611a LP |
167 | if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) |
168 | return -errno; | |
ec8927ca | 169 | |
6067611a LP |
170 | if (cap_set_proc(temp_cap) < 0) |
171 | log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); | |
172 | ||
173 | /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means | |
174 | * we'll fail later, when we actually intend to drop some capabilities. */ | |
ec8927ca LP |
175 | } |
176 | ||
6067611a LP |
177 | after_cap = cap_dup(before_cap); |
178 | if (!after_cap) | |
179 | return -errno; | |
180 | ||
ec8927ca | 181 | for (i = 0; i <= cap_last_cap(); i++) { |
6067611a | 182 | cap_value_t v; |
ec8927ca | 183 | |
6067611a LP |
184 | if ((keep & (UINT64_C(1) << i))) |
185 | continue; | |
ec8927ca | 186 | |
6067611a LP |
187 | /* Drop it from the bounding set */ |
188 | if (prctl(PR_CAPBSET_DROP, i) < 0) { | |
189 | r = -errno; | |
190 | ||
191 | /* If dropping the capability failed, let's see if we didn't have it in the first place. If so, | |
192 | * continue anyway, as dropping a capability we didn't have in the first place doesn't really | |
193 | * matter anyway. */ | |
194 | if (prctl(PR_CAPBSET_READ, i) != 0) | |
ec8927ca | 195 | goto finish; |
6067611a LP |
196 | } |
197 | v = (cap_value_t) i; | |
198 | ||
199 | /* Also drop it from the inheritable set, so | |
200 | * that anything we exec() loses the | |
201 | * capability for good. */ | |
202 | if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { | |
203 | r = -errno; | |
204 | goto finish; | |
205 | } | |
ec8927ca | 206 | |
6067611a LP |
207 | /* If we shall apply this right now drop it |
208 | * also from our own capability sets. */ | |
209 | if (right_now) { | |
210 | if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || | |
211 | cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { | |
ec8927ca LP |
212 | r = -errno; |
213 | goto finish; | |
214 | } | |
ec8927ca LP |
215 | } |
216 | } | |
217 | ||
218 | r = 0; | |
219 | ||
220 | finish: | |
6067611a LP |
221 | if (cap_set_proc(after_cap) < 0) { |
222 | /* If there are no actual changes anyway then let's ignore this error. */ | |
223 | if (cap_compare(before_cap, after_cap) != 0) | |
224 | r = -errno; | |
225 | } | |
ec8927ca LP |
226 | |
227 | return r; | |
228 | } | |
939b8f14 | 229 | |
a103496c | 230 | static int drop_from_file(const char *fn, uint64_t keep) { |
939b8f14 LP |
231 | int r, k; |
232 | uint32_t hi, lo; | |
233 | uint64_t current, after; | |
234 | char *p; | |
235 | ||
236 | r = read_one_line_file(fn, &p); | |
237 | if (r < 0) | |
238 | return r; | |
239 | ||
240 | assert_cc(sizeof(hi) == sizeof(unsigned)); | |
241 | assert_cc(sizeof(lo) == sizeof(unsigned)); | |
242 | ||
243 | k = sscanf(p, "%u %u", &lo, &hi); | |
244 | free(p); | |
245 | ||
246 | if (k != 2) | |
247 | return -EIO; | |
248 | ||
249 | current = (uint64_t) lo | ((uint64_t) hi << 32ULL); | |
a103496c | 250 | after = current & keep; |
939b8f14 LP |
251 | |
252 | if (current == after) | |
253 | return 0; | |
254 | ||
255 | lo = (unsigned) (after & 0xFFFFFFFFULL); | |
256 | hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); | |
257 | ||
258 | if (asprintf(&p, "%u %u", lo, hi) < 0) | |
259 | return -ENOMEM; | |
260 | ||
4c1fc3e4 | 261 | r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); |
939b8f14 LP |
262 | free(p); |
263 | ||
264 | return r; | |
265 | } | |
266 | ||
a103496c | 267 | int capability_bounding_set_drop_usermode(uint64_t keep) { |
939b8f14 LP |
268 | int r; |
269 | ||
a103496c | 270 | r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); |
939b8f14 LP |
271 | if (r < 0) |
272 | return r; | |
273 | ||
a103496c | 274 | r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); |
939b8f14 LP |
275 | if (r < 0) |
276 | return r; | |
277 | ||
278 | return r; | |
279 | } | |
966bff26 | 280 | |
ed617ec2 | 281 | int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { |
966bff26 | 282 | _cleanup_cap_free_ cap_t d = NULL; |
f11943c5 | 283 | unsigned i, j = 0; |
966bff26 LP |
284 | int r; |
285 | ||
286 | /* Unfortunately we cannot leave privilege dropping to PID 1 | |
287 | * here, since we want to run as user but want to keep some | |
288 | * capabilities. Since file capabilities have been introduced | |
289 | * this cannot be done across exec() anymore, unless our | |
290 | * binary has the capability configured in the file system, | |
291 | * which we want to avoid. */ | |
292 | ||
4a62c710 MS |
293 | if (setresgid(gid, gid, gid) < 0) |
294 | return log_error_errno(errno, "Failed to change group ID: %m"); | |
966bff26 | 295 | |
97f0e76f LP |
296 | r = maybe_setgroups(0, NULL); |
297 | if (r < 0) | |
298 | return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); | |
966bff26 | 299 | |
51ddf615 ZJS |
300 | /* Ensure we keep the permitted caps across the setresuid() */ |
301 | if (prctl(PR_SET_KEEPCAPS, 1) < 0) | |
4a62c710 | 302 | return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); |
966bff26 LP |
303 | |
304 | r = setresuid(uid, uid, uid); | |
4a62c710 MS |
305 | if (r < 0) |
306 | return log_error_errno(errno, "Failed to change user ID: %m"); | |
966bff26 | 307 | |
4a62c710 MS |
308 | if (prctl(PR_SET_KEEPCAPS, 0) < 0) |
309 | return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); | |
966bff26 | 310 | |
f11943c5 | 311 | /* Drop all caps from the bounding set, except the ones we want */ |
a103496c | 312 | r = capability_bounding_set_drop(keep_capabilities, true); |
f647962d MS |
313 | if (r < 0) |
314 | return log_error_errno(r, "Failed to drop capabilities: %m"); | |
966bff26 | 315 | |
f11943c5 | 316 | /* Now upgrade the permitted caps we still kept to effective caps */ |
966bff26 LP |
317 | d = cap_init(); |
318 | if (!d) | |
319 | return log_oom(); | |
320 | ||
51ddf615 | 321 | if (keep_capabilities) { |
057255fb | 322 | cap_value_t bits[u64log2(keep_capabilities) + 1]; |
6a010ac9 | 323 | |
7d328b54 | 324 | for (i = 0; i < ELEMENTSOF(bits); i++) |
51ddf615 ZJS |
325 | if (keep_capabilities & (1ULL << i)) |
326 | bits[j++] = i; | |
057255fb | 327 | |
2c9fc266 ZJS |
328 | /* use enough bits */ |
329 | assert(i == 64 || (keep_capabilities >> i) == 0); | |
330 | /* don't use too many bits */ | |
331 | assert(keep_capabilities & (1ULL << (i - 1))); | |
966bff26 | 332 | |
51ddf615 | 333 | if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || |
e1427b13 MS |
334 | cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) |
335 | return log_error_errno(errno, "Failed to enable capabilities bits: %m"); | |
51ddf615 ZJS |
336 | |
337 | if (cap_set_proc(d) < 0) | |
338 | return log_error_errno(errno, "Failed to increase capabilities: %m"); | |
339 | } | |
966bff26 LP |
340 | |
341 | return 0; | |
342 | } | |
dd5ae4c3 PK |
343 | |
344 | int drop_capability(cap_value_t cv) { | |
345 | _cleanup_cap_free_ cap_t tmp_cap = NULL; | |
346 | ||
347 | tmp_cap = cap_get_proc(); | |
348 | if (!tmp_cap) | |
349 | return -errno; | |
350 | ||
351 | if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || | |
352 | (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || | |
353 | (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) | |
354 | return -errno; | |
355 | ||
356 | if (cap_set_proc(tmp_cap) < 0) | |
357 | return -errno; | |
358 | ||
359 | return 0; | |
360 | } | |
39f608e4 LP |
361 | |
362 | bool ambient_capabilities_supported(void) { | |
363 | static int cache = -1; | |
364 | ||
365 | if (cache >= 0) | |
366 | return cache; | |
367 | ||
368 | /* If PR_CAP_AMBIENT returns something valid, or an unexpected error code we assume that ambient caps are | |
369 | * available. */ | |
370 | ||
371 | cache = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_KILL, 0, 0) >= 0 || | |
372 | !IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS); | |
373 | ||
374 | return cache; | |
375 | } |