]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/selinux-util.c
resolve: voidify sd_event_add_signal() and sd_event_set_watchdog()
[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
319int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
66b6d9d5 320
349cc4a5 321#if HAVE_SELINUX
2ed96880 322 _cleanup_freecon_ char *filecon = NULL;
0f474365 323 int r;
66b6d9d5 324
ecabcf8b
LP
325 assert(path);
326
66cedb30 327 if (!label_hnd)
66b6d9d5
WC
328 return 0;
329
c34255bd
LP
330 if (path_is_absolute(path))
331 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
332 else {
0f474365 333 _cleanup_free_ char *newpath = NULL;
c34255bd 334
0f474365
LP
335 r = path_make_absolute_cwd(path, &newpath);
336 if (r < 0)
337 return r;
c34255bd 338
a07e9cfb 339 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
c34255bd
LP
340 }
341
0f474365
LP
342 if (r < 0) {
343 /* No context specified by the policy? Proceed without setting it. */
344 if (errno == ENOENT)
345 return 0;
2d58aa46 346
b8b846d7 347 log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
0f474365 348 } else {
5c5433ad 349 if (setfscreatecon_raw(filecon) >= 0)
0f474365
LP
350 return 0; /* Success! */
351
b8b846d7 352 log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
66b6d9d5
WC
353 }
354
0f474365
LP
355 if (security_getenforce() > 0)
356 return -errno;
66b6d9d5 357
0f474365
LP
358#endif
359 return 0;
66b6d9d5
WC
360}
361
ecabcf8b 362void mac_selinux_create_file_clear(void) {
66b6d9d5 363
349cc4a5 364#if HAVE_SELINUX
66b6d9d5
WC
365 PROTECT_ERRNO;
366
6baa7db0 367 if (!mac_selinux_use())
66b6d9d5
WC
368 return;
369
a1d2de07 370 setfscreatecon_raw(NULL);
66b6d9d5
WC
371#endif
372}
373
ecabcf8b 374int mac_selinux_create_socket_prepare(const char *label) {
66b6d9d5 375
349cc4a5 376#if HAVE_SELINUX
6baa7db0 377 if (!mac_selinux_use())
ecabcf8b 378 return 0;
66b6d9d5 379
ecabcf8b
LP
380 assert(label);
381
2ed96880 382 if (setsockcreatecon(label) < 0) {
b8b846d7 383 log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
ecabcf8b
LP
384
385 if (security_getenforce() == 1)
386 return -errno;
387 }
66b6d9d5 388#endif
ecabcf8b
LP
389
390 return 0;
66b6d9d5
WC
391}
392
ecabcf8b 393void mac_selinux_create_socket_clear(void) {
66b6d9d5 394
349cc4a5 395#if HAVE_SELINUX
ecabcf8b
LP
396 PROTECT_ERRNO;
397
6baa7db0 398 if (!mac_selinux_use())
66b6d9d5
WC
399 return;
400
a1d2de07 401 setsockcreatecon_raw(NULL);
66b6d9d5
WC
402#endif
403}
404
cc56fafe 405int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
66b6d9d5
WC
406
407 /* Binds a socket and label its file system object according to the SELinux policy */
408
349cc4a5 409#if HAVE_SELINUX
2ed96880 410 _cleanup_freecon_ char *fcon = NULL;
66b6d9d5 411 const struct sockaddr_un *un;
0f474365 412 bool context_changed = false;
66b6d9d5
WC
413 char *path;
414 int r;
415
416 assert(fd >= 0);
417 assert(addr);
418 assert(addrlen >= sizeof(sa_family_t));
419
ecabcf8b 420 if (!label_hnd)
66b6d9d5
WC
421 goto skipped;
422
423 /* Filter out non-local sockets */
424 if (addr->sa_family != AF_UNIX)
425 goto skipped;
426
427 /* Filter out anonymous sockets */
0f474365 428 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
66b6d9d5
WC
429 goto skipped;
430
431 /* Filter out abstract namespace sockets */
432 un = (const struct sockaddr_un*) addr;
433 if (un->sun_path[0] == 0)
434 goto skipped;
435
436 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
437
438 if (path_is_absolute(path))
439 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
440 else {
0f474365 441 _cleanup_free_ char *newpath = NULL;
66b6d9d5 442
0f474365
LP
443 r = path_make_absolute_cwd(path, &newpath);
444 if (r < 0)
445 return r;
66b6d9d5
WC
446
447 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
448 }
449
0f474365
LP
450 if (r < 0) {
451 /* No context specified by the policy? Proceed without setting it */
452 if (errno == ENOENT)
453 goto skipped;
66b6d9d5 454
b8b846d7 455 log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
0f474365
LP
456 if (security_getenforce() > 0)
457 return -errno;
66b6d9d5 458
0f474365 459 } else {
a1d2de07 460 if (setfscreatecon_raw(fcon) < 0) {
b8b846d7 461 log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
0f474365
LP
462 if (security_getenforce() > 0)
463 return -errno;
464 } else
465 context_changed = true;
66b6d9d5
WC
466 }
467
0f474365
LP
468 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
469
470 if (context_changed)
a1d2de07 471 setfscreatecon_raw(NULL);
66b6d9d5 472
66b6d9d5
WC
473 return r;
474
475skipped:
476#endif
0f474365
LP
477 if (bind(fd, addr, addrlen) < 0)
478 return -errno;
479
480 return 0;
66b6d9d5 481}