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