]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/selinux-util.c
hwdb: add another Logitech G502 Hero variant (#16256)
[thirdparty/systemd.git] / src / basic / selinux-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
cad45ba1 2
66b6d9d5 3#include <errno.h>
ca78ad1d 4#include <fcntl.h>
66b6d9d5 5#include <malloc.h>
11c3a366
TA
6#include <stddef.h>
7#include <string.h>
8#include <sys/stat.h>
9#include <sys/time.h>
ca78ad1d 10#include <sys/types.h>
66b6d9d5 11#include <sys/un.h>
11c3a366 12#include <syslog.h>
a07e9cfb 13
349cc4a5 14#if HAVE_SELINUX
61f3e897 15#include <selinux/avc.h>
66b6d9d5 16#include <selinux/context.h>
cf0fbc49
TA
17#include <selinux/label.h>
18#include <selinux/selinux.h>
66b6d9d5
WC
19#endif
20
b5efdb8a 21#include "alloc-util.h"
2b2fec7d 22#include "errno-util.h"
08c84981 23#include "fd-util.h"
11c3a366
TA
24#include "log.h"
25#include "macro.h"
93cc7779
TA
26#include "path-util.h"
27#include "selinux-util.h"
08c84981 28#include "stdio-util.h"
11c3a366 29#include "time-util.h"
cad45ba1 30
349cc4a5 31#if HAVE_SELINUX
66b6d9d5 32DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
66b6d9d5 33#define _cleanup_context_free_ _cleanup_(context_freep)
0b6018f3 34
61f3e897
CG
35static int mac_selinux_reload(int seqno);
36
6baa7db0 37static int cached_use = -1;
257188f8 38static int cached_enforcing = -1;
66b6d9d5 39static struct selabel_handle *label_hnd = NULL;
66cedb30 40
257188f8 41#define log_enforcing(...) log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
65804d6a
LP
42
43#define log_enforcing_errno(error, ...) \
44 ({ \
45 bool _enforcing = mac_selinux_enforcing(); \
46 int _level = _enforcing ? LOG_ERR : LOG_WARNING; \
47 int _e = (error); \
48 \
49 int _r = (log_get_max_level() >= LOG_PRI(_level)) \
50 ? log_internal_realm(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
51 : -ERRNO_VALUE(_e); \
52 _enforcing ? _r : 0; \
53 })
66b6d9d5 54#endif
cad45ba1 55
6d395665 56bool mac_selinux_use(void) {
349cc4a5 57#if HAVE_SELINUX
2073bf3f 58 if (_unlikely_(cached_use < 0)) {
6baa7db0 59 cached_use = is_selinux_enabled() > 0;
2073bf3f
CG
60 log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
61 }
cad45ba1 62
6baa7db0 63 return cached_use;
66b6d9d5
WC
64#else
65 return false;
66#endif
cad45ba1
LP
67}
68
257188f8
CG
69bool mac_selinux_enforcing(void) {
70#if HAVE_SELINUX
c8aa389c 71 if (_unlikely_(cached_enforcing < 0)) {
257188f8 72 cached_enforcing = security_getenforce();
65804d6a
LP
73 if (cached_enforcing < 0) {
74 log_debug_errno(errno, "Failed to get SELinux enforced status, continuing in enforcing mode: %m");
75 return true; /* treat failure as enforcing mode */
76 }
77
78 log_debug("SELinux enforcing state cached to: %s", cached_enforcing ? "enforcing" : "permissive");
257188f8
CG
79 }
80
65804d6a 81 return cached_enforcing > 0;
257188f8
CG
82#else
83 return false;
84#endif
85}
86
6baa7db0 87void mac_selinux_retest(void) {
349cc4a5 88#if HAVE_SELINUX
6baa7db0 89 cached_use = -1;
257188f8 90 cached_enforcing = -1;
66b6d9d5 91#endif
cad45ba1 92}
0b6018f3 93
257188f8
CG
94#if HAVE_SELINUX
95static int setenforce_callback(int enforcing) {
96 cached_enforcing = enforcing;
97
2073bf3f
CG
98 log_debug("SELinux enforcing state updated to: %s", cached_enforcing ? "enforcing" : "permissive");
99
257188f8
CG
100 return 0;
101}
102#endif
103
c3dacc8b 104int mac_selinux_init(void) {
349cc4a5 105#if HAVE_SELINUX
66b6d9d5
WC
106 usec_t before_timestamp, after_timestamp;
107 struct mallinfo before_mallinfo, after_mallinfo;
65804d6a
LP
108 char timespan[FORMAT_TIMESPAN_MAX];
109 int l;
66b6d9d5 110
61f3e897 111 selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
257188f8 112 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) setenforce_callback);
61f3e897 113
c3dacc8b 114 if (label_hnd)
66b6d9d5
WC
115 return 0;
116
c3dacc8b 117 if (!mac_selinux_use())
66b6d9d5
WC
118 return 0;
119
120 before_mallinfo = mallinfo();
121 before_timestamp = now(CLOCK_MONOTONIC);
122
c3dacc8b 123 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
65804d6a
LP
124 if (!label_hnd)
125 return log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
66b6d9d5 126
65804d6a
LP
127 after_timestamp = now(CLOCK_MONOTONIC);
128 after_mallinfo = mallinfo();
66b6d9d5 129
65804d6a 130 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
66b6d9d5 131
65804d6a
LP
132 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
133 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
134 (l+1023)/1024);
66b6d9d5 135
65804d6a
LP
136#endif
137 return 0;
d682b3a7
LP
138}
139
ecabcf8b
LP
140void mac_selinux_finish(void) {
141
349cc4a5 142#if HAVE_SELINUX
ecabcf8b
LP
143 if (!label_hnd)
144 return;
145
146 selabel_close(label_hnd);
f5ce2b49 147 label_hnd = NULL;
ecabcf8b
LP
148#endif
149}
150
a9dfac21 151#if HAVE_SELINUX
61f3e897 152static int mac_selinux_reload(int seqno) {
a9dfac21
CG
153 struct selabel_handle *backup_label_hnd;
154
155 if (!label_hnd)
61f3e897 156 return 0;
a9dfac21
CG
157
158 backup_label_hnd = TAKE_PTR(label_hnd);
159
160 /* try to initialize new handle
161 * on success close backup
162 * on failure restore backup */
163 if (mac_selinux_init() == 0)
164 selabel_close(backup_label_hnd);
165 else
166 label_hnd = backup_label_hnd;
61f3e897
CG
167
168 return 0;
a9dfac21 169}
61f3e897 170#endif
a9dfac21 171
c3151977 172int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
66b6d9d5 173
349cc4a5 174#if HAVE_SELINUX
08c84981
LP
175 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
176 _cleanup_freecon_ char* fcon = NULL;
177 _cleanup_close_ int fd = -1;
66b6d9d5 178 struct stat st;
ecabcf8b 179 int r;
66b6d9d5 180
5dfc5461
LP
181 assert(path);
182
183 /* if mac_selinux_init() wasn't called before we are a NOOP */
66b6d9d5
WC
184 if (!label_hnd)
185 return 0;
186
08c84981
LP
187 /* Open the file as O_PATH, to pin it while we determine and adjust the label */
188 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
189 if (fd < 0) {
190 if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
191 return 0;
192
193 return -errno;
194 }
195
196 if (fstat(fd, &st) < 0)
197 return -errno;
5dfc5461 198
61f3e897
CG
199 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
200 (void) avc_netlink_check_nb();
201
c3151977 202 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) {
08c84981 203 r = -errno;
66b6d9d5
WC
204
205 /* If there's no label to set, then exit without warning */
08c84981 206 if (r == -ENOENT)
66b6d9d5
WC
207 return 0;
208
08c84981 209 goto fail;
66b6d9d5
WC
210 }
211
08c84981
LP
212 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
213 if (setfilecon_raw(procfs_path, fcon) < 0) {
214 _cleanup_freecon_ char *oldcon = NULL;
215
216 r = -errno;
217
218 /* If the FS doesn't support labels, then exit without warning */
219 if (r == -EOPNOTSUPP)
66b6d9d5
WC
220 return 0;
221
08c84981
LP
222 /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
223 if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
66b6d9d5
WC
224 return 0;
225
08c84981
LP
226 /* If the old label is identical to the new one, suppress any kind of error */
227 if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
228 return 0;
229
230 goto fail;
66b6d9d5 231 }
08c84981
LP
232
233 return 0;
234
235fail:
65804d6a 236 return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", path, inside_path);
66b6d9d5
WC
237#endif
238
ecabcf8b 239 return 0;
66b6d9d5
WC
240}
241
ecabcf8b 242int mac_selinux_apply(const char *path, const char *label) {
66b6d9d5 243
349cc4a5 244#if HAVE_SELINUX
ecabcf8b
LP
245 if (!mac_selinux_use())
246 return 0;
247
0f474365
LP
248 assert(path);
249 assert(label);
250
65804d6a
LP
251 if (setfilecon(path, label) < 0)
252 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
66b6d9d5 253#endif
ecabcf8b 254 return 0;
d682b3a7
LP
255}
256
cc56fafe 257int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
349cc4a5 258#if HAVE_SELINUX
2ed96880 259 _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
66b6d9d5 260 security_class_t sclass;
65804d6a 261 int r;
66b6d9d5 262
7f416dae
LP
263 assert(exe);
264 assert(label);
265
6d395665 266 if (!mac_selinux_use())
7f416dae 267 return -EOPNOTSUPP;
66b6d9d5 268
24154879 269 r = getcon_raw(&mycon);
66b6d9d5 270 if (r < 0)
7f416dae 271 return -errno;
66b6d9d5 272
24154879 273 r = getfilecon_raw(exe, &fcon);
66b6d9d5 274 if (r < 0)
7f416dae 275 return -errno;
66b6d9d5
WC
276
277 sclass = string_to_security_class("process");
fdb0405e
CG
278 if (sclass == 0)
279 return -ENOSYS;
280
2ed96880 281 r = security_compute_create_raw(mycon, fcon, sclass, label);
7f416dae
LP
282 if (r < 0)
283 return -errno;
66b6d9d5 284
65804d6a
LP
285 return 0;
286#else
287 return -EOPNOTSUPP;
288#endif
66b6d9d5
WC
289}
290
cc56fafe 291int mac_selinux_get_our_label(char **label) {
65804d6a
LP
292#if HAVE_SELINUX
293 int r;
66b6d9d5 294
7f416dae
LP
295 assert(label);
296
6d395665 297 if (!mac_selinux_use())
7f416dae 298 return -EOPNOTSUPP;
66b6d9d5 299
24154879 300 r = getcon_raw(label);
66b6d9d5 301 if (r < 0)
7f416dae 302 return -errno;
66b6d9d5 303
65804d6a
LP
304 return 0;
305#else
306 return -EOPNOTSUPP;
307#endif
66b6d9d5
WC
308}
309
9008e1ac 310int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
349cc4a5 311#if HAVE_SELINUX
2ed96880 312 _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
66b6d9d5
WC
313 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
314 security_class_t sclass;
66b6d9d5 315 const char *range = NULL;
65804d6a 316 int r;
66b6d9d5
WC
317
318 assert(socket_fd >= 0);
319 assert(exe);
320 assert(label);
321
6d395665 322 if (!mac_selinux_use())
7f416dae
LP
323 return -EOPNOTSUPP;
324
24154879 325 r = getcon_raw(&mycon);
7f416dae
LP
326 if (r < 0)
327 return -errno;
66b6d9d5 328
a1d2de07 329 r = getpeercon_raw(socket_fd, &peercon);
7f416dae
LP
330 if (r < 0)
331 return -errno;
66b6d9d5 332
9008e1ac 333 if (!exec_label) {
66b6d9d5
WC
334 /* If there is no context set for next exec let's use context
335 of target executable */
24154879 336 r = getfilecon_raw(exe, &fcon);
7f416dae
LP
337 if (r < 0)
338 return -errno;
66b6d9d5
WC
339 }
340
341 bcon = context_new(mycon);
7f416dae
LP
342 if (!bcon)
343 return -ENOMEM;
66b6d9d5
WC
344
345 pcon = context_new(peercon);
7f416dae
LP
346 if (!pcon)
347 return -ENOMEM;
66b6d9d5
WC
348
349 range = context_range_get(pcon);
7f416dae
LP
350 if (!range)
351 return -errno;
66b6d9d5
WC
352
353 r = context_range_set(bcon, range);
7f416dae
LP
354 if (r)
355 return -errno;
66b6d9d5
WC
356
357 freecon(mycon);
358 mycon = strdup(context_str(bcon));
7f416dae
LP
359 if (!mycon)
360 return -ENOMEM;
66b6d9d5
WC
361
362 sclass = string_to_security_class("process");
fdb0405e
CG
363 if (sclass == 0)
364 return -ENOSYS;
365
2ed96880 366 r = security_compute_create_raw(mycon, fcon, sclass, label);
7f416dae
LP
367 if (r < 0)
368 return -errno;
7f416dae 369
65804d6a
LP
370 return 0;
371#else
372 return -EOPNOTSUPP;
373#endif
66b6d9d5
WC
374}
375
710a6b50 376char* mac_selinux_free(char *label) {
ecabcf8b 377
349cc4a5 378#if HAVE_SELINUX
710a6b50
LP
379 if (!label)
380 return NULL;
381
6d395665 382 if (!mac_selinux_use())
710a6b50
LP
383 return NULL;
384
2ed96880 385 freecon(label);
ecabcf8b 386#endif
710a6b50
LP
387
388 return NULL;
ecabcf8b
LP
389}
390
349cc4a5 391#if HAVE_SELINUX
7e531a52 392static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
2ed96880 393 _cleanup_freecon_ char *filecon = NULL;
0f474365 394 int r;
66b6d9d5 395
7e531a52
FB
396 assert(abspath);
397 assert(path_is_absolute(abspath));
c34255bd 398
61f3e897
CG
399 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
400 (void) avc_netlink_check_nb();
401
7e531a52 402 r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
0f474365
LP
403 if (r < 0) {
404 /* No context specified by the policy? Proceed without setting it. */
405 if (errno == ENOENT)
406 return 0;
2d58aa46 407
65804d6a 408 return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
66b6d9d5
WC
409 }
410
65804d6a
LP
411 if (setfscreatecon_raw(filecon) < 0)
412 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
66b6d9d5 413
0f474365 414 return 0;
66b6d9d5 415}
7e531a52
FB
416#endif
417
418int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
7e531a52
FB
419#if HAVE_SELINUX
420 _cleanup_free_ char *abspath = NULL;
65804d6a
LP
421 int r;
422
7e531a52
FB
423
424 assert(path);
425
426 if (!label_hnd)
427 return 0;
428
429 if (!path_is_absolute(path)) {
430 _cleanup_free_ char *p = NULL;
431
432 if (dirfd == AT_FDCWD)
433 r = safe_getcwd(&p);
434 else
435 r = fd_get_path(dirfd, &p);
436 if (r < 0)
437 return r;
438
62a85ee0 439 path = abspath = path_join(p, path);
30016f21 440 if (!path)
7e531a52 441 return -ENOMEM;
7e531a52
FB
442 }
443
65804d6a
LP
444 return selinux_create_file_prepare_abspath(path, mode);
445#else
446 return 0;
7e531a52 447#endif
7e531a52
FB
448}
449
450int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
7e531a52 451#if HAVE_SELINUX
65804d6a
LP
452 int r;
453
7e531a52
FB
454 _cleanup_free_ char *abspath = NULL;
455
456 assert(path);
457
458 if (!label_hnd)
459 return 0;
460
461 r = path_make_absolute_cwd(path, &abspath);
462 if (r < 0)
463 return r;
464
65804d6a
LP
465 return selinux_create_file_prepare_abspath(abspath, mode);
466#else
467 return 0;
7e531a52 468#endif
7e531a52 469}
66b6d9d5 470
ecabcf8b 471void mac_selinux_create_file_clear(void) {
66b6d9d5 472
349cc4a5 473#if HAVE_SELINUX
66b6d9d5
WC
474 PROTECT_ERRNO;
475
6baa7db0 476 if (!mac_selinux_use())
66b6d9d5
WC
477 return;
478
a1d2de07 479 setfscreatecon_raw(NULL);
66b6d9d5
WC
480#endif
481}
482
ecabcf8b 483int mac_selinux_create_socket_prepare(const char *label) {
66b6d9d5 484
349cc4a5 485#if HAVE_SELINUX
ecabcf8b
LP
486 assert(label);
487
65804d6a
LP
488 if (!mac_selinux_use())
489 return 0;
ecabcf8b 490
65804d6a
LP
491 if (setsockcreatecon(label) < 0)
492 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
66b6d9d5 493#endif
ecabcf8b
LP
494
495 return 0;
66b6d9d5
WC
496}
497
ecabcf8b 498void mac_selinux_create_socket_clear(void) {
66b6d9d5 499
349cc4a5 500#if HAVE_SELINUX
ecabcf8b
LP
501 PROTECT_ERRNO;
502
6baa7db0 503 if (!mac_selinux_use())
66b6d9d5
WC
504 return;
505
a1d2de07 506 setsockcreatecon_raw(NULL);
66b6d9d5
WC
507#endif
508}
509
cc56fafe 510int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
66b6d9d5
WC
511
512 /* Binds a socket and label its file system object according to the SELinux policy */
513
349cc4a5 514#if HAVE_SELINUX
2ed96880 515 _cleanup_freecon_ char *fcon = NULL;
66b6d9d5 516 const struct sockaddr_un *un;
0f474365 517 bool context_changed = false;
66b6d9d5
WC
518 char *path;
519 int r;
520
521 assert(fd >= 0);
522 assert(addr);
523 assert(addrlen >= sizeof(sa_family_t));
524
ecabcf8b 525 if (!label_hnd)
66b6d9d5
WC
526 goto skipped;
527
528 /* Filter out non-local sockets */
529 if (addr->sa_family != AF_UNIX)
530 goto skipped;
531
532 /* Filter out anonymous sockets */
0f474365 533 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
66b6d9d5
WC
534 goto skipped;
535
536 /* Filter out abstract namespace sockets */
537 un = (const struct sockaddr_un*) addr;
538 if (un->sun_path[0] == 0)
539 goto skipped;
540
541 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
542
61f3e897
CG
543 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
544 (void) avc_netlink_check_nb();
545
66b6d9d5
WC
546 if (path_is_absolute(path))
547 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
548 else {
0f474365 549 _cleanup_free_ char *newpath = NULL;
66b6d9d5 550
0f474365
LP
551 r = path_make_absolute_cwd(path, &newpath);
552 if (r < 0)
553 return r;
66b6d9d5
WC
554
555 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
556 }
557
0f474365
LP
558 if (r < 0) {
559 /* No context specified by the policy? Proceed without setting it */
560 if (errno == ENOENT)
561 goto skipped;
66b6d9d5 562
65804d6a
LP
563 r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
564 if (r < 0)
565 return r;
0f474365 566 } else {
a1d2de07 567 if (setfscreatecon_raw(fcon) < 0) {
65804d6a
LP
568 r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
569 if (r < 0)
570 return r;
0f474365
LP
571 } else
572 context_changed = true;
66b6d9d5
WC
573 }
574
0f474365
LP
575 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
576
577 if (context_changed)
65804d6a 578 (void) setfscreatecon_raw(NULL);
66b6d9d5 579
66b6d9d5
WC
580 return r;
581
582skipped:
583#endif
0f474365
LP
584 if (bind(fd, addr, addrlen) < 0)
585 return -errno;
586
587 return 0;
66b6d9d5 588}