]>
Commit | Line | Data |
---|---|---|
663996b3 MS |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright 2010 Lennart Poettering | |
5 | ||
6 | systemd is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU Lesser General Public License as published by | |
8 | the Free Software Foundation; either version 2.1 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | systemd is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public License | |
17 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
18 | ***/ | |
19 | ||
663996b3 | 20 | #include <errno.h> |
db2df898 | 21 | #include <grp.h> |
663996b3 | 22 | #include <stdio.h> |
4c89c718 | 23 | #include <stdlib.h> |
663996b3 MS |
24 | #include <sys/capability.h> |
25 | #include <sys/prctl.h> | |
db2df898 | 26 | #include <unistd.h> |
663996b3 | 27 | |
db2df898 MP |
28 | #include "alloc-util.h" |
29 | #include "capability-util.h" | |
30 | #include "fileio.h" | |
31 | #include "log.h" | |
663996b3 | 32 | #include "macro.h" |
db2df898 | 33 | #include "parse-util.h" |
663996b3 | 34 | #include "util.h" |
663996b3 MS |
35 | |
36 | int have_effective_cap(int value) { | |
60f067b4 | 37 | _cleanup_cap_free_ cap_t cap; |
663996b3 | 38 | cap_flag_value_t fv; |
663996b3 MS |
39 | |
40 | cap = cap_get_proc(); | |
41 | if (!cap) | |
42 | return -errno; | |
43 | ||
44 | if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) | |
60f067b4 | 45 | return -errno; |
663996b3 | 46 | else |
60f067b4 | 47 | return fv == CAP_SET; |
663996b3 MS |
48 | } |
49 | ||
50 | unsigned long cap_last_cap(void) { | |
60f067b4 JS |
51 | static thread_local unsigned long saved; |
52 | static thread_local bool valid = false; | |
e735f4d4 | 53 | _cleanup_free_ char *content = NULL; |
e3bff60a | 54 | unsigned long p = 0; |
e735f4d4 | 55 | int r; |
663996b3 MS |
56 | |
57 | if (valid) | |
58 | return saved; | |
59 | ||
e735f4d4 MP |
60 | /* available since linux-3.2 */ |
61 | r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); | |
62 | if (r >= 0) { | |
63 | r = safe_atolu(content, &p); | |
64 | if (r >= 0) { | |
65 | saved = p; | |
66 | valid = true; | |
67 | return p; | |
68 | } | |
69 | } | |
70 | ||
71 | /* fall back to syscall-probing for pre linux-3.2 */ | |
663996b3 MS |
72 | p = (unsigned long) CAP_LAST_CAP; |
73 | ||
74 | if (prctl(PR_CAPBSET_READ, p) < 0) { | |
75 | ||
76 | /* Hmm, look downwards, until we find one that | |
77 | * works */ | |
78 | for (p--; p > 0; p --) | |
79 | if (prctl(PR_CAPBSET_READ, p) >= 0) | |
80 | break; | |
81 | ||
82 | } else { | |
83 | ||
84 | /* Hmm, look upwards, until we find one that doesn't | |
85 | * work */ | |
86 | for (;; p++) | |
87 | if (prctl(PR_CAPBSET_READ, p+1) < 0) | |
88 | break; | |
89 | } | |
90 | ||
91 | saved = p; | |
92 | valid = true; | |
93 | ||
94 | return p; | |
95 | } | |
96 | ||
4c89c718 MP |
97 | int capability_update_inherited_set(cap_t caps, uint64_t set) { |
98 | unsigned long i; | |
99 | ||
100 | /* Add capabilities in the set to the inherited caps. Do not apply | |
101 | * them yet. */ | |
102 | ||
103 | for (i = 0; i < cap_last_cap(); i++) { | |
104 | ||
105 | if (set & (UINT64_C(1) << i)) { | |
106 | cap_value_t v; | |
107 | ||
108 | v = (cap_value_t) i; | |
109 | ||
110 | /* Make the capability inheritable. */ | |
111 | if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0) | |
112 | return -errno; | |
113 | } | |
114 | } | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | int capability_ambient_set_apply(uint64_t set, bool also_inherit) { | |
120 | unsigned long i; | |
121 | _cleanup_cap_free_ cap_t caps = NULL; | |
122 | ||
123 | /* Add the capabilities to the ambient set. */ | |
124 | ||
125 | if (also_inherit) { | |
126 | int r; | |
127 | caps = cap_get_proc(); | |
128 | if (!caps) | |
129 | return -errno; | |
130 | ||
131 | r = capability_update_inherited_set(caps, set); | |
132 | if (r < 0) | |
133 | return -errno; | |
134 | ||
135 | if (cap_set_proc(caps) < 0) | |
136 | return -errno; | |
137 | } | |
138 | ||
139 | for (i = 0; i < cap_last_cap(); i++) { | |
140 | ||
141 | if (set & (UINT64_C(1) << i)) { | |
142 | ||
143 | /* Add the capability to the ambient set. */ | |
144 | if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) | |
145 | return -errno; | |
146 | } | |
147 | } | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | int capability_bounding_set_drop(uint64_t keep, bool right_now) { | |
60f067b4 | 153 | _cleanup_cap_free_ cap_t after_cap = NULL; |
663996b3 | 154 | cap_flag_value_t fv; |
60f067b4 | 155 | unsigned long i; |
663996b3 MS |
156 | int r; |
157 | ||
158 | /* If we are run as PID 1 we will lack CAP_SETPCAP by default | |
159 | * in the effective set (yes, the kernel drops that when | |
160 | * executing init!), so get it back temporarily so that we can | |
161 | * call PR_CAPBSET_DROP. */ | |
162 | ||
163 | after_cap = cap_get_proc(); | |
164 | if (!after_cap) | |
165 | return -errno; | |
166 | ||
60f067b4 | 167 | if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) |
663996b3 | 168 | return -errno; |
663996b3 MS |
169 | |
170 | if (fv != CAP_SET) { | |
60f067b4 | 171 | _cleanup_cap_free_ cap_t temp_cap = NULL; |
663996b3 MS |
172 | static const cap_value_t v = CAP_SETPCAP; |
173 | ||
174 | temp_cap = cap_dup(after_cap); | |
175 | if (!temp_cap) { | |
176 | r = -errno; | |
177 | goto finish; | |
178 | } | |
179 | ||
180 | if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { | |
181 | r = -errno; | |
182 | goto finish; | |
183 | } | |
184 | ||
185 | if (cap_set_proc(temp_cap) < 0) { | |
186 | r = -errno; | |
187 | goto finish; | |
188 | } | |
189 | } | |
190 | ||
191 | for (i = 0; i <= cap_last_cap(); i++) { | |
192 | ||
4c89c718 | 193 | if (!(keep & (UINT64_C(1) << i))) { |
663996b3 MS |
194 | cap_value_t v; |
195 | ||
196 | /* Drop it from the bounding set */ | |
197 | if (prctl(PR_CAPBSET_DROP, i) < 0) { | |
198 | r = -errno; | |
199 | goto finish; | |
200 | } | |
60f067b4 | 201 | v = (cap_value_t) i; |
663996b3 MS |
202 | |
203 | /* Also drop it from the inheritable set, so | |
204 | * that anything we exec() loses the | |
205 | * capability for good. */ | |
206 | if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { | |
207 | r = -errno; | |
208 | goto finish; | |
209 | } | |
210 | ||
211 | /* If we shall apply this right now drop it | |
212 | * also from our own capability sets. */ | |
213 | if (right_now) { | |
214 | if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || | |
215 | cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { | |
216 | r = -errno; | |
217 | goto finish; | |
218 | } | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
223 | r = 0; | |
224 | ||
225 | finish: | |
60f067b4 JS |
226 | if (cap_set_proc(after_cap) < 0) |
227 | return -errno; | |
663996b3 MS |
228 | |
229 | return r; | |
230 | } | |
231 | ||
4c89c718 | 232 | static int drop_from_file(const char *fn, uint64_t keep) { |
663996b3 MS |
233 | int r, k; |
234 | uint32_t hi, lo; | |
235 | uint64_t current, after; | |
236 | char *p; | |
237 | ||
238 | r = read_one_line_file(fn, &p); | |
239 | if (r < 0) | |
240 | return r; | |
241 | ||
242 | assert_cc(sizeof(hi) == sizeof(unsigned)); | |
243 | assert_cc(sizeof(lo) == sizeof(unsigned)); | |
244 | ||
245 | k = sscanf(p, "%u %u", &lo, &hi); | |
246 | free(p); | |
247 | ||
248 | if (k != 2) | |
249 | return -EIO; | |
250 | ||
251 | current = (uint64_t) lo | ((uint64_t) hi << 32ULL); | |
4c89c718 | 252 | after = current & keep; |
663996b3 MS |
253 | |
254 | if (current == after) | |
255 | return 0; | |
256 | ||
257 | lo = (unsigned) (after & 0xFFFFFFFFULL); | |
258 | hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); | |
259 | ||
260 | if (asprintf(&p, "%u %u", lo, hi) < 0) | |
261 | return -ENOMEM; | |
262 | ||
7035cd9e | 263 | r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); |
663996b3 MS |
264 | free(p); |
265 | ||
266 | return r; | |
267 | } | |
268 | ||
4c89c718 | 269 | int capability_bounding_set_drop_usermode(uint64_t keep) { |
663996b3 MS |
270 | int r; |
271 | ||
4c89c718 | 272 | r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); |
663996b3 MS |
273 | if (r < 0) |
274 | return r; | |
275 | ||
4c89c718 | 276 | r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); |
663996b3 MS |
277 | if (r < 0) |
278 | return r; | |
279 | ||
280 | return r; | |
281 | } | |
60f067b4 JS |
282 | |
283 | int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { | |
60f067b4 | 284 | _cleanup_cap_free_ cap_t d = NULL; |
e735f4d4 | 285 | unsigned i, j = 0; |
60f067b4 JS |
286 | int r; |
287 | ||
288 | /* Unfortunately we cannot leave privilege dropping to PID 1 | |
289 | * here, since we want to run as user but want to keep some | |
290 | * capabilities. Since file capabilities have been introduced | |
291 | * this cannot be done across exec() anymore, unless our | |
292 | * binary has the capability configured in the file system, | |
293 | * which we want to avoid. */ | |
294 | ||
f47781d8 MP |
295 | if (setresgid(gid, gid, gid) < 0) |
296 | return log_error_errno(errno, "Failed to change group ID: %m"); | |
60f067b4 | 297 | |
f47781d8 MP |
298 | if (setgroups(0, NULL) < 0) |
299 | return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); | |
60f067b4 | 300 | |
e735f4d4 | 301 | /* Ensure we keep the permitted caps across the setresuid() */ |
f47781d8 MP |
302 | if (prctl(PR_SET_KEEPCAPS, 1) < 0) |
303 | return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); | |
60f067b4 JS |
304 | |
305 | r = setresuid(uid, uid, uid); | |
f47781d8 MP |
306 | if (r < 0) |
307 | return log_error_errno(errno, "Failed to change user ID: %m"); | |
60f067b4 | 308 | |
f47781d8 MP |
309 | if (prctl(PR_SET_KEEPCAPS, 0) < 0) |
310 | return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); | |
60f067b4 | 311 | |
e735f4d4 | 312 | /* Drop all caps from the bounding set, except the ones we want */ |
4c89c718 | 313 | r = capability_bounding_set_drop(keep_capabilities, true); |
f47781d8 MP |
314 | if (r < 0) |
315 | return log_error_errno(r, "Failed to drop capabilities: %m"); | |
60f067b4 | 316 | |
e735f4d4 | 317 | /* Now upgrade the permitted caps we still kept to effective caps */ |
60f067b4 JS |
318 | d = cap_init(); |
319 | if (!d) | |
320 | return log_oom(); | |
321 | ||
322 | if (keep_capabilities) { | |
e735f4d4 | 323 | cap_value_t bits[u64log2(keep_capabilities) + 1]; |
60f067b4 | 324 | |
e735f4d4 | 325 | for (i = 0; i < ELEMENTSOF(bits); i++) |
60f067b4 JS |
326 | if (keep_capabilities & (1ULL << i)) |
327 | bits[j++] = i; | |
328 | ||
e735f4d4 MP |
329 | /* use enough bits */ |
330 | assert(i == 64 || (keep_capabilities >> i) == 0); | |
331 | /* don't use too many bits */ | |
332 | assert(keep_capabilities & (1ULL << (i - 1))); | |
333 | ||
60f067b4 | 334 | if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || |
db2df898 MP |
335 | cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) |
336 | return log_error_errno(errno, "Failed to enable capabilities bits: %m"); | |
60f067b4 | 337 | |
e735f4d4 MP |
338 | if (cap_set_proc(d) < 0) |
339 | return log_error_errno(errno, "Failed to increase capabilities: %m"); | |
340 | } | |
f47781d8 MP |
341 | |
342 | return 0; | |
343 | } | |
344 | ||
345 | int drop_capability(cap_value_t cv) { | |
346 | _cleanup_cap_free_ cap_t tmp_cap = NULL; | |
347 | ||
348 | tmp_cap = cap_get_proc(); | |
349 | if (!tmp_cap) | |
350 | return -errno; | |
351 | ||
352 | if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || | |
353 | (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || | |
354 | (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) | |
355 | return -errno; | |
356 | ||
357 | if (cap_set_proc(tmp_cap) < 0) | |
60f067b4 | 358 | return -errno; |
60f067b4 JS |
359 | |
360 | return 0; | |
361 | } |