]>
Commit | Line | Data |
---|---|---|
d7832d2c KS |
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 | |
5430f7f2 LP |
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 | |
d7832d2c KS |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
d7832d2c | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
d7832d2c KS |
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> | |
966bff26 | 32 | #include "grp.h" |
d7832d2c KS |
33 | |
34 | #include "macro.h" | |
d7832d2c KS |
35 | #include "util.h" |
36 | #include "log.h" | |
a5c32cff | 37 | #include "fileio.h" |
966bff26 | 38 | #include "capability.h" |
d7832d2c KS |
39 | |
40 | int have_effective_cap(int value) { | |
5ce70e5b | 41 | _cleanup_cap_free_ cap_t cap; |
d7832d2c | 42 | cap_flag_value_t fv; |
d7832d2c | 43 | |
ec8927ca LP |
44 | cap = cap_get_proc(); |
45 | if (!cap) | |
d7832d2c KS |
46 | return -errno; |
47 | ||
48 | if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) | |
5ce70e5b | 49 | return -errno; |
d7832d2c | 50 | else |
5ce70e5b | 51 | return fv == CAP_SET; |
d7832d2c KS |
52 | } |
53 | ||
54 | unsigned long cap_last_cap(void) { | |
ec202eae SL |
55 | static thread_local unsigned long saved; |
56 | static thread_local bool valid = false; | |
d7832d2c KS |
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 | } | |
ec8927ca LP |
86 | |
87 | int capability_bounding_set_drop(uint64_t drop, bool right_now) { | |
6a010ac9 | 88 | _cleanup_cap_free_ cap_t after_cap = NULL; |
ec8927ca | 89 | cap_flag_value_t fv; |
6a010ac9 | 90 | unsigned long i; |
ec8927ca LP |
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 | ||
5ce70e5b | 102 | if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) |
ec8927ca | 103 | return -errno; |
ec8927ca LP |
104 | |
105 | if (fv != CAP_SET) { | |
6a010ac9 | 106 | _cleanup_cap_free_ cap_t temp_cap = NULL; |
ec8927ca LP |
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 | } | |
693eb9a2 | 136 | v = (cap_value_t) i; |
ec8927ca LP |
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: | |
a349eb10 LP |
161 | if (cap_set_proc(after_cap) < 0) |
162 | return -errno; | |
ec8927ca LP |
163 | |
164 | return r; | |
165 | } | |
939b8f14 LP |
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 | ||
574d5f2d | 198 | r = write_string_file(fn, p); |
939b8f14 LP |
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 | } | |
966bff26 | 217 | |
ed617ec2 | 218 | int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { |
966bff26 LP |
219 | |
220 | _cleanup_cap_free_ cap_t d = NULL; | |
966bff26 LP |
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) { | |
56f64d95 | 231 | log_error_errno(errno, "Failed to change group ID: %m"); |
966bff26 LP |
232 | return -errno; |
233 | } | |
234 | ||
235 | if (setgroups(0, NULL) < 0) { | |
56f64d95 | 236 | log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); |
966bff26 LP |
237 | return -errno; |
238 | } | |
239 | ||
240 | if (prctl(PR_SET_KEEPCAPS, 1) < 0) { | |
56f64d95 | 241 | log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); |
966bff26 LP |
242 | return -errno; |
243 | } | |
244 | ||
245 | r = setresuid(uid, uid, uid); | |
246 | if (r < 0) { | |
56f64d95 | 247 | log_error_errno(errno, "Failed to change user ID: %m"); |
966bff26 LP |
248 | return -errno; |
249 | } | |
250 | ||
251 | if (prctl(PR_SET_KEEPCAPS, 0) < 0) { | |
56f64d95 | 252 | log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); |
966bff26 LP |
253 | return -errno; |
254 | } | |
255 | ||
ed617ec2 | 256 | r = capability_bounding_set_drop(~keep_capabilities, true); |
f647962d MS |
257 | if (r < 0) |
258 | return log_error_errno(r, "Failed to drop capabilities: %m"); | |
966bff26 LP |
259 | |
260 | d = cap_init(); | |
261 | if (!d) | |
262 | return log_oom(); | |
263 | ||
ed617ec2 | 264 | if (keep_capabilities) { |
6a010ac9 LP |
265 | cap_value_t bits[sizeof(keep_capabilities)*8]; |
266 | unsigned i, j = 0; | |
267 | ||
e5999b46 TG |
268 | for (i = 0; i < sizeof(keep_capabilities)*8; i++) |
269 | if (keep_capabilities & (1ULL << i)) | |
270 | bits[j++] = i; | |
271 | ||
ed617ec2 TG |
272 | if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || |
273 | cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) { | |
56f64d95 | 274 | log_error_errno(errno, "Failed to enable capabilities bits: %m"); |
ed617ec2 TG |
275 | return -errno; |
276 | } | |
966bff26 LP |
277 | } |
278 | ||
279 | if (cap_set_proc(d) < 0) { | |
56f64d95 | 280 | log_error_errno(errno, "Failed to increase capabilities: %m"); |
966bff26 LP |
281 | return -errno; |
282 | } | |
283 | ||
284 | return 0; | |
285 | } |