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