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