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