]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/selinux-util.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / shared / selinux-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
cad45ba1 2
66b6d9d5 3#include <errno.h>
ca78ad1d 4#include <fcntl.h>
66b6d9d5 5#include <malloc.h>
11c3a366
TA
6#include <stddef.h>
7#include <string.h>
8#include <sys/stat.h>
9#include <sys/time.h>
ca78ad1d 10#include <sys/types.h>
66b6d9d5 11#include <sys/un.h>
11c3a366 12#include <syslog.h>
a07e9cfb 13
349cc4a5 14#if HAVE_SELINUX
61f3e897 15#include <selinux/avc.h>
66b6d9d5 16#include <selinux/context.h>
cf0fbc49
TA
17#include <selinux/label.h>
18#include <selinux/selinux.h>
66b6d9d5
WC
19#endif
20
b5efdb8a 21#include "alloc-util.h"
2b2fec7d 22#include "errno-util.h"
08c84981 23#include "fd-util.h"
11c3a366
TA
24#include "log.h"
25#include "macro.h"
93cc7779
TA
26#include "path-util.h"
27#include "selinux-util.h"
08c84981 28#include "stdio-util.h"
11c3a366 29#include "time-util.h"
cad45ba1 30
349cc4a5 31#if HAVE_SELINUX
fd421c4a 32DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(context_t, context_free, NULL);
66b6d9d5 33#define _cleanup_context_free_ _cleanup_(context_freep)
0b6018f3 34
61f3e897
CG
35static int mac_selinux_reload(int seqno);
36
6baa7db0 37static int cached_use = -1;
fd5e402f 38static bool initialized = false;
fd5e402f 39static int last_policyload = 0;
66b6d9d5 40static struct selabel_handle *label_hnd = NULL;
c3b8bacd 41static bool have_status_page = false;
66cedb30 42
3cd44590 43#define log_enforcing(...) \
fd5e402f 44 log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
65804d6a
LP
45
46#define log_enforcing_errno(error, ...) \
47 ({ \
fd5e402f 48 bool _enforcing = mac_selinux_enforcing(); \
65804d6a
LP
49 int _level = _enforcing ? LOG_ERR : LOG_WARNING; \
50 int _e = (error); \
51 \
52 int _r = (log_get_max_level() >= LOG_PRI(_level)) \
10d296c1 53 ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
65804d6a
LP
54 : -ERRNO_VALUE(_e); \
55 _enforcing ? _r : 0; \
56 })
66b6d9d5 57#endif
cad45ba1 58
6d395665 59bool mac_selinux_use(void) {
349cc4a5 60#if HAVE_SELINUX
2073bf3f 61 if (_unlikely_(cached_use < 0)) {
6baa7db0 62 cached_use = is_selinux_enabled() > 0;
2073bf3f
CG
63 log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
64 }
cad45ba1 65
6baa7db0 66 return cached_use;
66b6d9d5
WC
67#else
68 return false;
69#endif
cad45ba1
LP
70}
71
fd5e402f 72bool mac_selinux_enforcing(void) {
c3b8bacd 73 int r = 0;
fd5e402f 74#if HAVE_SELINUX
c3b8bacd
ZJS
75
76 /* If the SELinux status page has been successfully opened, retrieve the enforcing
77 * status over it to avoid system calls in security_getenforce(). */
78
79 if (have_status_page)
80 r = selinux_status_getenforce();
81 else
82 r = security_getenforce();
83
fd5e402f 84#endif
c3b8bacd 85 return r != 0;
fd5e402f
CG
86}
87
6baa7db0 88void mac_selinux_retest(void) {
349cc4a5 89#if HAVE_SELINUX
6baa7db0 90 cached_use = -1;
66b6d9d5 91#endif
cad45ba1 92}
0b6018f3 93
349cc4a5 94#if HAVE_SELINUX
1885169c
LP
95# if HAVE_MALLINFO2
96# define HAVE_GENERIC_MALLINFO 1
97typedef struct mallinfo2 generic_mallinfo;
98static generic_mallinfo generic_mallinfo_get(void) {
99 return mallinfo2();
100}
101# elif HAVE_MALLINFO
102# define HAVE_GENERIC_MALLINFO 1
103typedef struct mallinfo generic_mallinfo;
104static generic_mallinfo generic_mallinfo_get(void) {
105 /* glibc has deprecated mallinfo(), let's suppress the deprecation warning if mallinfo2() doesn't
106 * exist yet. */
4b6f74f5
ZJS
107DISABLE_WARNING_DEPRECATED_DECLARATIONS
108 return mallinfo();
109REENABLE_WARNING
110}
111# else
1885169c 112# define HAVE_GENERIC_MALLINFO 0
4b6f74f5
ZJS
113# endif
114
fd5e402f
CG
115static int open_label_db(void) {
116 struct selabel_handle *hnd;
66b6d9d5 117 usec_t before_timestamp, after_timestamp;
66b6d9d5 118
1885169c
LP
119# if HAVE_GENERIC_MALLINFO
120 generic_mallinfo before_mallinfo = generic_mallinfo_get();
4b6f74f5 121# endif
66b6d9d5
WC
122 before_timestamp = now(CLOCK_MONOTONIC);
123
fd5e402f
CG
124 hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
125 if (!hnd)
a9ba0e32 126 return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m");
66b6d9d5 127
65804d6a 128 after_timestamp = now(CLOCK_MONOTONIC);
1885169c
LP
129# if HAVE_GENERIC_MALLINFO
130 generic_mallinfo after_mallinfo = generic_mallinfo_get();
131 size_t l = LESS_BY((size_t) after_mallinfo.uordblks, (size_t) before_mallinfo.uordblks);
132 log_debug("Successfully loaded SELinux database in %s, size on heap is %zuK.",
5291f26d 133 FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0),
4b6f74f5
ZJS
134 DIV_ROUND_UP(l, 1024));
135# else
136 log_debug("Successfully loaded SELinux database in %s.",
5291f26d 137 FORMAT_TIMESPAN(after_timestamp - before_timestamp, 0));
4b6f74f5 138# endif
66b6d9d5 139
fd5e402f
CG
140 /* release memory after measurement */
141 if (label_hnd)
142 selabel_close(label_hnd);
143 label_hnd = TAKE_PTR(hnd);
144
145 return 0;
146}
147#endif
148
149int mac_selinux_init(void) {
150#if HAVE_SELINUX
151 int r;
152
153 if (initialized)
154 return 0;
155
156 if (!mac_selinux_use())
157 return 0;
158
961b341e
ZJS
159 r = selinux_status_open(/* netlink fallback */ 1);
160 if (r < 0) {
161 if (!ERRNO_IS_PRIVILEGE(errno))
162 return log_enforcing_errno(errno, "Failed to open SELinux status page: %m");
163 log_warning_errno(errno, "selinux_status_open() with netlink fallback failed, not checking for policy reloads: %m");
164 } else if (r == 1)
165 log_warning("selinux_status_open() failed to open the status page, using the netlink fallback.");
166 else
167 have_status_page = true;
fd5e402f
CG
168
169 r = open_label_db();
170 if (r < 0) {
171 selinux_status_close();
172 return r;
173 }
174
961b341e
ZJS
175 /* Save the current policyload sequence number, so mac_selinux_maybe_reload() does not trigger on
176 * first call without any actual change. */
fd5e402f
CG
177 last_policyload = selinux_status_policyload();
178
fd5e402f 179 initialized = true;
65804d6a
LP
180#endif
181 return 0;
d682b3a7
LP
182}
183
fd5e402f
CG
184void mac_selinux_maybe_reload(void) {
185#if HAVE_SELINUX
c46c7688 186 int policyload;
fd5e402f 187
1006038c
CG
188 if (!initialized)
189 return;
190
c46c7688
CG
191 /* Do not use selinux_status_updated(3), cause since libselinux 3.2 selinux_check_access(3),
192 * called in core and user instances, does also use it under the hood.
193 * That can cause changes to be consumed by selinux_check_access(3) and not being visible here.
194 * Also do not use selinux callbacks, selinux_set_callback(3), cause they are only automatically
195 * invoked since libselinux 3.2 by selinux_status_updated(3).
196 * Relevant libselinux commit: https://github.com/SELinuxProject/selinux/commit/05bdc03130d741e53e1fb45a958d0a2c184be503
197 * Debian Bullseye is going to ship libselinux 3.1, so stay compatible for backports. */
198 policyload = selinux_status_policyload();
199 if (policyload < 0) {
200 log_debug_errno(errno, "Failed to get SELinux policyload from status page: %m");
201 return;
202 }
fd5e402f 203
c46c7688
CG
204 if (policyload != last_policyload) {
205 mac_selinux_reload(policyload);
206 last_policyload = policyload;
fd5e402f
CG
207 }
208#endif
209}
210
ecabcf8b
LP
211void mac_selinux_finish(void) {
212
349cc4a5 213#if HAVE_SELINUX
fd5e402f
CG
214 if (label_hnd) {
215 selabel_close(label_hnd);
216 label_hnd = NULL;
217 }
218
fd5e402f 219 selinux_status_close();
c3b8bacd 220 have_status_page = false;
fd5e402f
CG
221
222 initialized = false;
ecabcf8b
LP
223#endif
224}
225
a9dfac21 226#if HAVE_SELINUX
61f3e897 227static int mac_selinux_reload(int seqno) {
fd5e402f 228 log_debug("SELinux reload %d", seqno);
a9dfac21 229
fd5e402f 230 (void) open_label_db();
61f3e897
CG
231
232 return 0;
a9dfac21 233}
61f3e897 234#endif
a9dfac21 235
349cc4a5 236#if HAVE_SELINUX
03bc11d1
LP
237static int selinux_fix_fd(
238 int fd,
239 const char *label_path,
240 LabelFixFlags flags) {
9271daee 241
9271daee
LP
242 _cleanup_freecon_ char* fcon = NULL;
243 struct stat st;
244 int r;
245
03bc11d1
LP
246 assert(fd >= 0);
247 assert(label_path);
248 assert(path_is_absolute(label_path));
9271daee 249
08c84981
LP
250 if (fstat(fd, &st) < 0)
251 return -errno;
5dfc5461 252
61f3e897 253 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
fd5e402f 254 mac_selinux_maybe_reload();
7960ba96
ZJS
255 if (!label_hnd)
256 return 0;
61f3e897 257
03bc11d1 258 if (selabel_lookup_raw(label_hnd, &fcon, label_path, st.st_mode) < 0) {
66b6d9d5 259 /* If there's no label to set, then exit without warning */
9271daee 260 if (errno == ENOENT)
66b6d9d5
WC
261 return 0;
262
03bc11d1 263 return log_enforcing_errno(errno, "Unable to lookup intended SELinux security context of %s: %m", label_path);
66b6d9d5
WC
264 }
265
ddb6eeaf 266 if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) {
08c84981
LP
267 _cleanup_freecon_ char *oldcon = NULL;
268
03bc11d1
LP
269 r = -errno;
270
08c84981 271 /* If the FS doesn't support labels, then exit without warning */
03bc11d1 272 if (ERRNO_IS_NOT_SUPPORTED(r))
66b6d9d5
WC
273 return 0;
274
08c84981 275 /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
03bc11d1 276 if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
66b6d9d5
WC
277 return 0;
278
08c84981 279 /* If the old label is identical to the new one, suppress any kind of error */
af614e45 280 if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq_ptr(fcon, oldcon))
08c84981
LP
281 return 0;
282
03bc11d1 283 return log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", label_path);
66b6d9d5 284 }
08c84981
LP
285
286 return 0;
03bc11d1 287}
66b6d9d5
WC
288#endif
289
03bc11d1
LP
290int mac_selinux_fix_full(
291 int atfd,
292 const char *inode_path,
293 const char *label_path,
294 LabelFixFlags flags) {
295
296 assert(atfd >= 0 || atfd == AT_FDCWD);
297 assert(atfd >= 0 || inode_path);
298
299#if HAVE_SELINUX
254d1313 300 _cleanup_close_ int opened_fd = -EBADF;
03bc11d1
LP
301 _cleanup_free_ char *p = NULL;
302 int inode_fd, r;
303
304 /* if mac_selinux_init() wasn't called before we are a NOOP */
305 if (!label_hnd)
306 return 0;
307
308 if (inode_path) {
309 opened_fd = openat(atfd, inode_path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
310 if (opened_fd < 0) {
311 if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
312 return 0;
313
314 return -errno;
315 }
316
317 inode_fd = opened_fd;
318 } else
319 inode_fd = atfd;
320
321 if (!label_path) {
322 if (path_is_absolute(inode_path))
323 label_path = inode_path;
324 else {
325 r = fd_get_path(inode_fd, &p);
326 if (r < 0)
327 return r;
328
329 label_path = p;
330 }
331 }
332
333 return selinux_fix_fd(inode_fd, label_path, flags);
334#else
ecabcf8b 335 return 0;
03bc11d1 336#endif
66b6d9d5
WC
337}
338
ecabcf8b 339int mac_selinux_apply(const char *path, const char *label) {
66b6d9d5 340
9271daee
LP
341 assert(path);
342
349cc4a5 343#if HAVE_SELINUX
ecabcf8b
LP
344 if (!mac_selinux_use())
345 return 0;
346
0f474365
LP
347 assert(label);
348
65804d6a
LP
349 if (setfilecon(path, label) < 0)
350 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
66b6d9d5 351#endif
ecabcf8b 352 return 0;
d682b3a7
LP
353}
354
9271daee
LP
355int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
356
357 assert(fd >= 0);
358
359#if HAVE_SELINUX
360 if (!mac_selinux_use())
361 return 0;
362
363 assert(label);
364
a718364e 365 if (setfilecon(FORMAT_PROC_FD_PATH(fd), label) < 0)
9271daee
LP
366 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
367#endif
368 return 0;
369}
370
cc56fafe 371int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
349cc4a5 372#if HAVE_SELINUX
2ed96880 373 _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
66b6d9d5
WC
374 security_class_t sclass;
375
7f416dae
LP
376 assert(exe);
377 assert(label);
378
6d395665 379 if (!mac_selinux_use())
7f416dae 380 return -EOPNOTSUPP;
66b6d9d5 381
09f7e8d6 382 if (getcon_raw(&mycon) < 0)
7f416dae 383 return -errno;
af614e45
LP
384 if (!mycon)
385 return -EOPNOTSUPP;
66b6d9d5 386
09f7e8d6 387 if (getfilecon_raw(exe, &fcon) < 0)
7f416dae 388 return -errno;
af614e45
LP
389 if (!fcon)
390 return -EOPNOTSUPP;
66b6d9d5
WC
391
392 sclass = string_to_security_class("process");
fdb0405e
CG
393 if (sclass == 0)
394 return -ENOSYS;
395
7c248223 396 return RET_NERRNO(security_compute_create_raw(mycon, fcon, sclass, label));
65804d6a
LP
397#else
398 return -EOPNOTSUPP;
399#endif
66b6d9d5
WC
400}
401
af614e45
LP
402int mac_selinux_get_our_label(char **ret) {
403 assert(ret);
7f416dae 404
af614e45 405#if HAVE_SELINUX
6d395665 406 if (!mac_selinux_use())
7f416dae 407 return -EOPNOTSUPP;
66b6d9d5 408
af614e45
LP
409 _cleanup_freecon_ char *con = NULL;
410 if (getcon_raw(&con) < 0)
411 return -errno;
412 if (!con)
413 return -EOPNOTSUPP;
414
415 *ret = TAKE_PTR(con);
416 return 0;
65804d6a
LP
417#else
418 return -EOPNOTSUPP;
419#endif
66b6d9d5
WC
420}
421
09f7e8d6 422int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **ret_label) {
349cc4a5 423#if HAVE_SELINUX
2ed96880 424 _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
66b6d9d5 425 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
09f7e8d6 426 const char *range = NULL, *bcon_str = NULL;
66b6d9d5 427 security_class_t sclass;
66b6d9d5
WC
428
429 assert(socket_fd >= 0);
430 assert(exe);
09f7e8d6 431 assert(ret_label);
66b6d9d5 432
6d395665 433 if (!mac_selinux_use())
7f416dae
LP
434 return -EOPNOTSUPP;
435
09f7e8d6 436 if (getcon_raw(&mycon) < 0)
7f416dae 437 return -errno;
af614e45
LP
438 if (!mycon)
439 return -EOPNOTSUPP;
66b6d9d5 440
09f7e8d6 441 if (getpeercon_raw(socket_fd, &peercon) < 0)
7f416dae 442 return -errno;
af614e45
LP
443 if (!peercon)
444 return -EOPNOTSUPP;
66b6d9d5 445
af614e45 446 if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */
09f7e8d6 447 if (getfilecon_raw(exe, &fcon) < 0)
7f416dae 448 return -errno;
af614e45
LP
449 if (!fcon)
450 return -EOPNOTSUPP;
451 }
66b6d9d5
WC
452
453 bcon = context_new(mycon);
7f416dae
LP
454 if (!bcon)
455 return -ENOMEM;
66b6d9d5
WC
456
457 pcon = context_new(peercon);
7f416dae
LP
458 if (!pcon)
459 return -ENOMEM;
66b6d9d5
WC
460
461 range = context_range_get(pcon);
7f416dae
LP
462 if (!range)
463 return -errno;
66b6d9d5 464
09f7e8d6 465 if (context_range_set(bcon, range) != 0)
7f416dae 466 return -errno;
66b6d9d5 467
09f7e8d6
LP
468 bcon_str = context_str(bcon);
469 if (!bcon_str)
7f416dae 470 return -ENOMEM;
66b6d9d5
WC
471
472 sclass = string_to_security_class("process");
fdb0405e
CG
473 if (sclass == 0)
474 return -ENOSYS;
475
09f7e8d6 476 return RET_NERRNO(security_compute_create_raw(bcon_str, fcon, sclass, ret_label));
65804d6a
LP
477#else
478 return -EOPNOTSUPP;
479#endif
66b6d9d5
WC
480}
481
710a6b50 482char* mac_selinux_free(char *label) {
ecabcf8b 483
349cc4a5 484#if HAVE_SELINUX
2ed96880 485 freecon(label);
3d9fbea4
CG
486#else
487 assert(!label);
ecabcf8b 488#endif
710a6b50
LP
489
490 return NULL;
ecabcf8b
LP
491}
492
349cc4a5 493#if HAVE_SELINUX
7e531a52 494static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
2ed96880 495 _cleanup_freecon_ char *filecon = NULL;
0f474365 496 int r;
66b6d9d5 497
7e531a52
FB
498 assert(abspath);
499 assert(path_is_absolute(abspath));
c34255bd 500
61f3e897 501 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
fd5e402f 502 mac_selinux_maybe_reload();
7960ba96
ZJS
503 if (!label_hnd)
504 return 0;
61f3e897 505
7e531a52 506 r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
0f474365
LP
507 if (r < 0) {
508 /* No context specified by the policy? Proceed without setting it. */
509 if (errno == ENOENT)
510 return 0;
2d58aa46 511
65804d6a 512 return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
66b6d9d5
WC
513 }
514
65804d6a
LP
515 if (setfscreatecon_raw(filecon) < 0)
516 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
66b6d9d5 517
0f474365 518 return 0;
66b6d9d5 519}
7e531a52
FB
520#endif
521
fc0f4d60
LP
522int mac_selinux_create_file_prepare_at(
523 int dir_fd,
524 const char *path,
525 mode_t mode) {
526
7e531a52
FB
527#if HAVE_SELINUX
528 _cleanup_free_ char *abspath = NULL;
65804d6a
LP
529 int r;
530
fc0f4d60
LP
531 if (dir_fd < 0 && dir_fd != AT_FDCWD)
532 return -EBADF;
7e531a52
FB
533
534 if (!label_hnd)
535 return 0;
536
fc0f4d60
LP
537 if (isempty(path) || !path_is_absolute(path)) {
538 if (dir_fd == AT_FDCWD)
4ede9802 539 r = safe_getcwd(&abspath);
7e531a52 540 else
fc0f4d60 541 r = fd_get_path(dir_fd, &abspath);
7e531a52
FB
542 if (r < 0)
543 return r;
544
fc0f4d60 545 if (!isempty(path) && !path_extend(&abspath, path))
7e531a52 546 return -ENOMEM;
4ede9802
LP
547
548 path = abspath;
7e531a52
FB
549 }
550
65804d6a
LP
551 return selinux_create_file_prepare_abspath(path, mode);
552#else
553 return 0;
7e531a52 554#endif
7e531a52
FB
555}
556
6f7ccbcc
LB
557int mac_selinux_create_file_prepare_label(const char *path, const char *label) {
558#if HAVE_SELINUX
559
560 if (!label)
561 return 0;
562
563 if (!mac_selinux_use())
564 return 0;
565
566 if (setfscreatecon_raw(label) < 0)
567 return log_enforcing_errno(errno, "Failed to set specified SELinux security context '%s' for '%s': %m", label, strna(path));
568#endif
569 return 0;
570}
571
ecabcf8b 572void mac_selinux_create_file_clear(void) {
66b6d9d5 573
349cc4a5 574#if HAVE_SELINUX
66b6d9d5
WC
575 PROTECT_ERRNO;
576
6baa7db0 577 if (!mac_selinux_use())
66b6d9d5
WC
578 return;
579
a1d2de07 580 setfscreatecon_raw(NULL);
66b6d9d5
WC
581#endif
582}
583
ecabcf8b 584int mac_selinux_create_socket_prepare(const char *label) {
66b6d9d5 585
349cc4a5 586#if HAVE_SELINUX
ecabcf8b
LP
587 assert(label);
588
65804d6a
LP
589 if (!mac_selinux_use())
590 return 0;
ecabcf8b 591
65804d6a
LP
592 if (setsockcreatecon(label) < 0)
593 return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
66b6d9d5 594#endif
ecabcf8b
LP
595
596 return 0;
66b6d9d5
WC
597}
598
ecabcf8b 599void mac_selinux_create_socket_clear(void) {
66b6d9d5 600
349cc4a5 601#if HAVE_SELINUX
ecabcf8b
LP
602 PROTECT_ERRNO;
603
6baa7db0 604 if (!mac_selinux_use())
66b6d9d5
WC
605 return;
606
a1d2de07 607 setsockcreatecon_raw(NULL);
66b6d9d5
WC
608#endif
609}
610
cc56fafe 611int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
66b6d9d5
WC
612
613 /* Binds a socket and label its file system object according to the SELinux policy */
614
349cc4a5 615#if HAVE_SELINUX
2ed96880 616 _cleanup_freecon_ char *fcon = NULL;
66b6d9d5 617 const struct sockaddr_un *un;
0f474365 618 bool context_changed = false;
07359766 619 size_t sz;
66b6d9d5
WC
620 char *path;
621 int r;
622
623 assert(fd >= 0);
624 assert(addr);
625 assert(addrlen >= sizeof(sa_family_t));
626
ecabcf8b 627 if (!label_hnd)
66b6d9d5
WC
628 goto skipped;
629
630 /* Filter out non-local sockets */
631 if (addr->sa_family != AF_UNIX)
632 goto skipped;
633
634 /* Filter out anonymous sockets */
0f474365 635 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
66b6d9d5
WC
636 goto skipped;
637
638 /* Filter out abstract namespace sockets */
639 un = (const struct sockaddr_un*) addr;
640 if (un->sun_path[0] == 0)
641 goto skipped;
642
07359766
LP
643 sz = addrlen - offsetof(struct sockaddr_un, sun_path);
644 if (sz > PATH_MAX)
645 goto skipped;
646 path = strndupa_safe(un->sun_path, sz);
66b6d9d5 647
61f3e897 648 /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
fd5e402f 649 mac_selinux_maybe_reload();
7960ba96
ZJS
650 if (!label_hnd)
651 goto skipped;
61f3e897 652
66b6d9d5
WC
653 if (path_is_absolute(path))
654 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
655 else {
0f474365 656 _cleanup_free_ char *newpath = NULL;
66b6d9d5 657
0f474365
LP
658 r = path_make_absolute_cwd(path, &newpath);
659 if (r < 0)
660 return r;
66b6d9d5
WC
661
662 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
663 }
664
0f474365
LP
665 if (r < 0) {
666 /* No context specified by the policy? Proceed without setting it */
667 if (errno == ENOENT)
668 goto skipped;
66b6d9d5 669
65804d6a
LP
670 r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
671 if (r < 0)
672 return r;
0f474365 673 } else {
a1d2de07 674 if (setfscreatecon_raw(fcon) < 0) {
65804d6a
LP
675 r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
676 if (r < 0)
677 return r;
0f474365
LP
678 } else
679 context_changed = true;
66b6d9d5
WC
680 }
681
7c248223 682 r = RET_NERRNO(bind(fd, addr, addrlen));
0f474365
LP
683
684 if (context_changed)
65804d6a 685 (void) setfscreatecon_raw(NULL);
66b6d9d5 686
66b6d9d5
WC
687 return r;
688
689skipped:
690#endif
7c248223 691 return RET_NERRNO(bind(fd, addr, addrlen));
66b6d9d5 692}