chiark / gitweb /
json: fix a mem leak
[elogind.git] / src / shared / capability.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 <unistd.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <sys/capability.h>
26 #include <sys/prctl.h>
27 #include "grp.h"
28
29 #include "macro.h"
30 #include "util.h"
31 #include "log.h"
32 #include "fileio.h"
33 #include "capability.h"
34
35 int have_effective_cap(int value) {
36         _cleanup_cap_free_ cap_t cap;
37         cap_flag_value_t fv;
38
39         cap = cap_get_proc();
40         if (!cap)
41                 return -errno;
42
43         if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
44                 return -errno;
45         else
46                 return fv == CAP_SET;
47 }
48
49 unsigned long cap_last_cap(void) {
50         static thread_local unsigned long saved;
51         static thread_local bool valid = false;
52         _cleanup_free_ char *content = NULL;
53         unsigned long p = 0;
54         int r;
55
56         if (valid)
57                 return saved;
58
59         /* available since linux-3.2 */
60         r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
61         if (r >= 0) {
62                 r = safe_atolu(content, &p);
63                 if (r >= 0) {
64                         saved = p;
65                         valid = true;
66                         return p;
67                 }
68         }
69
70         /* fall back to syscall-probing for pre linux-3.2 */
71         p = (unsigned long) CAP_LAST_CAP;
72
73         if (prctl(PR_CAPBSET_READ, p) < 0) {
74
75                 /* Hmm, look downwards, until we find one that
76                  * works */
77                 for (p--; p > 0; p --)
78                         if (prctl(PR_CAPBSET_READ, p) >= 0)
79                                 break;
80
81         } else {
82
83                 /* Hmm, look upwards, until we find one that doesn't
84                  * work */
85                 for (;; p++)
86                         if (prctl(PR_CAPBSET_READ, p+1) < 0)
87                                 break;
88         }
89
90         saved = p;
91         valid = true;
92
93         return p;
94 }
95
96 int capability_bounding_set_drop(uint64_t drop, bool right_now) {
97         _cleanup_cap_free_ cap_t after_cap = NULL;
98         cap_flag_value_t fv;
99         unsigned long i;
100         int r;
101
102         /* If we are run as PID 1 we will lack CAP_SETPCAP by default
103          * in the effective set (yes, the kernel drops that when
104          * executing init!), so get it back temporarily so that we can
105          * call PR_CAPBSET_DROP. */
106
107         after_cap = cap_get_proc();
108         if (!after_cap)
109                 return -errno;
110
111         if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
112                 return -errno;
113
114         if (fv != CAP_SET) {
115                 _cleanup_cap_free_ cap_t temp_cap = NULL;
116                 static const cap_value_t v = CAP_SETPCAP;
117
118                 temp_cap = cap_dup(after_cap);
119                 if (!temp_cap) {
120                         r = -errno;
121                         goto finish;
122                 }
123
124                 if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
125                         r = -errno;
126                         goto finish;
127                 }
128
129                 if (cap_set_proc(temp_cap) < 0) {
130                         r = -errno;
131                         goto finish;
132                 }
133         }
134
135         for (i = 0; i <= cap_last_cap(); i++) {
136
137                 if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
138                         cap_value_t v;
139
140                         /* Drop it from the bounding set */
141                         if (prctl(PR_CAPBSET_DROP, i) < 0) {
142                                 r = -errno;
143                                 goto finish;
144                         }
145                         v = (cap_value_t) i;
146
147                         /* Also drop it from the inheritable set, so
148                          * that anything we exec() loses the
149                          * capability for good. */
150                         if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
151                                 r = -errno;
152                                 goto finish;
153                         }
154
155                         /* If we shall apply this right now drop it
156                          * also from our own capability sets. */
157                         if (right_now) {
158                                 if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
159                                     cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
160                                         r = -errno;
161                                         goto finish;
162                                 }
163                         }
164                 }
165         }
166
167         r = 0;
168
169 finish:
170         if (cap_set_proc(after_cap) < 0)
171                 return -errno;
172
173         return r;
174 }
175
176 static int drop_from_file(const char *fn, uint64_t drop) {
177         int r, k;
178         uint32_t hi, lo;
179         uint64_t current, after;
180         char *p;
181
182         r = read_one_line_file(fn, &p);
183         if (r < 0)
184                 return r;
185
186         assert_cc(sizeof(hi) == sizeof(unsigned));
187         assert_cc(sizeof(lo) == sizeof(unsigned));
188
189         k = sscanf(p, "%u %u", &lo, &hi);
190         free(p);
191
192         if (k != 2)
193                 return -EIO;
194
195         current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
196         after = current & ~drop;
197
198         if (current == after)
199                 return 0;
200
201         lo = (unsigned) (after & 0xFFFFFFFFULL);
202         hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
203
204         if (asprintf(&p, "%u %u", lo, hi) < 0)
205                 return -ENOMEM;
206
207         r = write_string_file(fn, p);
208         free(p);
209
210         return r;
211 }
212
213 int capability_bounding_set_drop_usermode(uint64_t drop) {
214         int r;
215
216         r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
217         if (r < 0)
218                 return r;
219
220         r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
221         if (r < 0)
222                 return r;
223
224         return r;
225 }
226
227 int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
228         _cleanup_cap_free_ cap_t d = NULL;
229         unsigned i, j = 0;
230         int r;
231
232         /* Unfortunately we cannot leave privilege dropping to PID 1
233          * here, since we want to run as user but want to keep some
234          * capabilities. Since file capabilities have been introduced
235          * this cannot be done across exec() anymore, unless our
236          * binary has the capability configured in the file system,
237          * which we want to avoid. */
238
239         if (setresgid(gid, gid, gid) < 0)
240                 return log_error_errno(errno, "Failed to change group ID: %m");
241
242         if (setgroups(0, NULL) < 0)
243                 return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
244
245         /* Ensure we keep the permitted caps across the setresuid() */
246         if (prctl(PR_SET_KEEPCAPS, 1) < 0)
247                 return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
248
249         r = setresuid(uid, uid, uid);
250         if (r < 0)
251                 return log_error_errno(errno, "Failed to change user ID: %m");
252
253         if (prctl(PR_SET_KEEPCAPS, 0) < 0)
254                 return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
255
256         /* Drop all caps from the bounding set, except the ones we want */
257         r = capability_bounding_set_drop(~keep_capabilities, true);
258         if (r < 0)
259                 return log_error_errno(r, "Failed to drop capabilities: %m");
260
261         /* Now upgrade the permitted caps we still kept to effective caps */
262         d = cap_init();
263         if (!d)
264                 return log_oom();
265
266         if (keep_capabilities) {
267                 cap_value_t bits[u64log2(keep_capabilities) + 1];
268
269                 for (i = 0; i < ELEMENTSOF(bits); i++)
270                         if (keep_capabilities & (1ULL << i))
271                                 bits[j++] = i;
272
273                 /* use enough bits */
274                 assert(i == 64 || (keep_capabilities >> i) == 0);
275                 /* don't use too many bits */
276                 assert(keep_capabilities & (1ULL << (i - 1)));
277
278                 if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
279                     cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
280                         log_error_errno(errno, "Failed to enable capabilities bits: %m");
281                         return -errno;
282                 }
283
284                 if (cap_set_proc(d) < 0)
285                         return log_error_errno(errno, "Failed to increase capabilities: %m");
286         }
287
288         return 0;
289 }
290
291 int drop_capability(cap_value_t cv) {
292         _cleanup_cap_free_ cap_t tmp_cap = NULL;
293
294         tmp_cap = cap_get_proc();
295         if (!tmp_cap)
296                 return -errno;
297
298         if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
299             (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
300             (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
301                 return -errno;
302
303         if (cap_set_proc(tmp_cap) < 0)
304                 return -errno;
305
306         return 0;
307 }