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