Branch data 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 [ + + ]: 248 : DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
32 [ - + ]: 8 : 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 : 1000 : bool mac_selinux_use(void) {
45 : : #if HAVE_SELINUX
46 [ + + ]: 1000 : if (cached_use < 0)
47 : 412 : cached_use = is_selinux_enabled() > 0;
48 : :
49 : 1000 : return cached_use;
50 : : #else
51 : : return false;
52 : : #endif
53 : : }
54 : :
55 : 4 : void mac_selinux_retest(void) {
56 : : #if HAVE_SELINUX
57 : 4 : cached_use = -1;
58 : : #endif
59 : 4 : }
60 : :
61 : 340 : int mac_selinux_init(void) {
62 : 340 : int r = 0;
63 : :
64 : : #if HAVE_SELINUX
65 : : usec_t before_timestamp, after_timestamp;
66 : : struct mallinfo before_mallinfo, after_mallinfo;
67 : :
68 [ - + ]: 340 : if (label_hnd)
69 : 0 : return 0;
70 : :
71 [ - + ]: 340 : if (!mac_selinux_use())
72 : 0 : return 0;
73 : :
74 : 340 : before_mallinfo = mallinfo();
75 : 340 : before_timestamp = now(CLOCK_MONOTONIC);
76 : :
77 : 340 : label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
78 [ - + ]: 340 : 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 : 340 : after_timestamp = now(CLOCK_MONOTONIC);
86 : 340 : after_mallinfo = mallinfo();
87 : :
88 [ + - ]: 340 : l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
89 : :
90 [ + + ]: 340 : 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 : 340 : return r;
97 : : }
98 : :
99 : 1128 : void mac_selinux_finish(void) {
100 : :
101 : : #if HAVE_SELINUX
102 [ + + ]: 1128 : if (!label_hnd)
103 : 788 : return;
104 : :
105 : 340 : selabel_close(label_hnd);
106 : 340 : label_hnd = NULL;
107 : : #endif
108 : : }
109 : :
110 : 116 : 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 : 116 : _cleanup_freecon_ char* fcon = NULL;
115 : 116 : _cleanup_close_ int fd = -1;
116 : : struct stat st;
117 : : int r;
118 : :
119 [ - + ]: 116 : assert(path);
120 : :
121 : : /* if mac_selinux_init() wasn't called before we are a NOOP */
122 [ - + ]: 116 : 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 : 116 : fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
127 [ - + ]: 116 : if (fd < 0) {
128 [ # # # # ]: 0 : if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
129 : 0 : return 0;
130 : :
131 : 0 : return -errno;
132 : : }
133 : :
134 [ - + ]: 116 : if (fstat(fd, &st) < 0)
135 : 0 : return -errno;
136 : :
137 [ + - ]: 116 : if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
138 : 116 : r = -errno;
139 : :
140 : : /* If there's no label to set, then exit without warning */
141 [ + - ]: 116 : if (r == -ENOENT)
142 : 116 : 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 : 4 : int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
198 : 4 : int r = -EOPNOTSUPP;
199 : :
200 : : #if HAVE_SELINUX
201 : 4 : _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
202 : : security_class_t sclass;
203 : :
204 [ - + ]: 4 : assert(exe);
205 [ - + ]: 4 : assert(label);
206 : :
207 [ - + ]: 4 : if (!mac_selinux_use())
208 : 0 : return -EOPNOTSUPP;
209 : :
210 : 4 : r = getcon_raw(&mycon);
211 [ - + ]: 4 : if (r < 0)
212 : 0 : return -errno;
213 : :
214 : 4 : r = getfilecon_raw(exe, &fcon);
215 [ - + ]: 4 : if (r < 0)
216 : 0 : return -errno;
217 : :
218 : 4 : sclass = string_to_security_class("process");
219 : 4 : r = security_compute_create_raw(mycon, fcon, sclass, label);
220 [ - + ]: 4 : if (r < 0)
221 : 0 : return -errno;
222 : : #endif
223 : :
224 : 4 : return r;
225 : : }
226 : :
227 : 4 : int mac_selinux_get_our_label(char **label) {
228 : 4 : int r = -EOPNOTSUPP;
229 : :
230 [ - + ]: 4 : assert(label);
231 : :
232 : : #if HAVE_SELINUX
233 [ - + ]: 4 : if (!mac_selinux_use())
234 : 0 : return -EOPNOTSUPP;
235 : :
236 : 4 : r = getcon_raw(label);
237 [ - + ]: 4 : if (r < 0)
238 : 0 : return -errno;
239 : : #endif
240 : :
241 : 4 : return r;
242 : : }
243 : :
244 : 4 : int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
245 : 4 : int r = -EOPNOTSUPP;
246 : :
247 : : #if HAVE_SELINUX
248 : 4 : _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
249 : 4 : _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
250 : : security_class_t sclass;
251 : 4 : const char *range = NULL;
252 : :
253 [ - + ]: 4 : assert(socket_fd >= 0);
254 [ - + ]: 4 : assert(exe);
255 [ - + ]: 4 : assert(label);
256 : :
257 [ - + ]: 4 : if (!mac_selinux_use())
258 : 0 : return -EOPNOTSUPP;
259 : :
260 : 4 : r = getcon_raw(&mycon);
261 [ - + ]: 4 : if (r < 0)
262 : 0 : return -errno;
263 : :
264 : 4 : r = getpeercon_raw(socket_fd, &peercon);
265 [ + - ]: 4 : if (r < 0)
266 : 4 : 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 : 8 : char* mac_selinux_free(char *label) {
307 : :
308 : : #if HAVE_SELINUX
309 [ - + ]: 8 : if (!label)
310 : 0 : return NULL;
311 : :
312 [ - + ]: 8 : if (!mac_selinux_use())
313 : 0 : return NULL;
314 : :
315 : 8 : freecon(label);
316 : : #endif
317 : :
318 : 8 : return NULL;
319 : : }
320 : :
321 : : #if HAVE_SELINUX
322 : 112 : static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
323 : 112 : _cleanup_freecon_ char *filecon = NULL;
324 : : int r;
325 : :
326 [ - + ]: 112 : assert(abspath);
327 [ - + ]: 112 : assert(path_is_absolute(abspath));
328 : :
329 : 112 : r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
330 [ + + ]: 112 : if (r < 0) {
331 : : /* No context specified by the policy? Proceed without setting it. */
332 [ + - ]: 108 : if (errno == ENOENT)
333 : 108 : return 0;
334 : :
335 [ # # # # ]: 0 : log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
336 : : } else {
337 [ + - ]: 4 : if (setfscreatecon_raw(filecon) >= 0)
338 : 4 : 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 : 612 : int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
382 : 612 : int r = 0;
383 : :
384 : : #if HAVE_SELINUX
385 : 612 : _cleanup_free_ char *abspath = NULL;
386 : :
387 [ - + ]: 612 : assert(path);
388 : :
389 [ + + ]: 612 : if (!label_hnd)
390 : 500 : return 0;
391 : :
392 : 112 : r = path_make_absolute_cwd(path, &abspath);
393 [ - + ]: 112 : if (r < 0)
394 : 0 : return r;
395 : :
396 : 112 : r = selinux_create_file_prepare_abspath(abspath, mode);
397 : : #endif
398 : 112 : return r;
399 : : }
400 : :
401 : 612 : void mac_selinux_create_file_clear(void) {
402 : :
403 : : #if HAVE_SELINUX
404 [ + - ]: 612 : PROTECT_ERRNO;
405 : :
406 [ - + ]: 612 : if (!mac_selinux_use())
407 : 0 : return;
408 : :
409 : 612 : 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 : : }
|