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