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