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