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