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