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