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