chiark / gitweb /
mac: also rename use_{smack,selinux,apparmor}() calls so that they share the new...
[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         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
197         freecon(mycon);
198         freecon(fcon);
199 #endif
200
201         return r;
202 }
203
204 int mac_selinux_get_our_label(char **label) {
205         int r = -EOPNOTSUPP;
206
207 #ifdef HAVE_SELINUX
208         char *l = NULL;
209
210         r = getcon(&l);
211         if (r < 0)
212                 return r;
213
214         *label = l;
215 #endif
216
217         return r;
218 }
219
220 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label) {
221         int r = -EOPNOTSUPP;
222
223 #ifdef HAVE_SELINUX
224
225         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL;
226         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
227         security_class_t sclass;
228
229         const char *range = NULL;
230
231         assert(socket_fd >= 0);
232         assert(exe);
233         assert(label);
234
235         r = getcon(&mycon);
236         if (r < 0) {
237                 r = -EINVAL;
238                 goto out;
239         }
240
241         r = getpeercon(socket_fd, &peercon);
242         if (r < 0) {
243                 r = -EINVAL;
244                 goto out;
245         }
246
247         r = getexeccon(&fcon);
248         if (r < 0) {
249                 r = -EINVAL;
250                 goto out;
251         }
252
253         if (!fcon) {
254                 /* If there is no context set for next exec let's use context
255                    of target executable */
256                 r = getfilecon(exe, &fcon);
257                 if (r < 0) {
258                         r = -errno;
259                         goto out;
260                 }
261         }
262
263         bcon = context_new(mycon);
264         if (!bcon) {
265                 r = -ENOMEM;
266                 goto out;
267         }
268
269         pcon = context_new(peercon);
270         if (!pcon) {
271                 r = -ENOMEM;
272                 goto out;
273         }
274
275         range = context_range_get(pcon);
276         if (!range) {
277                 r = -errno;
278                 goto out;
279         }
280
281         r = context_range_set(bcon, range);
282         if (r) {
283                 r = -errno;
284                 goto out;
285         }
286
287         freecon(mycon);
288         mycon = strdup(context_str(bcon));
289         if (!mycon) {
290                 r = -errno;
291                 goto out;
292         }
293
294         sclass = string_to_security_class("process");
295         r = security_compute_create(mycon, fcon, sclass, &ret);
296         if (r < 0) {
297                 r = -EINVAL;
298                 goto out;
299         }
300
301         *label = ret;
302         ret = NULL;
303         r = 0;
304
305 out:
306         if (r < 0 && security_getenforce() == 1)
307                 return r;
308 #endif
309         return r;
310 }
311
312 int mac_selinux_context_set(const char *path, mode_t mode) {
313         int r = 0;
314
315 #ifdef HAVE_SELINUX
316         security_context_t filecon = NULL;
317
318         if (!mac_selinux_use() || !label_hnd)
319                 return 0;
320
321         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
322         if (r < 0 && errno != ENOENT)
323                 r = -errno;
324         else if (r == 0) {
325                 r = setfscreatecon(filecon);
326                 if (r < 0) {
327                         log_error("Failed to set SELinux file context on %s: %m", path);
328                         r = -errno;
329                 }
330
331                 freecon(filecon);
332         }
333
334         if (r < 0 && security_getenforce() == 0)
335                 r = 0;
336 #endif
337
338         return r;
339 }
340
341 int mac_selinux_socket_set(const char *label) {
342
343 #ifdef HAVE_SELINUX
344         if (!mac_selinux_use())
345                 return 0;
346
347         if (setsockcreatecon((security_context_t) label) < 0) {
348                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
349                          "Failed to set SELinux context (%s) on socket: %m", label);
350
351                 if (security_getenforce() == 1)
352                         return -errno;
353         }
354 #endif
355
356         return 0;
357 }
358
359 void mac_selinux_context_clear(void) {
360
361 #ifdef HAVE_SELINUX
362         PROTECT_ERRNO;
363
364         if (!mac_selinux_use())
365                 return;
366
367         setfscreatecon(NULL);
368 #endif
369 }
370
371 void mac_selinux_socket_clear(void) {
372
373 #ifdef HAVE_SELINUX
374         PROTECT_ERRNO;
375
376         if (!mac_selinux_use())
377                 return;
378
379         setsockcreatecon(NULL);
380 #endif
381 }
382
383 void mac_selinux_free(const char *label) {
384
385 #ifdef HAVE_SELINUX
386         if (!mac_selinux_use())
387                 return;
388
389         freecon((security_context_t) label);
390 #endif
391 }
392
393 int mac_selinux_mkdir(const char *path, mode_t mode) {
394         int r = 0;
395
396 #ifdef HAVE_SELINUX
397         /* Creates a directory and labels it according to the SELinux policy */
398         security_context_t fcon = NULL;
399
400         if (!label_hnd)
401                 return 0;
402
403         if (path_is_absolute(path))
404                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
405         else {
406                 _cleanup_free_ char *newpath;
407
408                 newpath = path_make_absolute_cwd(path);
409                 if (!newpath)
410                         return -ENOMEM;
411
412                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
413         }
414
415         if (r == 0)
416                 r = setfscreatecon(fcon);
417
418         if (r < 0 && errno != ENOENT) {
419                 log_error("Failed to set security context %s for %s: %m", fcon, path);
420
421                 if (security_getenforce() == 1) {
422                         r = -errno;
423                         goto finish;
424                 }
425         }
426
427         r = mkdir(path, mode);
428         if (r < 0)
429                 r = -errno;
430
431 finish:
432         setfscreatecon(NULL);
433         freecon(fcon);
434 #endif
435
436         return r;
437 }
438
439 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
440
441         /* Binds a socket and label its file system object according to the SELinux policy */
442
443 #ifdef HAVE_SELINUX
444         security_context_t fcon = NULL;
445         const struct sockaddr_un *un;
446         char *path;
447         int r;
448
449         assert(fd >= 0);
450         assert(addr);
451         assert(addrlen >= sizeof(sa_family_t));
452
453         if (!mac_selinux_use() || !label_hnd)
454                 goto skipped;
455
456         /* Filter out non-local sockets */
457         if (addr->sa_family != AF_UNIX)
458                 goto skipped;
459
460         /* Filter out anonymous sockets */
461         if (addrlen < sizeof(sa_family_t) + 1)
462                 goto skipped;
463
464         /* Filter out abstract namespace sockets */
465         un = (const struct sockaddr_un*) addr;
466         if (un->sun_path[0] == 0)
467                 goto skipped;
468
469         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
470
471         if (path_is_absolute(path))
472                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
473         else {
474                 _cleanup_free_ char *newpath;
475
476                 newpath = path_make_absolute_cwd(path);
477                 if (!newpath)
478                         return -ENOMEM;
479
480                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
481         }
482
483         if (r == 0)
484                 r = setfscreatecon(fcon);
485
486         if (r < 0 && errno != ENOENT) {
487                 log_error("Failed to set security context %s for %s: %m", fcon, path);
488
489                 if (security_getenforce() == 1) {
490                         r = -errno;
491                         goto finish;
492                 }
493         }
494
495         r = bind(fd, addr, addrlen);
496         if (r < 0)
497                 r = -errno;
498
499 finish:
500         setfscreatecon(NULL);
501         freecon(fcon);
502
503         return r;
504
505 skipped:
506 #endif
507         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
508 }
509
510 int mac_selinux_apply(const char *path, const char *label) {
511         int r = 0;
512
513 #ifdef HAVE_SELINUX
514         if (!mac_selinux_use())
515                 return 0;
516
517         r = setfilecon(path, (char *)label);
518 #endif
519         return r;
520 }