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