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