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