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