chiark / gitweb /
Prep v221: Some more cleanup and a few fixes
[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 <sys/un.h>
25
26 #ifdef HAVE_SELINUX
27 #include <selinux/selinux.h>
28 #include <selinux/label.h>
29 #include <selinux/context.h>
30 #endif
31
32 #include "strv.h"
33 #include "path-util.h"
34 #include "selinux-util.h"
35
36 #ifdef HAVE_SELINUX
37 DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
38 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
39
40 #define _cleanup_security_context_free_ _cleanup_(freeconp)
41 #define _cleanup_context_free_ _cleanup_(context_freep)
42
43 static int cached_use = -1;
44 static struct selabel_handle *label_hnd = NULL;
45
46 #define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
47 #endif
48
49 bool mac_selinux_use(void) {
50 #ifdef HAVE_SELINUX
51         if (cached_use < 0)
52                 cached_use = is_selinux_enabled() > 0;
53
54         return cached_use;
55 #else
56         return false;
57 #endif
58 }
59
60 void mac_selinux_retest(void) {
61 #ifdef HAVE_SELINUX
62         cached_use = -1;
63 #endif
64 }
65
66 int mac_selinux_init(const char *prefix) {
67         int r = 0;
68
69 #ifdef HAVE_SELINUX
70         usec_t before_timestamp, after_timestamp;
71         struct mallinfo before_mallinfo, after_mallinfo;
72
73         if (!mac_selinux_use())
74                 return 0;
75
76         if (label_hnd)
77                 return 0;
78
79         before_mallinfo = mallinfo();
80         before_timestamp = now(CLOCK_MONOTONIC);
81
82         if (prefix) {
83                 struct selinux_opt options[] = {
84                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
85                 };
86
87                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
88         } else
89                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
90
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 #ifdef 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 #ifdef 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_security_context_free_ security_context_t 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(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 /// UNNEDED by elogind
172 #if 0
173 int mac_selinux_apply(const char *path, const char *label) {
174
175 #ifdef HAVE_SELINUX
176         assert(path);
177         assert(label);
178
179         if (!mac_selinux_use())
180                 return 0;
181
182         if (setfilecon(path, (security_context_t) label) < 0) {
183                 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
184                 if (security_getenforce() == 1)
185                         return -errno;
186         }
187 #endif
188         return 0;
189 }
190 #endif // 0
191
192 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
193         int r = -EOPNOTSUPP;
194
195 #ifdef HAVE_SELINUX
196         _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
197         security_class_t sclass;
198
199         assert(exe);
200         assert(label);
201
202         if (!mac_selinux_use())
203                 return -EOPNOTSUPP;
204
205         r = getcon(&mycon);
206         if (r < 0)
207                 return -errno;
208
209         r = getfilecon(exe, &fcon);
210         if (r < 0)
211                 return -errno;
212
213         sclass = string_to_security_class("process");
214         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
215         if (r < 0)
216                 return -errno;
217 #endif
218
219         return r;
220 }
221
222 int mac_selinux_get_our_label(char **label) {
223         int r = -EOPNOTSUPP;
224
225         assert(label);
226
227 #ifdef HAVE_SELINUX
228         if (!mac_selinux_use())
229                 return -EOPNOTSUPP;
230
231         r = getcon(label);
232         if (r < 0)
233                 return -errno;
234 #endif
235
236         return r;
237 }
238
239 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
240         int r = -EOPNOTSUPP;
241
242 #ifdef HAVE_SELINUX
243         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
244         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
245         security_class_t sclass;
246         const char *range = NULL;
247
248         assert(socket_fd >= 0);
249         assert(exe);
250         assert(label);
251
252         if (!mac_selinux_use())
253                 return -EOPNOTSUPP;
254
255         r = getcon(&mycon);
256         if (r < 0)
257                 return -errno;
258
259         r = getpeercon(socket_fd, &peercon);
260         if (r < 0)
261                 return -errno;
262
263         if (!exec_label) {
264                 /* If there is no context set for next exec let's use context
265                    of target executable */
266                 r = getfilecon(exe, &fcon);
267                 if (r < 0)
268                         return -errno;
269         }
270
271         bcon = context_new(mycon);
272         if (!bcon)
273                 return -ENOMEM;
274
275         pcon = context_new(peercon);
276         if (!pcon)
277                 return -ENOMEM;
278
279         range = context_range_get(pcon);
280         if (!range)
281                 return -errno;
282
283         r = context_range_set(bcon, range);
284         if (r)
285                 return -errno;
286
287         freecon(mycon);
288         mycon = strdup(context_str(bcon));
289         if (!mycon)
290                 return -ENOMEM;
291
292         sclass = string_to_security_class("process");
293         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
294         if (r < 0)
295                 return -errno;
296 #endif
297
298         return r;
299 }
300
301 void mac_selinux_free(char *label) {
302
303 #ifdef HAVE_SELINUX
304         if (!mac_selinux_use())
305                 return;
306
307         freecon((security_context_t) label);
308 #endif
309 }
310
311 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
312         int r = 0;
313
314 #ifdef HAVE_SELINUX
315         _cleanup_security_context_free_ security_context_t filecon = NULL;
316
317         assert(path);
318
319         if (!label_hnd)
320                 return 0;
321
322         if (path_is_absolute(path))
323                 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
324         else {
325                 _cleanup_free_ char *newpath;
326
327                 newpath = path_make_absolute_cwd(path);
328                 if (!newpath)
329                         return -ENOMEM;
330
331                 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
332         }
333
334         /* No context specified by the policy? Proceed without setting it. */
335         if (r < 0 && errno == ENOENT)
336                 return 0;
337
338         if (r < 0)
339                 r = -errno;
340         else {
341                 r = setfscreatecon(filecon);
342                 if (r < 0) {
343                         log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
344                         r = -errno;
345                 }
346         }
347
348         if (r < 0 && security_getenforce() == 0)
349                 r = 0;
350 #endif
351
352         return r;
353 }
354
355 void mac_selinux_create_file_clear(void) {
356
357 #ifdef HAVE_SELINUX
358         PROTECT_ERRNO;
359
360         if (!mac_selinux_use())
361                 return;
362
363         setfscreatecon(NULL);
364 #endif
365 }
366
367 int mac_selinux_create_socket_prepare(const char *label) {
368
369 #ifdef HAVE_SELINUX
370         if (!mac_selinux_use())
371                 return 0;
372
373         assert(label);
374
375         if (setsockcreatecon((security_context_t) label) < 0) {
376                 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
377
378                 if (security_getenforce() == 1)
379                         return -errno;
380         }
381 #endif
382
383         return 0;
384 }
385
386 void mac_selinux_create_socket_clear(void) {
387
388 #ifdef HAVE_SELINUX
389         PROTECT_ERRNO;
390
391         if (!mac_selinux_use())
392                 return;
393
394         setsockcreatecon(NULL);
395 #endif
396 }
397
398 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
399
400         /* Binds a socket and label its file system object according to the SELinux policy */
401
402 #ifdef HAVE_SELINUX
403         _cleanup_security_context_free_ security_context_t fcon = NULL;
404         const struct sockaddr_un *un;
405         char *path;
406         int r;
407
408         assert(fd >= 0);
409         assert(addr);
410         assert(addrlen >= sizeof(sa_family_t));
411
412         if (!label_hnd)
413                 goto skipped;
414
415         /* Filter out non-local sockets */
416         if (addr->sa_family != AF_UNIX)
417                 goto skipped;
418
419         /* Filter out anonymous sockets */
420         if (addrlen < sizeof(sa_family_t) + 1)
421                 goto skipped;
422
423         /* Filter out abstract namespace sockets */
424         un = (const struct sockaddr_un*) addr;
425         if (un->sun_path[0] == 0)
426                 goto skipped;
427
428         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
429
430         if (path_is_absolute(path))
431                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
432         else {
433                 _cleanup_free_ char *newpath;
434
435                 newpath = path_make_absolute_cwd(path);
436                 if (!newpath)
437                         return -ENOMEM;
438
439                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
440         }
441
442         if (r == 0)
443                 r = setfscreatecon(fcon);
444
445         if (r < 0 && errno != ENOENT) {
446                 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
447
448                 if (security_getenforce() == 1) {
449                         r = -errno;
450                         goto finish;
451                 }
452         }
453
454         r = bind(fd, addr, addrlen);
455         if (r < 0)
456                 r = -errno;
457
458 finish:
459         setfscreatecon(NULL);
460         return r;
461
462 skipped:
463 #endif
464         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
465 }