chiark / gitweb /
Prep v220: Apply "Fixes to user and session saving"
[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 <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 int mac_selinux_apply(const char *path, const char *label) {
172
173 #ifdef HAVE_SELINUX
174         assert(path);
175         assert(label);
176
177         if (!mac_selinux_use())
178                 return 0;
179
180         if (setfilecon(path, (security_context_t) label) < 0) {
181                 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
182                 if (security_getenforce() == 1)
183                         return -errno;
184         }
185 #endif
186         return 0;
187 }
188
189 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
190         int r = -EOPNOTSUPP;
191
192 #ifdef HAVE_SELINUX
193         _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
194         security_class_t sclass;
195
196         assert(exe);
197         assert(label);
198
199         if (!mac_selinux_use())
200                 return -EOPNOTSUPP;
201
202         r = getcon(&mycon);
203         if (r < 0)
204                 return -errno;
205
206         r = getfilecon(exe, &fcon);
207         if (r < 0)
208                 return -errno;
209
210         sclass = string_to_security_class("process");
211         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
212         if (r < 0)
213                 return -errno;
214 #endif
215
216         return r;
217 }
218
219 int mac_selinux_get_our_label(char **label) {
220         int r = -EOPNOTSUPP;
221
222         assert(label);
223
224 #ifdef HAVE_SELINUX
225         if (!mac_selinux_use())
226                 return -EOPNOTSUPP;
227
228         r = getcon(label);
229         if (r < 0)
230                 return -errno;
231 #endif
232
233         return r;
234 }
235
236 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
237         int r = -EOPNOTSUPP;
238
239 #ifdef HAVE_SELINUX
240         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
241         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
242         security_class_t sclass;
243         const char *range = NULL;
244
245         assert(socket_fd >= 0);
246         assert(exe);
247         assert(label);
248
249         if (!mac_selinux_use())
250                 return -EOPNOTSUPP;
251
252         r = getcon(&mycon);
253         if (r < 0)
254                 return -errno;
255
256         r = getpeercon(socket_fd, &peercon);
257         if (r < 0)
258                 return -errno;
259
260         if (!exec_label) {
261                 /* If there is no context set for next exec let's use context
262                    of target executable */
263                 r = getfilecon(exe, &fcon);
264                 if (r < 0)
265                         return -errno;
266         }
267
268         bcon = context_new(mycon);
269         if (!bcon)
270                 return -ENOMEM;
271
272         pcon = context_new(peercon);
273         if (!pcon)
274                 return -ENOMEM;
275
276         range = context_range_get(pcon);
277         if (!range)
278                 return -errno;
279
280         r = context_range_set(bcon, range);
281         if (r)
282                 return -errno;
283
284         freecon(mycon);
285         mycon = strdup(context_str(bcon));
286         if (!mycon)
287                 return -ENOMEM;
288
289         sclass = string_to_security_class("process");
290         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
291         if (r < 0)
292                 return -errno;
293 #endif
294
295         return r;
296 }
297
298 void mac_selinux_free(char *label) {
299
300 #ifdef HAVE_SELINUX
301         if (!mac_selinux_use())
302                 return;
303
304         freecon((security_context_t) label);
305 #endif
306 }
307
308 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
309         int r = 0;
310
311 #ifdef HAVE_SELINUX
312         _cleanup_security_context_free_ security_context_t filecon = NULL;
313
314         assert(path);
315
316         if (!label_hnd)
317                 return 0;
318
319         if (path_is_absolute(path))
320                 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
321         else {
322                 _cleanup_free_ char *newpath;
323
324                 newpath = path_make_absolute_cwd(path);
325                 if (!newpath)
326                         return -ENOMEM;
327
328                 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
329         }
330
331         /* No context specified by the policy? Proceed without setting it. */
332         if (r < 0 && errno == ENOENT)
333                 return 0;
334
335         if (r < 0)
336                 r = -errno;
337         else {
338                 r = setfscreatecon(filecon);
339                 if (r < 0) {
340                         log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
341                         r = -errno;
342                 }
343         }
344
345         if (r < 0 && security_getenforce() == 0)
346                 r = 0;
347 #endif
348
349         return r;
350 }
351
352 void mac_selinux_create_file_clear(void) {
353
354 #ifdef HAVE_SELINUX
355         PROTECT_ERRNO;
356
357         if (!mac_selinux_use())
358                 return;
359
360         setfscreatecon(NULL);
361 #endif
362 }
363
364 int mac_selinux_create_socket_prepare(const char *label) {
365
366 #ifdef HAVE_SELINUX
367         if (!mac_selinux_use())
368                 return 0;
369
370         assert(label);
371
372         if (setsockcreatecon((security_context_t) label) < 0) {
373                 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
374
375                 if (security_getenforce() == 1)
376                         return -errno;
377         }
378 #endif
379
380         return 0;
381 }
382
383 void mac_selinux_create_socket_clear(void) {
384
385 #ifdef HAVE_SELINUX
386         PROTECT_ERRNO;
387
388         if (!mac_selinux_use())
389                 return;
390
391         setsockcreatecon(NULL);
392 #endif
393 }
394
395 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
396
397         /* Binds a socket and label its file system object according to the SELinux policy */
398
399 #ifdef HAVE_SELINUX
400         _cleanup_security_context_free_ security_context_t fcon = NULL;
401         const struct sockaddr_un *un;
402         char *path;
403         int r;
404
405         assert(fd >= 0);
406         assert(addr);
407         assert(addrlen >= sizeof(sa_family_t));
408
409         if (!label_hnd)
410                 goto skipped;
411
412         /* Filter out non-local sockets */
413         if (addr->sa_family != AF_UNIX)
414                 goto skipped;
415
416         /* Filter out anonymous sockets */
417         if (addrlen < sizeof(sa_family_t) + 1)
418                 goto skipped;
419
420         /* Filter out abstract namespace sockets */
421         un = (const struct sockaddr_un*) addr;
422         if (un->sun_path[0] == 0)
423                 goto skipped;
424
425         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
426
427         if (path_is_absolute(path))
428                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
429         else {
430                 _cleanup_free_ char *newpath;
431
432                 newpath = path_make_absolute_cwd(path);
433                 if (!newpath)
434                         return -ENOMEM;
435
436                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
437         }
438
439         if (r == 0)
440                 r = setfscreatecon(fcon);
441
442         if (r < 0 && errno != ENOENT) {
443                 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
444
445                 if (security_getenforce() == 1) {
446                         r = -errno;
447                         goto finish;
448                 }
449         }
450
451         r = bind(fd, addr, addrlen);
452         if (r < 0)
453                 r = -errno;
454
455 finish:
456         setfscreatecon(NULL);
457         return r;
458
459 skipped:
460 #endif
461         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
462 }