]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/capability.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / shared / capability.c
CommitLineData
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
40int 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
54unsigned 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
87int 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
160finish:
a349eb10
LP
161 if (cap_set_proc(after_cap) < 0)
162 return -errno;
ec8927ca
LP
163
164 return r;
165}
939b8f14
LP
166
167static 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
204int 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 218int 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}