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