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