]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/selinux-util.c
Merge pull request #15891 from bluca/host_os_release
[thirdparty/systemd.git] / src / basic / selinux-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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(context_t, context_free);
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 int cached_enforcing = -1;
39 static struct selabel_handle *label_hnd = NULL;
40
41 #define log_enforcing(...) log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
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 })
54 #endif
55
56 bool mac_selinux_use(void) {
57 #if HAVE_SELINUX
58 if (_unlikely_(cached_use < 0)) {
59 cached_use = is_selinux_enabled() > 0;
60 log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
61 }
62
63 return cached_use;
64 #else
65 return false;
66 #endif
67 }
68
69 bool mac_selinux_enforcing(void) {
70 #if HAVE_SELINUX
71 if (_unlikely_(cached_enforcing < 0)) {
72 cached_enforcing = security_getenforce();
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");
79 }
80
81 return cached_enforcing > 0;
82 #else
83 return false;
84 #endif
85 }
86
87 void mac_selinux_retest(void) {
88 #if HAVE_SELINUX
89 cached_use = -1;
90 cached_enforcing = -1;
91 #endif
92 }
93
94 #if HAVE_SELINUX
95 static int setenforce_callback(int enforcing) {
96 cached_enforcing = enforcing;
97
98 log_debug("SELinux enforcing state updated to: %s", cached_enforcing ? "enforcing" : "permissive");
99
100 return 0;
101 }
102 #endif
103
104 int mac_selinux_init(void) {
105 #if HAVE_SELINUX
106 usec_t before_timestamp, after_timestamp;
107 struct mallinfo before_mallinfo, after_mallinfo;
108 char timespan[FORMAT_TIMESPAN_MAX];
109 int l;
110
111 selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
112 selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) setenforce_callback);
113
114 if (label_hnd)
115 return 0;
116
117 if (!mac_selinux_use())
118 return 0;
119
120 before_mallinfo = mallinfo();
121 before_timestamp = now(CLOCK_MONOTONIC);
122
123 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
124 if (!label_hnd)
125 return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m");
126
127 after_timestamp = now(CLOCK_MONOTONIC);
128 after_mallinfo = mallinfo();
129
130 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
131
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);
135
136 #endif
137 return 0;
138 }
139
140 void mac_selinux_finish(void) {
141
142 #if HAVE_SELINUX
143 if (!label_hnd)
144 return;
145
146 selabel_close(label_hnd);
147 label_hnd = NULL;
148 #endif
149 }
150
151 #if HAVE_SELINUX
152 static int mac_selinux_reload(int seqno) {
153 struct selabel_handle *backup_label_hnd;
154
155 if (!label_hnd)
156 return 0;
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;
167
168 return 0;
169 }
170 #endif
171
172 int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
173
174 #if HAVE_SELINUX
175 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
176 _cleanup_freecon_ char* fcon = NULL;
177 _cleanup_close_ int fd = -1;
178 struct stat st;
179 int r;
180
181 assert(path);
182
183 /* if mac_selinux_init() wasn't called before we are a NOOP */
184 if (!label_hnd)
185 return 0;
186
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;
198
199 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
200 (void) avc_netlink_check_nb();
201
202 if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) {
203 r = -errno;
204
205 /* If there's no label to set, then exit without warning */
206 if (r == -ENOENT)
207 return 0;
208
209 goto fail;
210 }
211
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)
220 return 0;
221
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))
224 return 0;
225
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;
231 }
232
233 return 0;
234
235 fail:
236 return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", path, inside_path);
237 #endif
238
239 return 0;
240 }
241
242 int mac_selinux_apply(const char *path, const char *label) {
243
244 #if HAVE_SELINUX
245 if (!mac_selinux_use())
246 return 0;
247
248 assert(path);
249 assert(label);
250
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);
253 #endif
254 return 0;
255 }
256
257 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
258 #if HAVE_SELINUX
259 _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
260 security_class_t sclass;
261 int r;
262
263 assert(exe);
264 assert(label);
265
266 if (!mac_selinux_use())
267 return -EOPNOTSUPP;
268
269 r = getcon_raw(&mycon);
270 if (r < 0)
271 return -errno;
272
273 r = getfilecon_raw(exe, &fcon);
274 if (r < 0)
275 return -errno;
276
277 sclass = string_to_security_class("process");
278 if (sclass == 0)
279 return -ENOSYS;
280
281 r = security_compute_create_raw(mycon, fcon, sclass, label);
282 if (r < 0)
283 return -errno;
284
285 return 0;
286 #else
287 return -EOPNOTSUPP;
288 #endif
289 }
290
291 int mac_selinux_get_our_label(char **label) {
292 #if HAVE_SELINUX
293 int r;
294
295 assert(label);
296
297 if (!mac_selinux_use())
298 return -EOPNOTSUPP;
299
300 r = getcon_raw(label);
301 if (r < 0)
302 return -errno;
303
304 return 0;
305 #else
306 return -EOPNOTSUPP;
307 #endif
308 }
309
310 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
311 #if HAVE_SELINUX
312 _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
313 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
314 security_class_t sclass;
315 const char *range = NULL;
316 int r;
317
318 assert(socket_fd >= 0);
319 assert(exe);
320 assert(label);
321
322 if (!mac_selinux_use())
323 return -EOPNOTSUPP;
324
325 r = getcon_raw(&mycon);
326 if (r < 0)
327 return -errno;
328
329 r = getpeercon_raw(socket_fd, &peercon);
330 if (r < 0)
331 return -errno;
332
333 if (!exec_label) {
334 /* If there is no context set for next exec let's use context
335 of target executable */
336 r = getfilecon_raw(exe, &fcon);
337 if (r < 0)
338 return -errno;
339 }
340
341 bcon = context_new(mycon);
342 if (!bcon)
343 return -ENOMEM;
344
345 pcon = context_new(peercon);
346 if (!pcon)
347 return -ENOMEM;
348
349 range = context_range_get(pcon);
350 if (!range)
351 return -errno;
352
353 r = context_range_set(bcon, range);
354 if (r)
355 return -errno;
356
357 freecon(mycon);
358 mycon = strdup(context_str(bcon));
359 if (!mycon)
360 return -ENOMEM;
361
362 sclass = string_to_security_class("process");
363 if (sclass == 0)
364 return -ENOSYS;
365
366 r = security_compute_create_raw(mycon, fcon, sclass, label);
367 if (r < 0)
368 return -errno;
369
370 return 0;
371 #else
372 return -EOPNOTSUPP;
373 #endif
374 }
375
376 char* mac_selinux_free(char *label) {
377
378 #if HAVE_SELINUX
379 freecon(label);
380 #else
381 assert(!label);
382 #endif
383
384 return NULL;
385 }
386
387 #if HAVE_SELINUX
388 static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
389 _cleanup_freecon_ char *filecon = NULL;
390 int r;
391
392 assert(abspath);
393 assert(path_is_absolute(abspath));
394
395 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
396 (void) avc_netlink_check_nb();
397
398 r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
399 if (r < 0) {
400 /* No context specified by the policy? Proceed without setting it. */
401 if (errno == ENOENT)
402 return 0;
403
404 return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
405 }
406
407 if (setfscreatecon_raw(filecon) < 0)
408 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
409
410 return 0;
411 }
412 #endif
413
414 int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
415 #if HAVE_SELINUX
416 _cleanup_free_ char *abspath = NULL;
417 int r;
418
419
420 assert(path);
421
422 if (!label_hnd)
423 return 0;
424
425 if (!path_is_absolute(path)) {
426 _cleanup_free_ char *p = NULL;
427
428 if (dirfd == AT_FDCWD)
429 r = safe_getcwd(&p);
430 else
431 r = fd_get_path(dirfd, &p);
432 if (r < 0)
433 return r;
434
435 path = abspath = path_join(p, path);
436 if (!path)
437 return -ENOMEM;
438 }
439
440 return selinux_create_file_prepare_abspath(path, mode);
441 #else
442 return 0;
443 #endif
444 }
445
446 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
447 #if HAVE_SELINUX
448 int r;
449
450 _cleanup_free_ char *abspath = NULL;
451
452 assert(path);
453
454 if (!label_hnd)
455 return 0;
456
457 r = path_make_absolute_cwd(path, &abspath);
458 if (r < 0)
459 return r;
460
461 return selinux_create_file_prepare_abspath(abspath, mode);
462 #else
463 return 0;
464 #endif
465 }
466
467 void mac_selinux_create_file_clear(void) {
468
469 #if HAVE_SELINUX
470 PROTECT_ERRNO;
471
472 if (!mac_selinux_use())
473 return;
474
475 setfscreatecon_raw(NULL);
476 #endif
477 }
478
479 int mac_selinux_create_socket_prepare(const char *label) {
480
481 #if HAVE_SELINUX
482 assert(label);
483
484 if (!mac_selinux_use())
485 return 0;
486
487 if (setsockcreatecon(label) < 0)
488 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
489 #endif
490
491 return 0;
492 }
493
494 void mac_selinux_create_socket_clear(void) {
495
496 #if HAVE_SELINUX
497 PROTECT_ERRNO;
498
499 if (!mac_selinux_use())
500 return;
501
502 setsockcreatecon_raw(NULL);
503 #endif
504 }
505
506 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
507
508 /* Binds a socket and label its file system object according to the SELinux policy */
509
510 #if HAVE_SELINUX
511 _cleanup_freecon_ char *fcon = NULL;
512 const struct sockaddr_un *un;
513 bool context_changed = false;
514 char *path;
515 int r;
516
517 assert(fd >= 0);
518 assert(addr);
519 assert(addrlen >= sizeof(sa_family_t));
520
521 if (!label_hnd)
522 goto skipped;
523
524 /* Filter out non-local sockets */
525 if (addr->sa_family != AF_UNIX)
526 goto skipped;
527
528 /* Filter out anonymous sockets */
529 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
530 goto skipped;
531
532 /* Filter out abstract namespace sockets */
533 un = (const struct sockaddr_un*) addr;
534 if (un->sun_path[0] == 0)
535 goto skipped;
536
537 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
538
539 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
540 (void) avc_netlink_check_nb();
541
542 if (path_is_absolute(path))
543 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
544 else {
545 _cleanup_free_ char *newpath = NULL;
546
547 r = path_make_absolute_cwd(path, &newpath);
548 if (r < 0)
549 return r;
550
551 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
552 }
553
554 if (r < 0) {
555 /* No context specified by the policy? Proceed without setting it */
556 if (errno == ENOENT)
557 goto skipped;
558
559 r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
560 if (r < 0)
561 return r;
562 } else {
563 if (setfscreatecon_raw(fcon) < 0) {
564 r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
565 if (r < 0)
566 return r;
567 } else
568 context_changed = true;
569 }
570
571 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
572
573 if (context_changed)
574 (void) setfscreatecon_raw(NULL);
575
576 return r;
577
578 skipped:
579 #endif
580 if (bind(fd, addr, addrlen) < 0)
581 return -errno;
582
583 return 0;
584 }