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