]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/selinux-util.c
tree-wide: add FORMAT_TIMESPAN()
[thirdparty/systemd.git] / src / shared / selinux-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <malloc.h>
6 #include <stddef.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <sys/un.h>
12 #include <syslog.h>
13
14 #if HAVE_SELINUX
15 #include <selinux/avc.h>
16 #include <selinux/context.h>
17 #include <selinux/label.h>
18 #include <selinux/selinux.h>
19 #endif
20
21 #include "alloc-util.h"
22 #include "errno-util.h"
23 #include "fd-util.h"
24 #include "log.h"
25 #include "macro.h"
26 #include "path-util.h"
27 #include "selinux-util.h"
28 #include "stdio-util.h"
29 #include "time-util.h"
30
31 #if HAVE_SELINUX
32 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(context_t, context_free, NULL);
33 #define _cleanup_context_free_ _cleanup_(context_freep)
34
35 static int mac_selinux_reload(int seqno);
36
37 static int cached_use = -1;
38 static bool initialized = false;
39 static int last_policyload = 0;
40 static struct selabel_handle *label_hnd = NULL;
41 static bool have_status_page = false;
42
43 #define log_enforcing(...) \
44 log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
45
46 #define log_enforcing_errno(error, ...) \
47 ({ \
48 bool _enforcing = mac_selinux_enforcing(); \
49 int _level = _enforcing ? LOG_ERR : LOG_WARNING; \
50 int _e = (error); \
51 \
52 int _r = (log_get_max_level() >= LOG_PRI(_level)) \
53 ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
54 : -ERRNO_VALUE(_e); \
55 _enforcing ? _r : 0; \
56 })
57 #endif
58
59 bool mac_selinux_use(void) {
60 #if HAVE_SELINUX
61 if (_unlikely_(cached_use < 0)) {
62 cached_use = is_selinux_enabled() > 0;
63 log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
64 }
65
66 return cached_use;
67 #else
68 return false;
69 #endif
70 }
71
72 bool mac_selinux_enforcing(void) {
73 int r = 0;
74 #if HAVE_SELINUX
75
76 /* If the SELinux status page has been successfully opened, retrieve the enforcing
77 * status over it to avoid system calls in security_getenforce(). */
78
79 if (have_status_page)
80 r = selinux_status_getenforce();
81 else
82 r = security_getenforce();
83
84 #endif
85 return r != 0;
86 }
87
88 void mac_selinux_retest(void) {
89 #if HAVE_SELINUX
90 cached_use = -1;
91 #endif
92 }
93
94 #if HAVE_SELINUX
95 # if HAVE_MALLINFO2
96 # define HAVE_GENERIC_MALLINFO 1
97 typedef struct mallinfo2 generic_mallinfo;
98 static generic_mallinfo generic_mallinfo_get(void) {
99 return mallinfo2();
100 }
101 # elif HAVE_MALLINFO
102 # define HAVE_GENERIC_MALLINFO 1
103 typedef struct mallinfo generic_mallinfo;
104 static generic_mallinfo generic_mallinfo_get(void) {
105 /* glibc has deprecated mallinfo(), let's suppress the deprecation warning if mallinfo2() doesn't
106 * exist yet. */
107 DISABLE_WARNING_DEPRECATED_DECLARATIONS
108 return mallinfo();
109 REENABLE_WARNING
110 }
111 # else
112 # define HAVE_GENERIC_MALLINFO 0
113 # endif
114
115 static int open_label_db(void) {
116 struct selabel_handle *hnd;
117 usec_t before_timestamp, after_timestamp;
118
119 # if HAVE_GENERIC_MALLINFO
120 generic_mallinfo before_mallinfo = generic_mallinfo_get();
121 # endif
122 before_timestamp = now(CLOCK_MONOTONIC);
123
124 hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
125 if (!hnd)
126 return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m");
127
128 after_timestamp = now(CLOCK_MONOTONIC);
129 # if HAVE_GENERIC_MALLINFO
130 generic_mallinfo after_mallinfo = generic_mallinfo_get();
131 size_t l = LESS_BY((size_t) after_mallinfo.uordblks, (size_t) before_mallinfo.uordblks);
132 log_debug("Successfully loaded SELinux database in %s, size on heap is %zuK.",
133 FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0),
134 DIV_ROUND_UP(l, 1024));
135 # else
136 log_debug("Successfully loaded SELinux database in %s.",
137 FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0));
138 # endif
139
140 /* release memory after measurement */
141 if (label_hnd)
142 selabel_close(label_hnd);
143 label_hnd = TAKE_PTR(hnd);
144
145 return 0;
146 }
147 #endif
148
149 int mac_selinux_init(void) {
150 #if HAVE_SELINUX
151 int r;
152
153 if (initialized)
154 return 0;
155
156 if (!mac_selinux_use())
157 return 0;
158
159 r = selinux_status_open(/* netlink fallback */ 1);
160 if (r < 0) {
161 if (!ERRNO_IS_PRIVILEGE(errno))
162 return log_enforcing_errno(errno, "Failed to open SELinux status page: %m");
163 log_warning_errno(errno, "selinux_status_open() with netlink fallback failed, not checking for policy reloads: %m");
164 } else if (r == 1)
165 log_warning("selinux_status_open() failed to open the status page, using the netlink fallback.");
166 else
167 have_status_page = true;
168
169 r = open_label_db();
170 if (r < 0) {
171 selinux_status_close();
172 return r;
173 }
174
175 /* Save the current policyload sequence number, so mac_selinux_maybe_reload() does not trigger on
176 * first call without any actual change. */
177 last_policyload = selinux_status_policyload();
178
179 initialized = true;
180 #endif
181 return 0;
182 }
183
184 void mac_selinux_maybe_reload(void) {
185 #if HAVE_SELINUX
186 int policyload;
187
188 if (!initialized)
189 return;
190
191 /* Do not use selinux_status_updated(3), cause since libselinux 3.2 selinux_check_access(3),
192 * called in core and user instances, does also use it under the hood.
193 * That can cause changes to be consumed by selinux_check_access(3) and not being visible here.
194 * Also do not use selinux callbacks, selinux_set_callback(3), cause they are only automatically
195 * invoked since libselinux 3.2 by selinux_status_updated(3).
196 * Relevant libselinux commit: https://github.com/SELinuxProject/selinux/commit/05bdc03130d741e53e1fb45a958d0a2c184be503
197 * Debian Bullseye is going to ship libselinux 3.1, so stay compatible for backports. */
198 policyload = selinux_status_policyload();
199 if (policyload < 0) {
200 log_debug_errno(errno, "Failed to get SELinux policyload from status page: %m");
201 return;
202 }
203
204 if (policyload != last_policyload) {
205 mac_selinux_reload(policyload);
206 last_policyload = policyload;
207 }
208 #endif
209 }
210
211 void mac_selinux_finish(void) {
212
213 #if HAVE_SELINUX
214 if (label_hnd) {
215 selabel_close(label_hnd);
216 label_hnd = NULL;
217 }
218
219 selinux_status_close();
220 have_status_page = false;
221
222 initialized = false;
223 #endif
224 }
225
226 #if HAVE_SELINUX
227 static int mac_selinux_reload(int seqno) {
228 log_debug("SELinux reload %d", seqno);
229
230 (void) open_label_db();
231
232 return 0;
233 }
234 #endif
235
236 int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
237
238 assert(path);
239 assert(inside_path);
240
241 #if HAVE_SELINUX
242 _cleanup_close_ int fd = -1;
243
244 /* if mac_selinux_init() wasn't called before we are a NOOP */
245 if (!label_hnd)
246 return 0;
247
248 /* Open the file as O_PATH, to pin it while we determine and adjust the label */
249 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
250 if (fd < 0) {
251 if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
252 return 0;
253
254 return -errno;
255 }
256
257 return mac_selinux_fix_container_fd(fd, path, inside_path, flags);
258 #endif
259
260 return 0;
261 }
262
263 int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_path, LabelFixFlags flags) {
264
265 assert(fd >= 0);
266 assert(inside_path);
267
268 #if HAVE_SELINUX
269 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
270 _cleanup_freecon_ char* fcon = NULL;
271 struct stat st;
272 int r;
273
274 /* if mac_selinux_init() wasn't called before we are a NOOP */
275 if (!label_hnd)
276 return 0;
277
278 if (fstat(fd, &st) < 0)
279 return -errno;
280
281 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
282 mac_selinux_maybe_reload();
283 if (!label_hnd)
284 return 0;
285
286 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) {
287 /* If there's no label to set, then exit without warning */
288 if (errno == ENOENT)
289 return 0;
290
291 r = -errno;
292 goto fail;
293 }
294
295 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
296 if (setfilecon_raw(procfs_path, fcon) < 0) {
297 _cleanup_freecon_ char *oldcon = NULL;
298
299 /* If the FS doesn't support labels, then exit without warning */
300 if (ERRNO_IS_NOT_SUPPORTED(errno))
301 return 0;
302
303 /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
304 if (errno == EROFS && (flags & LABEL_IGNORE_EROFS))
305 return 0;
306
307 r = -errno;
308
309 /* If the old label is identical to the new one, suppress any kind of error */
310 if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
311 return 0;
312
313 goto fail;
314 }
315
316 return 0;
317
318 fail:
319 return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", strna(path), strna(inside_path));
320 #endif
321
322 return 0;
323 }
324
325 int mac_selinux_apply(const char *path, const char *label) {
326
327 assert(path);
328
329 #if HAVE_SELINUX
330 if (!mac_selinux_use())
331 return 0;
332
333 assert(label);
334
335 if (setfilecon(path, label) < 0)
336 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
337 #endif
338 return 0;
339 }
340
341 int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
342
343 assert(fd >= 0);
344
345 #if HAVE_SELINUX
346 if (!mac_selinux_use())
347 return 0;
348
349 assert(label);
350
351 if (fsetfilecon(fd, label) < 0)
352 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
353 #endif
354 return 0;
355 }
356
357 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
358 #if HAVE_SELINUX
359 _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
360 security_class_t sclass;
361 int r;
362
363 assert(exe);
364 assert(label);
365
366 if (!mac_selinux_use())
367 return -EOPNOTSUPP;
368
369 r = getcon_raw(&mycon);
370 if (r < 0)
371 return -errno;
372
373 r = getfilecon_raw(exe, &fcon);
374 if (r < 0)
375 return -errno;
376
377 sclass = string_to_security_class("process");
378 if (sclass == 0)
379 return -ENOSYS;
380
381 r = security_compute_create_raw(mycon, fcon, sclass, label);
382 if (r < 0)
383 return -errno;
384
385 return 0;
386 #else
387 return -EOPNOTSUPP;
388 #endif
389 }
390
391 int mac_selinux_get_our_label(char **label) {
392 #if HAVE_SELINUX
393 int r;
394
395 assert(label);
396
397 if (!mac_selinux_use())
398 return -EOPNOTSUPP;
399
400 r = getcon_raw(label);
401 if (r < 0)
402 return -errno;
403
404 return 0;
405 #else
406 return -EOPNOTSUPP;
407 #endif
408 }
409
410 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
411 #if HAVE_SELINUX
412 _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
413 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
414 security_class_t sclass;
415 const char *range = NULL;
416 int r;
417
418 assert(socket_fd >= 0);
419 assert(exe);
420 assert(label);
421
422 if (!mac_selinux_use())
423 return -EOPNOTSUPP;
424
425 r = getcon_raw(&mycon);
426 if (r < 0)
427 return -errno;
428
429 r = getpeercon_raw(socket_fd, &peercon);
430 if (r < 0)
431 return -errno;
432
433 if (!exec_label) {
434 /* If there is no context set for next exec let's use context
435 of target executable */
436 r = getfilecon_raw(exe, &fcon);
437 if (r < 0)
438 return -errno;
439 }
440
441 bcon = context_new(mycon);
442 if (!bcon)
443 return -ENOMEM;
444
445 pcon = context_new(peercon);
446 if (!pcon)
447 return -ENOMEM;
448
449 range = context_range_get(pcon);
450 if (!range)
451 return -errno;
452
453 r = context_range_set(bcon, range);
454 if (r)
455 return -errno;
456
457 freecon(mycon);
458 mycon = strdup(context_str(bcon));
459 if (!mycon)
460 return -ENOMEM;
461
462 sclass = string_to_security_class("process");
463 if (sclass == 0)
464 return -ENOSYS;
465
466 r = security_compute_create_raw(mycon, fcon, sclass, label);
467 if (r < 0)
468 return -errno;
469
470 return 0;
471 #else
472 return -EOPNOTSUPP;
473 #endif
474 }
475
476 char* mac_selinux_free(char *label) {
477
478 #if HAVE_SELINUX
479 freecon(label);
480 #else
481 assert(!label);
482 #endif
483
484 return NULL;
485 }
486
487 #if HAVE_SELINUX
488 static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
489 _cleanup_freecon_ char *filecon = NULL;
490 int r;
491
492 assert(abspath);
493 assert(path_is_absolute(abspath));
494
495 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
496 mac_selinux_maybe_reload();
497 if (!label_hnd)
498 return 0;
499
500 r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
501 if (r < 0) {
502 /* No context specified by the policy? Proceed without setting it. */
503 if (errno == ENOENT)
504 return 0;
505
506 return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
507 }
508
509 if (setfscreatecon_raw(filecon) < 0)
510 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
511
512 return 0;
513 }
514 #endif
515
516 int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
517 #if HAVE_SELINUX
518 _cleanup_free_ char *abspath = NULL;
519 int r;
520
521 assert(path);
522
523 if (!label_hnd)
524 return 0;
525
526 if (!path_is_absolute(path)) {
527 if (dirfd == AT_FDCWD)
528 r = safe_getcwd(&abspath);
529 else
530 r = fd_get_path(dirfd, &abspath);
531 if (r < 0)
532 return r;
533
534 if (!path_extend(&abspath, path))
535 return -ENOMEM;
536
537 path = abspath;
538 }
539
540 return selinux_create_file_prepare_abspath(path, mode);
541 #else
542 return 0;
543 #endif
544 }
545
546 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
547 #if HAVE_SELINUX
548 int r;
549
550 _cleanup_free_ char *abspath = NULL;
551
552 assert(path);
553
554 if (!label_hnd)
555 return 0;
556
557 r = path_make_absolute_cwd(path, &abspath);
558 if (r < 0)
559 return r;
560
561 return selinux_create_file_prepare_abspath(abspath, mode);
562 #else
563 return 0;
564 #endif
565 }
566
567 void mac_selinux_create_file_clear(void) {
568
569 #if HAVE_SELINUX
570 PROTECT_ERRNO;
571
572 if (!mac_selinux_use())
573 return;
574
575 setfscreatecon_raw(NULL);
576 #endif
577 }
578
579 int mac_selinux_create_socket_prepare(const char *label) {
580
581 #if HAVE_SELINUX
582 assert(label);
583
584 if (!mac_selinux_use())
585 return 0;
586
587 if (setsockcreatecon(label) < 0)
588 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
589 #endif
590
591 return 0;
592 }
593
594 void mac_selinux_create_socket_clear(void) {
595
596 #if HAVE_SELINUX
597 PROTECT_ERRNO;
598
599 if (!mac_selinux_use())
600 return;
601
602 setsockcreatecon_raw(NULL);
603 #endif
604 }
605
606 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
607
608 /* Binds a socket and label its file system object according to the SELinux policy */
609
610 #if HAVE_SELINUX
611 _cleanup_freecon_ char *fcon = NULL;
612 const struct sockaddr_un *un;
613 bool context_changed = false;
614 char *path;
615 int r;
616
617 assert(fd >= 0);
618 assert(addr);
619 assert(addrlen >= sizeof(sa_family_t));
620
621 if (!label_hnd)
622 goto skipped;
623
624 /* Filter out non-local sockets */
625 if (addr->sa_family != AF_UNIX)
626 goto skipped;
627
628 /* Filter out anonymous sockets */
629 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
630 goto skipped;
631
632 /* Filter out abstract namespace sockets */
633 un = (const struct sockaddr_un*) addr;
634 if (un->sun_path[0] == 0)
635 goto skipped;
636
637 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
638
639 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
640 mac_selinux_maybe_reload();
641 if (!label_hnd)
642 goto skipped;
643
644 if (path_is_absolute(path))
645 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
646 else {
647 _cleanup_free_ char *newpath = NULL;
648
649 r = path_make_absolute_cwd(path, &newpath);
650 if (r < 0)
651 return r;
652
653 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
654 }
655
656 if (r < 0) {
657 /* No context specified by the policy? Proceed without setting it */
658 if (errno == ENOENT)
659 goto skipped;
660
661 r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
662 if (r < 0)
663 return r;
664 } else {
665 if (setfscreatecon_raw(fcon) < 0) {
666 r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
667 if (r < 0)
668 return r;
669 } else
670 context_changed = true;
671 }
672
673 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
674
675 if (context_changed)
676 (void) setfscreatecon_raw(NULL);
677
678 return r;
679
680 skipped:
681 #endif
682 if (bind(fd, addr, addrlen) < 0)
683 return -errno;
684
685 return 0;
686 }