chiark / gitweb /
machined: add early checks for unrealistically large image/pool sizes
[elogind.git] / src / basic / capability-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <grp.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/capability.h>
27 #include <sys/prctl.h>
28 #include <unistd.h>
29
30 #include "alloc-util.h"
31 #include "capability-util.h"
32 #include "fileio.h"
33 #include "log.h"
34 #include "macro.h"
35 #include "parse-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_bounding_set_drop(uint64_t drop, bool right_now) {
103         _cleanup_cap_free_ cap_t after_cap = NULL;
104         cap_flag_value_t fv;
105         unsigned long i;
106         int r;
107
108         /* If we are run as PID 1 we will lack CAP_SETPCAP by default
109          * in the effective set (yes, the kernel drops that when
110          * executing init!), so get it back temporarily so that we can
111          * call PR_CAPBSET_DROP. */
112
113         after_cap = cap_get_proc();
114         if (!after_cap)
115                 return -errno;
116
117         if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
118                 return -errno;
119
120         if (fv != CAP_SET) {
121                 _cleanup_cap_free_ cap_t temp_cap = NULL;
122                 static const cap_value_t v = CAP_SETPCAP;
123
124                 temp_cap = cap_dup(after_cap);
125                 if (!temp_cap) {
126                         r = -errno;
127                         goto finish;
128                 }
129
130                 if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
131                         r = -errno;
132                         goto finish;
133                 }
134
135                 if (cap_set_proc(temp_cap) < 0) {
136                         r = -errno;
137                         goto finish;
138                 }
139         }
140
141         for (i = 0; i <= cap_last_cap(); i++) {
142
143                 if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
144                         cap_value_t v;
145
146                         /* Drop it from the bounding set */
147                         if (prctl(PR_CAPBSET_DROP, i) < 0) {
148                                 r = -errno;
149                                 goto finish;
150                         }
151                         v = (cap_value_t) i;
152
153                         /* Also drop it from the inheritable set, so
154                          * that anything we exec() loses the
155                          * capability for good. */
156                         if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
157                                 r = -errno;
158                                 goto finish;
159                         }
160
161                         /* If we shall apply this right now drop it
162                          * also from our own capability sets. */
163                         if (right_now) {
164                                 if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
165                                     cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
166                                         r = -errno;
167                                         goto finish;
168                                 }
169                         }
170                 }
171         }
172
173         r = 0;
174
175 finish:
176         if (cap_set_proc(after_cap) < 0)
177                 return -errno;
178
179         return r;
180 }
181
182 static int drop_from_file(const char *fn, uint64_t drop) {
183         int r, k;
184         uint32_t hi, lo;
185         uint64_t current, after;
186         char *p;
187
188         r = read_one_line_file(fn, &p);
189         if (r < 0)
190                 return r;
191
192         assert_cc(sizeof(hi) == sizeof(unsigned));
193         assert_cc(sizeof(lo) == sizeof(unsigned));
194
195         k = sscanf(p, "%u %u", &lo, &hi);
196         free(p);
197
198         if (k != 2)
199                 return -EIO;
200
201         current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
202         after = current & ~drop;
203
204         if (current == after)
205                 return 0;
206
207         lo = (unsigned) (after & 0xFFFFFFFFULL);
208         hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
209
210         if (asprintf(&p, "%u %u", lo, hi) < 0)
211                 return -ENOMEM;
212
213         r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE);
214         free(p);
215
216         return r;
217 }
218
219 int capability_bounding_set_drop_usermode(uint64_t drop) {
220         int r;
221
222         r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
223         if (r < 0)
224                 return r;
225
226         r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
227         if (r < 0)
228                 return r;
229
230         return r;
231 }
232
233 int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
234         _cleanup_cap_free_ cap_t d = NULL;
235         unsigned i, j = 0;
236         int r;
237
238         /* Unfortunately we cannot leave privilege dropping to PID 1
239          * here, since we want to run as user but want to keep some
240          * capabilities. Since file capabilities have been introduced
241          * this cannot be done across exec() anymore, unless our
242          * binary has the capability configured in the file system,
243          * which we want to avoid. */
244
245         if (setresgid(gid, gid, gid) < 0)
246                 return log_error_errno(errno, "Failed to change group ID: %m");
247
248         if (setgroups(0, NULL) < 0)
249                 return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
250
251         /* Ensure we keep the permitted caps across the setresuid() */
252         if (prctl(PR_SET_KEEPCAPS, 1) < 0)
253                 return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
254
255         r = setresuid(uid, uid, uid);
256         if (r < 0)
257                 return log_error_errno(errno, "Failed to change user ID: %m");
258
259         if (prctl(PR_SET_KEEPCAPS, 0) < 0)
260                 return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
261
262         /* Drop all caps from the bounding set, except the ones we want */
263         r = capability_bounding_set_drop(~keep_capabilities, true);
264         if (r < 0)
265                 return log_error_errno(r, "Failed to drop capabilities: %m");
266
267         /* Now upgrade the permitted caps we still kept to effective caps */
268         d = cap_init();
269         if (!d)
270                 return log_oom();
271
272         if (keep_capabilities) {
273                 cap_value_t bits[u64log2(keep_capabilities) + 1];
274
275                 for (i = 0; i < ELEMENTSOF(bits); i++)
276                         if (keep_capabilities & (1ULL << i))
277                                 bits[j++] = i;
278
279                 /* use enough bits */
280                 assert(i == 64 || (keep_capabilities >> i) == 0);
281                 /* don't use too many bits */
282                 assert(keep_capabilities & (1ULL << (i - 1)));
283
284                 if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
285                     cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0)
286                         return log_error_errno(errno, "Failed to enable capabilities bits: %m");
287
288                 if (cap_set_proc(d) < 0)
289                         return log_error_errno(errno, "Failed to increase capabilities: %m");
290         }
291
292         return 0;
293 }
294
295 int drop_capability(cap_value_t cv) {
296         _cleanup_cap_free_ cap_t tmp_cap = NULL;
297
298         tmp_cap = cap_get_proc();
299         if (!tmp_cap)
300                 return -errno;
301
302         if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
303             (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
304             (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
305                 return -errno;
306
307         if (cap_set_proc(tmp_cap) < 0)
308                 return -errno;
309
310         return 0;
311 }
312 #endif // 0