Line data Source code
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 0 : int have_effective_cap(int value) {
36 0 : _cleanup_cap_free_ cap_t cap;
37 : cap_flag_value_t fv;
38 :
39 0 : cap = cap_get_proc();
40 0 : if (!cap)
41 0 : return -errno;
42 :
43 0 : if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
44 0 : return -errno;
45 : else
46 0 : return fv == CAP_SET;
47 : }
48 :
49 1244 : unsigned long cap_last_cap(void) {
50 : static thread_local unsigned long saved;
51 : static thread_local bool valid = false;
52 2488 : _cleanup_free_ char *content = NULL;
53 1244 : unsigned long p = 0;
54 : int r;
55 :
56 1244 : if (valid)
57 1241 : return saved;
58 :
59 : /* available since linux-3.2 */
60 3 : r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
61 3 : if (r >= 0) {
62 3 : r = safe_atolu(content, &p);
63 3 : if (r >= 0) {
64 3 : saved = p;
65 3 : valid = true;
66 3 : return p;
67 : }
68 : }
69 :
70 : /* fall back to syscall-probing for pre linux-3.2 */
71 0 : p = (unsigned long) CAP_LAST_CAP;
72 :
73 0 : if (prctl(PR_CAPBSET_READ, p) < 0) {
74 :
75 : /* Hmm, look downwards, until we find one that
76 : * works */
77 0 : for (p--; p > 0; p --)
78 0 : if (prctl(PR_CAPBSET_READ, p) >= 0)
79 0 : break;
80 :
81 : } else {
82 :
83 : /* Hmm, look upwards, until we find one that doesn't
84 : * work */
85 0 : for (;; p++)
86 0 : if (prctl(PR_CAPBSET_READ, p+1) < 0)
87 0 : break;
88 0 : }
89 :
90 0 : saved = p;
91 0 : valid = true;
92 :
93 0 : return p;
94 : }
95 :
96 0 : int capability_bounding_set_drop(uint64_t drop, bool right_now) {
97 0 : _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 0 : after_cap = cap_get_proc();
108 0 : if (!after_cap)
109 0 : return -errno;
110 :
111 0 : if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
112 0 : return -errno;
113 :
114 0 : if (fv != CAP_SET) {
115 0 : _cleanup_cap_free_ cap_t temp_cap = NULL;
116 : static const cap_value_t v = CAP_SETPCAP;
117 :
118 0 : temp_cap = cap_dup(after_cap);
119 0 : if (!temp_cap) {
120 0 : r = -errno;
121 0 : goto finish;
122 : }
123 :
124 0 : if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
125 0 : r = -errno;
126 0 : goto finish;
127 : }
128 :
129 0 : if (cap_set_proc(temp_cap) < 0) {
130 0 : r = -errno;
131 0 : goto finish;
132 : }
133 : }
134 :
135 0 : for (i = 0; i <= cap_last_cap(); i++) {
136 :
137 0 : if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
138 : cap_value_t v;
139 :
140 : /* Drop it from the bounding set */
141 0 : if (prctl(PR_CAPBSET_DROP, i) < 0) {
142 0 : r = -errno;
143 0 : goto finish;
144 : }
145 0 : 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 0 : if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
151 0 : r = -errno;
152 0 : goto finish;
153 : }
154 :
155 : /* If we shall apply this right now drop it
156 : * also from our own capability sets. */
157 0 : if (right_now) {
158 0 : if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
159 0 : cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
160 0 : r = -errno;
161 0 : goto finish;
162 : }
163 : }
164 : }
165 : }
166 :
167 0 : r = 0;
168 :
169 : finish:
170 0 : if (cap_set_proc(after_cap) < 0)
171 0 : return -errno;
172 :
173 0 : return r;
174 : }
175 :
176 0 : 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 0 : r = read_one_line_file(fn, &p);
183 0 : if (r < 0)
184 0 : return r;
185 :
186 : assert_cc(sizeof(hi) == sizeof(unsigned));
187 : assert_cc(sizeof(lo) == sizeof(unsigned));
188 :
189 0 : k = sscanf(p, "%u %u", &lo, &hi);
190 0 : free(p);
191 :
192 0 : if (k != 2)
193 0 : return -EIO;
194 :
195 0 : current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
196 0 : after = current & ~drop;
197 :
198 0 : if (current == after)
199 0 : return 0;
200 :
201 0 : lo = (unsigned) (after & 0xFFFFFFFFULL);
202 0 : hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL);
203 :
204 0 : if (asprintf(&p, "%u %u", lo, hi) < 0)
205 0 : return -ENOMEM;
206 :
207 0 : r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE);
208 0 : free(p);
209 :
210 0 : return r;
211 : }
212 :
213 0 : int capability_bounding_set_drop_usermode(uint64_t drop) {
214 : int r;
215 :
216 0 : r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
217 0 : if (r < 0)
218 0 : return r;
219 :
220 0 : r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
221 0 : if (r < 0)
222 0 : return r;
223 :
224 0 : return r;
225 : }
226 :
227 0 : int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
228 0 : _cleanup_cap_free_ cap_t d = NULL;
229 0 : 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 0 : if (setresgid(gid, gid, gid) < 0)
240 0 : return log_error_errno(errno, "Failed to change group ID: %m");
241 :
242 0 : if (setgroups(0, NULL) < 0)
243 0 : return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
244 :
245 : /* Ensure we keep the permitted caps across the setresuid() */
246 0 : if (prctl(PR_SET_KEEPCAPS, 1) < 0)
247 0 : return log_error_errno(errno, "Failed to enable keep capabilities flag: %m");
248 :
249 0 : r = setresuid(uid, uid, uid);
250 0 : if (r < 0)
251 0 : return log_error_errno(errno, "Failed to change user ID: %m");
252 :
253 0 : if (prctl(PR_SET_KEEPCAPS, 0) < 0)
254 0 : 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 0 : r = capability_bounding_set_drop(~keep_capabilities, true);
258 0 : if (r < 0)
259 0 : return log_error_errno(r, "Failed to drop capabilities: %m");
260 :
261 : /* Now upgrade the permitted caps we still kept to effective caps */
262 0 : d = cap_init();
263 0 : if (!d)
264 0 : return log_oom();
265 :
266 0 : if (keep_capabilities) {
267 0 : cap_value_t bits[u64log2(keep_capabilities) + 1];
268 :
269 0 : for (i = 0; i < ELEMENTSOF(bits); i++)
270 0 : if (keep_capabilities & (1ULL << i))
271 0 : bits[j++] = i;
272 :
273 : /* use enough bits */
274 0 : assert(i == 64 || (keep_capabilities >> i) == 0);
275 : /* don't use too many bits */
276 0 : assert(keep_capabilities & (1ULL << (i - 1)));
277 :
278 0 : if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
279 0 : cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) {
280 0 : log_error_errno(errno, "Failed to enable capabilities bits: %m");
281 0 : return -errno;
282 : }
283 :
284 0 : if (cap_set_proc(d) < 0)
285 0 : return log_error_errno(errno, "Failed to increase capabilities: %m");
286 : }
287 :
288 0 : return 0;
289 : }
290 :
291 0 : int drop_capability(cap_value_t cv) {
292 0 : _cleanup_cap_free_ cap_t tmp_cap = NULL;
293 :
294 0 : tmp_cap = cap_get_proc();
295 0 : if (!tmp_cap)
296 0 : return -errno;
297 :
298 0 : if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
299 0 : (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
300 0 : (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
301 0 : return -errno;
302 :
303 0 : if (cap_set_proc(tmp_cap) < 0)
304 0 : return -errno;
305 :
306 0 : return 0;
307 : }
|