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