chiark / gitweb /
selinux: make use of cleanup gcc magic
[elogind.git] / src / shared / 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 <unistd.h>
24 #include <malloc.h>
25 #include <sys/un.h>
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 #endif
46
47 bool mac_selinux_use(void) {
48 #ifdef HAVE_SELINUX
49         if (cached_use < 0)
50                 cached_use = is_selinux_enabled() > 0;
51
52         return cached_use;
53 #else
54         return false;
55 #endif
56 }
57
58 void mac_selinux_retest(void) {
59 #ifdef HAVE_SELINUX
60         cached_use = -1;
61 #endif
62 }
63
64 int mac_selinux_init(const char *prefix) {
65         int r = 0;
66
67 #ifdef HAVE_SELINUX
68         usec_t before_timestamp, after_timestamp;
69         struct mallinfo before_mallinfo, after_mallinfo;
70
71         if (!mac_selinux_use())
72                 return 0;
73
74         if (label_hnd)
75                 return 0;
76
77         before_mallinfo = mallinfo();
78         before_timestamp = now(CLOCK_MONOTONIC);
79
80         if (prefix) {
81                 struct selinux_opt options[] = {
82                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
83                 };
84
85                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
86         } else
87                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
88
89         if (!label_hnd) {
90                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
91                          "Failed to initialize SELinux context: %m");
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;
109 }
110
111 int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
112         int r = 0;
113
114 #ifdef HAVE_SELINUX
115         struct stat st;
116         security_context_t fcon;
117
118         if (!label_hnd)
119                 return 0;
120
121         r = lstat(path, &st);
122         if (r == 0) {
123                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
124
125                 /* If there's no label to set, then exit without warning */
126                 if (r < 0 && errno == ENOENT)
127                         return 0;
128
129                 if (r == 0) {
130                         r = lsetfilecon(path, fcon);
131                         freecon(fcon);
132
133                         /* If the FS doesn't support labels, then exit without warning */
134                         if (r < 0 && errno == ENOTSUP)
135                                 return 0;
136                 }
137         }
138
139         if (r < 0) {
140                 /* Ignore ENOENT in some cases */
141                 if (ignore_enoent && errno == ENOENT)
142                         return 0;
143
144                 if (ignore_erofs && errno == EROFS)
145                         return 0;
146
147                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
148                          "Unable to fix label of %s: %m", path);
149                 r = security_getenforce() == 1 ? -errno : 0;
150         }
151 #endif
152
153         return r;
154 }
155
156 void mac_selinux_finish(void) {
157
158 #ifdef HAVE_SELINUX
159         if (!mac_selinux_use())
160                 return;
161
162         if (label_hnd)
163                 selabel_close(label_hnd);
164 #endif
165 }
166
167 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
168
169         int r = 0;
170
171 #ifdef HAVE_SELINUX
172         _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
173         security_class_t sclass;
174
175         if (!mac_selinux_use()) {
176                 *label = NULL;
177                 return 0;
178         }
179
180         r = getcon(&mycon);
181         if (r < 0)
182                 goto fail;
183
184         r = getfilecon(exe, &fcon);
185         if (r < 0)
186                 goto fail;
187
188         sclass = string_to_security_class("process");
189         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
190         if (r == 0)
191                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
192
193 fail:
194         if (r < 0 && security_getenforce() == 1)
195                 r = -errno;
196 #endif
197
198         return r;
199 }
200
201 int mac_selinux_get_our_label(char **label) {
202         int r = -EOPNOTSUPP;
203
204 #ifdef HAVE_SELINUX
205         char *l = NULL;
206
207         r = getcon(&l);
208         if (r < 0)
209                 return r;
210
211         *label = l;
212 #endif
213
214         return r;
215 }
216
217 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label) {
218         int r = -EOPNOTSUPP;
219
220 #ifdef HAVE_SELINUX
221
222         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL;
223         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
224         security_class_t sclass;
225
226         const char *range = NULL;
227
228         assert(socket_fd >= 0);
229         assert(exe);
230         assert(label);
231
232         r = getcon(&mycon);
233         if (r < 0) {
234                 r = -EINVAL;
235                 goto out;
236         }
237
238         r = getpeercon(socket_fd, &peercon);
239         if (r < 0) {
240                 r = -EINVAL;
241                 goto out;
242         }
243
244         r = getexeccon(&fcon);
245         if (r < 0) {
246                 r = -EINVAL;
247                 goto out;
248         }
249
250         if (!fcon) {
251                 /* If there is no context set for next exec let's use context
252                    of target executable */
253                 r = getfilecon(exe, &fcon);
254                 if (r < 0) {
255                         r = -errno;
256                         goto out;
257                 }
258         }
259
260         bcon = context_new(mycon);
261         if (!bcon) {
262                 r = -ENOMEM;
263                 goto out;
264         }
265
266         pcon = context_new(peercon);
267         if (!pcon) {
268                 r = -ENOMEM;
269                 goto out;
270         }
271
272         range = context_range_get(pcon);
273         if (!range) {
274                 r = -errno;
275                 goto out;
276         }
277
278         r = context_range_set(bcon, range);
279         if (r) {
280                 r = -errno;
281                 goto out;
282         }
283
284         freecon(mycon);
285         mycon = strdup(context_str(bcon));
286         if (!mycon) {
287                 r = -errno;
288                 goto out;
289         }
290
291         sclass = string_to_security_class("process");
292         r = security_compute_create(mycon, fcon, sclass, &ret);
293         if (r < 0) {
294                 r = -EINVAL;
295                 goto out;
296         }
297
298         *label = ret;
299         ret = NULL;
300         r = 0;
301
302 out:
303         if (r < 0 && security_getenforce() == 1)
304                 return r;
305 #endif
306         return r;
307 }
308
309 int mac_selinux_context_set(const char *path, mode_t mode) {
310         int r = 0;
311
312 #ifdef HAVE_SELINUX
313         _cleanup_security_context_free_ security_context_t filecon = NULL;
314
315         if (!mac_selinux_use() || !label_hnd)
316                 return 0;
317
318         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
319         if (r < 0 && errno != ENOENT)
320                 r = -errno;
321         else if (r == 0) {
322                 r = setfscreatecon(filecon);
323                 if (r < 0) {
324                         log_error("Failed to set SELinux file context on %s: %m", path);
325                         r = -errno;
326                 }
327         }
328
329         if (r < 0 && security_getenforce() == 0)
330                 r = 0;
331 #endif
332
333         return r;
334 }
335
336 int mac_selinux_socket_set(const char *label) {
337
338 #ifdef HAVE_SELINUX
339         if (!mac_selinux_use())
340                 return 0;
341
342         if (setsockcreatecon((security_context_t) label) < 0) {
343                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
344                          "Failed to set SELinux context (%s) on socket: %m", label);
345
346                 if (security_getenforce() == 1)
347                         return -errno;
348         }
349 #endif
350
351         return 0;
352 }
353
354 void mac_selinux_context_clear(void) {
355
356 #ifdef HAVE_SELINUX
357         PROTECT_ERRNO;
358
359         if (!mac_selinux_use())
360                 return;
361
362         setfscreatecon(NULL);
363 #endif
364 }
365
366 void mac_selinux_socket_clear(void) {
367
368 #ifdef HAVE_SELINUX
369         PROTECT_ERRNO;
370
371         if (!mac_selinux_use())
372                 return;
373
374         setsockcreatecon(NULL);
375 #endif
376 }
377
378 void mac_selinux_free(const char *label) {
379
380 #ifdef HAVE_SELINUX
381         if (!mac_selinux_use())
382                 return;
383
384         freecon((security_context_t) label);
385 #endif
386 }
387
388 int mac_selinux_mkdir(const char *path, mode_t mode) {
389         int r = 0;
390
391 #ifdef HAVE_SELINUX
392         /* Creates a directory and labels it according to the SELinux policy */
393         _cleanup_security_context_free_ security_context_t fcon = NULL;
394
395         if (!label_hnd)
396                 return 0;
397
398         if (path_is_absolute(path))
399                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
400         else {
401                 _cleanup_free_ char *newpath;
402
403                 newpath = path_make_absolute_cwd(path);
404                 if (!newpath)
405                         return -ENOMEM;
406
407                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
408         }
409
410         if (r == 0)
411                 r = setfscreatecon(fcon);
412
413         if (r < 0 && errno != ENOENT) {
414                 log_error("Failed to set security context %s for %s: %m", fcon, path);
415
416                 if (security_getenforce() == 1) {
417                         r = -errno;
418                         goto finish;
419                 }
420         }
421
422         r = mkdir(path, mode);
423         if (r < 0)
424                 r = -errno;
425
426 finish:
427         setfscreatecon(NULL);
428 #endif
429
430         return r;
431 }
432
433 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
434
435         /* Binds a socket and label its file system object according to the SELinux policy */
436
437 #ifdef HAVE_SELINUX
438         _cleanup_security_context_free_ security_context_t fcon = NULL;
439         const struct sockaddr_un *un;
440         char *path;
441         int r;
442
443         assert(fd >= 0);
444         assert(addr);
445         assert(addrlen >= sizeof(sa_family_t));
446
447         if (!mac_selinux_use() || !label_hnd)
448                 goto skipped;
449
450         /* Filter out non-local sockets */
451         if (addr->sa_family != AF_UNIX)
452                 goto skipped;
453
454         /* Filter out anonymous sockets */
455         if (addrlen < sizeof(sa_family_t) + 1)
456                 goto skipped;
457
458         /* Filter out abstract namespace sockets */
459         un = (const struct sockaddr_un*) addr;
460         if (un->sun_path[0] == 0)
461                 goto skipped;
462
463         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
464
465         if (path_is_absolute(path))
466                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
467         else {
468                 _cleanup_free_ char *newpath;
469
470                 newpath = path_make_absolute_cwd(path);
471                 if (!newpath)
472                         return -ENOMEM;
473
474                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
475         }
476
477         if (r == 0)
478                 r = setfscreatecon(fcon);
479
480         if (r < 0 && errno != ENOENT) {
481                 log_error("Failed to set security context %s for %s: %m", fcon, path);
482
483                 if (security_getenforce() == 1) {
484                         r = -errno;
485                         goto finish;
486                 }
487         }
488
489         r = bind(fd, addr, addrlen);
490         if (r < 0)
491                 r = -errno;
492
493 finish:
494         setfscreatecon(NULL);
495         return r;
496
497 skipped:
498 #endif
499         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
500 }
501
502 int mac_selinux_apply(const char *path, const char *label) {
503         int r = 0;
504
505 #ifdef HAVE_SELINUX
506         if (!mac_selinux_use())
507                 return 0;
508
509         r = setfilecon(path, (char *)label);
510 #endif
511         return r;
512 }