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