]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/selinux-util.c
tree-wide: add missing includes
[thirdparty/systemd.git] / src / basic / selinux-util.c
CommitLineData
cad45ba1
LP
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
66b6d9d5 22#include <errno.h>
66b6d9d5
WC
23#include <malloc.h>
24#include <sys/un.h>
a07e9cfb 25
66b6d9d5
WC
26#ifdef HAVE_SELINUX
27#include <selinux/selinux.h>
28#include <selinux/label.h>
29#include <selinux/context.h>
30#endif
31
b5efdb8a 32#include "alloc-util.h"
66b6d9d5
WC
33#include "strv.h"
34#include "path-util.h"
cad45ba1
LP
35#include "selinux-util.h"
36
0b6018f3 37#ifdef HAVE_SELINUX
66b6d9d5
WC
38DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
39DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
0b6018f3 40
66b6d9d5
WC
41#define _cleanup_security_context_free_ _cleanup_(freeconp)
42#define _cleanup_context_free_ _cleanup_(context_freep)
0b6018f3 43
6baa7db0 44static int cached_use = -1;
66b6d9d5 45static struct selabel_handle *label_hnd = NULL;
66cedb30
LP
46
47#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
66b6d9d5 48#endif
cad45ba1 49
6baa7db0 50bool mac_selinux_use(void) {
66b6d9d5 51#ifdef HAVE_SELINUX
6baa7db0
LP
52 if (cached_use < 0)
53 cached_use = is_selinux_enabled() > 0;
cad45ba1 54
6baa7db0 55 return cached_use;
66b6d9d5
WC
56#else
57 return false;
58#endif
cad45ba1
LP
59}
60
6baa7db0 61void mac_selinux_retest(void) {
66b6d9d5 62#ifdef HAVE_SELINUX
6baa7db0 63 cached_use = -1;
66b6d9d5 64#endif
cad45ba1 65}
0b6018f3 66
cc56fafe 67int mac_selinux_init(const char *prefix) {
66b6d9d5 68 int r = 0;
d682b3a7 69
66b6d9d5
WC
70#ifdef HAVE_SELINUX
71 usec_t before_timestamp, after_timestamp;
72 struct mallinfo before_mallinfo, after_mallinfo;
73
6baa7db0 74 if (!mac_selinux_use())
66b6d9d5
WC
75 return 0;
76
77 if (label_hnd)
78 return 0;
79
80 before_mallinfo = mallinfo();
81 before_timestamp = now(CLOCK_MONOTONIC);
82
83 if (prefix) {
84 struct selinux_opt options[] = {
85 { .type = SELABEL_OPT_SUBSET, .value = prefix },
86 };
87
88 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
89 } else
90 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
91
92 if (!label_hnd) {
66cedb30 93 log_enforcing("Failed to initialize SELinux context: %m");
66b6d9d5
WC
94 r = security_getenforce() == 1 ? -errno : 0;
95 } else {
96 char timespan[FORMAT_TIMESPAN_MAX];
97 int l;
98
99 after_timestamp = now(CLOCK_MONOTONIC);
100 after_mallinfo = mallinfo();
101
102 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
103
104 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
105 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
106 (l+1023)/1024);
107 }
108#endif
109
110 return r;
d682b3a7
LP
111}
112
ecabcf8b
LP
113void mac_selinux_finish(void) {
114
115#ifdef HAVE_SELINUX
116 if (!label_hnd)
117 return;
118
119 selabel_close(label_hnd);
f5ce2b49 120 label_hnd = NULL;
ecabcf8b
LP
121#endif
122}
123
cc56fafe 124int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
66b6d9d5
WC
125
126#ifdef HAVE_SELINUX
127 struct stat st;
ecabcf8b 128 int r;
66b6d9d5 129
5dfc5461
LP
130 assert(path);
131
132 /* if mac_selinux_init() wasn't called before we are a NOOP */
66b6d9d5
WC
133 if (!label_hnd)
134 return 0;
135
136 r = lstat(path, &st);
5dfc5461
LP
137 if (r >= 0) {
138 _cleanup_security_context_free_ security_context_t fcon = NULL;
139
66b6d9d5
WC
140 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
141
142 /* If there's no label to set, then exit without warning */
143 if (r < 0 && errno == ENOENT)
144 return 0;
145
5dfc5461 146 if (r >= 0) {
66b6d9d5 147 r = lsetfilecon(path, fcon);
66b6d9d5
WC
148
149 /* If the FS doesn't support labels, then exit without warning */
15411c0c 150 if (r < 0 && errno == EOPNOTSUPP)
66b6d9d5
WC
151 return 0;
152 }
153 }
154
155 if (r < 0) {
156 /* Ignore ENOENT in some cases */
157 if (ignore_enoent && errno == ENOENT)
158 return 0;
159
160 if (ignore_erofs && errno == EROFS)
161 return 0;
162
ecabcf8b
LP
163 log_enforcing("Unable to fix SELinux security context of %s: %m", path);
164 if (security_getenforce() == 1)
165 return -errno;
66b6d9d5
WC
166 }
167#endif
168
ecabcf8b 169 return 0;
66b6d9d5
WC
170}
171
ecabcf8b 172int mac_selinux_apply(const char *path, const char *label) {
66b6d9d5
WC
173
174#ifdef HAVE_SELINUX
ecabcf8b
LP
175 if (!mac_selinux_use())
176 return 0;
177
0f474365
LP
178 assert(path);
179 assert(label);
180
ecabcf8b
LP
181 if (setfilecon(path, (security_context_t) label) < 0) {
182 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
0f474365 183 if (security_getenforce() > 0)
ecabcf8b
LP
184 return -errno;
185 }
66b6d9d5 186#endif
ecabcf8b 187 return 0;
d682b3a7
LP
188}
189
cc56fafe 190int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
7f416dae 191 int r = -EOPNOTSUPP;
66b6d9d5
WC
192
193#ifdef HAVE_SELINUX
1ec220bc 194 _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
66b6d9d5
WC
195 security_class_t sclass;
196
7f416dae
LP
197 assert(exe);
198 assert(label);
199
200 if (!mac_selinux_use())
201 return -EOPNOTSUPP;
66b6d9d5 202
24154879 203 r = getcon_raw(&mycon);
66b6d9d5 204 if (r < 0)
7f416dae 205 return -errno;
66b6d9d5 206
24154879 207 r = getfilecon_raw(exe, &fcon);
66b6d9d5 208 if (r < 0)
7f416dae 209 return -errno;
66b6d9d5
WC
210
211 sclass = string_to_security_class("process");
212 r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
7f416dae
LP
213 if (r < 0)
214 return -errno;
66b6d9d5
WC
215#endif
216
217 return r;
218}
219
cc56fafe 220int mac_selinux_get_our_label(char **label) {
66b6d9d5
WC
221 int r = -EOPNOTSUPP;
222
7f416dae
LP
223 assert(label);
224
66b6d9d5 225#ifdef HAVE_SELINUX
7f416dae
LP
226 if (!mac_selinux_use())
227 return -EOPNOTSUPP;
66b6d9d5 228
24154879 229 r = getcon_raw(label);
66b6d9d5 230 if (r < 0)
7f416dae 231 return -errno;
0b6018f3 232#endif
66b6d9d5
WC
233
234 return r;
235}
236
9008e1ac 237int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
66b6d9d5
WC
238 int r = -EOPNOTSUPP;
239
240#ifdef HAVE_SELINUX
7f416dae 241 _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
66b6d9d5
WC
242 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
243 security_class_t sclass;
66b6d9d5
WC
244 const char *range = NULL;
245
246 assert(socket_fd >= 0);
247 assert(exe);
248 assert(label);
249
7f416dae
LP
250 if (!mac_selinux_use())
251 return -EOPNOTSUPP;
252
24154879 253 r = getcon_raw(&mycon);
7f416dae
LP
254 if (r < 0)
255 return -errno;
66b6d9d5
WC
256
257 r = getpeercon(socket_fd, &peercon);
7f416dae
LP
258 if (r < 0)
259 return -errno;
66b6d9d5 260
9008e1ac 261 if (!exec_label) {
66b6d9d5
WC
262 /* If there is no context set for next exec let's use context
263 of target executable */
24154879 264 r = getfilecon_raw(exe, &fcon);
7f416dae
LP
265 if (r < 0)
266 return -errno;
66b6d9d5
WC
267 }
268
269 bcon = context_new(mycon);
7f416dae
LP
270 if (!bcon)
271 return -ENOMEM;
66b6d9d5
WC
272
273 pcon = context_new(peercon);
7f416dae
LP
274 if (!pcon)
275 return -ENOMEM;
66b6d9d5
WC
276
277 range = context_range_get(pcon);
7f416dae
LP
278 if (!range)
279 return -errno;
66b6d9d5
WC
280
281 r = context_range_set(bcon, range);
7f416dae
LP
282 if (r)
283 return -errno;
66b6d9d5
WC
284
285 freecon(mycon);
286 mycon = strdup(context_str(bcon));
7f416dae
LP
287 if (!mycon)
288 return -ENOMEM;
66b6d9d5
WC
289
290 sclass = string_to_security_class("process");
7f416dae
LP
291 r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
292 if (r < 0)
293 return -errno;
66b6d9d5 294#endif
7f416dae 295
66b6d9d5
WC
296 return r;
297}
298
710a6b50 299char* mac_selinux_free(char *label) {
ecabcf8b
LP
300
301#ifdef HAVE_SELINUX
710a6b50
LP
302 if (!label)
303 return NULL;
304
ecabcf8b 305 if (!mac_selinux_use())
710a6b50
LP
306 return NULL;
307
ecabcf8b
LP
308
309 freecon((security_context_t) label);
310#endif
710a6b50
LP
311
312 return NULL;
ecabcf8b
LP
313}
314
315int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
66b6d9d5
WC
316
317#ifdef HAVE_SELINUX
1ec220bc 318 _cleanup_security_context_free_ security_context_t filecon = NULL;
0f474365 319 int r;
66b6d9d5 320
ecabcf8b
LP
321 assert(path);
322
66cedb30 323 if (!label_hnd)
66b6d9d5
WC
324 return 0;
325
c34255bd
LP
326 if (path_is_absolute(path))
327 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
328 else {
0f474365 329 _cleanup_free_ char *newpath = NULL;
c34255bd 330
0f474365
LP
331 r = path_make_absolute_cwd(path, &newpath);
332 if (r < 0)
333 return r;
c34255bd 334
a07e9cfb 335 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
c34255bd
LP
336 }
337
0f474365
LP
338 if (r < 0) {
339 /* No context specified by the policy? Proceed without setting it. */
340 if (errno == ENOENT)
341 return 0;
2d58aa46 342
0f474365
LP
343 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
344 } else {
345 if (setfscreatecon(filecon) >= 0)
346 return 0; /* Success! */
347
348 log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
66b6d9d5
WC
349 }
350
0f474365
LP
351 if (security_getenforce() > 0)
352 return -errno;
66b6d9d5 353
0f474365
LP
354#endif
355 return 0;
66b6d9d5
WC
356}
357
ecabcf8b 358void mac_selinux_create_file_clear(void) {
66b6d9d5
WC
359
360#ifdef HAVE_SELINUX
361 PROTECT_ERRNO;
362
6baa7db0 363 if (!mac_selinux_use())
66b6d9d5
WC
364 return;
365
366 setfscreatecon(NULL);
367#endif
368}
369
ecabcf8b 370int mac_selinux_create_socket_prepare(const char *label) {
66b6d9d5
WC
371
372#ifdef HAVE_SELINUX
6baa7db0 373 if (!mac_selinux_use())
ecabcf8b 374 return 0;
66b6d9d5 375
ecabcf8b
LP
376 assert(label);
377
378 if (setsockcreatecon((security_context_t) label) < 0) {
379 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
380
381 if (security_getenforce() == 1)
382 return -errno;
383 }
66b6d9d5 384#endif
ecabcf8b
LP
385
386 return 0;
66b6d9d5
WC
387}
388
ecabcf8b 389void mac_selinux_create_socket_clear(void) {
66b6d9d5
WC
390
391#ifdef HAVE_SELINUX
ecabcf8b
LP
392 PROTECT_ERRNO;
393
6baa7db0 394 if (!mac_selinux_use())
66b6d9d5
WC
395 return;
396
ecabcf8b 397 setsockcreatecon(NULL);
66b6d9d5
WC
398#endif
399}
400
cc56fafe 401int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
66b6d9d5
WC
402
403 /* Binds a socket and label its file system object according to the SELinux policy */
404
405#ifdef HAVE_SELINUX
1ec220bc 406 _cleanup_security_context_free_ security_context_t fcon = NULL;
66b6d9d5 407 const struct sockaddr_un *un;
0f474365 408 bool context_changed = false;
66b6d9d5
WC
409 char *path;
410 int r;
411
412 assert(fd >= 0);
413 assert(addr);
414 assert(addrlen >= sizeof(sa_family_t));
415
ecabcf8b 416 if (!label_hnd)
66b6d9d5
WC
417 goto skipped;
418
419 /* Filter out non-local sockets */
420 if (addr->sa_family != AF_UNIX)
421 goto skipped;
422
423 /* Filter out anonymous sockets */
0f474365 424 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
66b6d9d5
WC
425 goto skipped;
426
427 /* Filter out abstract namespace sockets */
428 un = (const struct sockaddr_un*) addr;
429 if (un->sun_path[0] == 0)
430 goto skipped;
431
432 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
433
434 if (path_is_absolute(path))
435 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
436 else {
0f474365 437 _cleanup_free_ char *newpath = NULL;
66b6d9d5 438
0f474365
LP
439 r = path_make_absolute_cwd(path, &newpath);
440 if (r < 0)
441 return r;
66b6d9d5
WC
442
443 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
444 }
445
0f474365
LP
446 if (r < 0) {
447 /* No context specified by the policy? Proceed without setting it */
448 if (errno == ENOENT)
449 goto skipped;
66b6d9d5 450
0f474365
LP
451 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
452 if (security_getenforce() > 0)
453 return -errno;
66b6d9d5 454
0f474365
LP
455 } else {
456 if (setfscreatecon(fcon) < 0) {
457 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
458 if (security_getenforce() > 0)
459 return -errno;
460 } else
461 context_changed = true;
66b6d9d5
WC
462 }
463
0f474365
LP
464 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
465
466 if (context_changed)
467 setfscreatecon(NULL);
66b6d9d5 468
66b6d9d5
WC
469 return r;
470
471skipped:
472#endif
0f474365
LP
473 if (bind(fd, addr, addrlen) < 0)
474 return -errno;
475
476 return 0;
66b6d9d5 477}