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