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