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