1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
26 #include <sys/socket.h>
31 #include "path-util.h"
34 #include <selinux/selinux.h>
35 #include <selinux/label.h>
37 static struct selabel_handle *label_hnd = NULL;
39 static int use_selinux_cached = -1;
41 static inline bool use_selinux(void) {
43 if (use_selinux_cached < 0)
44 use_selinux_cached = is_selinux_enabled() > 0;
46 return use_selinux_cached;
49 void label_retest_selinux(void) {
50 use_selinux_cached = -1;
55 int label_init(const char *prefix) {
59 usec_t before_timestamp, after_timestamp;
60 struct mallinfo before_mallinfo, after_mallinfo;
68 before_mallinfo = mallinfo();
69 before_timestamp = now(CLOCK_MONOTONIC);
72 struct selinux_opt options[] = {
73 { .type = SELABEL_OPT_SUBSET, .value = prefix },
76 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
78 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
81 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
82 "Failed to initialize SELinux context: %m");
83 r = security_getenforce() == 1 ? -errno : 0;
85 char timespan[FORMAT_TIMESPAN_MAX];
88 after_timestamp = now(CLOCK_MONOTONIC);
89 after_mallinfo = mallinfo();
91 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
93 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
94 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
102 int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
107 security_context_t fcon;
109 if (!use_selinux() || !label_hnd)
112 r = lstat(path, &st);
114 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
116 /* If there's no label to set, then exit without warning */
117 if (r < 0 && errno == ENOENT)
121 r = lsetfilecon(path, fcon);
124 /* If the FS doesn't support labels, then exit without warning */
125 if (r < 0 && errno == ENOTSUP)
131 /* Ignore ENOENT in some cases */
132 if (ignore_enoent && errno == ENOENT)
135 if (ignore_erofs && errno == EROFS)
138 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
139 "Unable to fix label of %s: %m", path);
140 r = security_getenforce() == 1 ? -errno : 0;
147 void label_finish(void) {
150 if (use_selinux() && label_hnd)
151 selabel_close(label_hnd);
155 int label_get_create_label_from_exe(const char *exe, char **label) {
160 security_context_t mycon = NULL, fcon = NULL;
161 security_class_t sclass;
163 if (!use_selinux()) {
172 r = getfilecon(exe, &fcon);
176 sclass = string_to_security_class("process");
177 r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
179 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
182 if (r < 0 && security_getenforce() == 1)
192 int label_context_set(const char *path, mode_t mode) {
196 security_context_t filecon = NULL;
198 if (!use_selinux() || !label_hnd)
201 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
205 r = setfscreatecon(filecon);
207 log_error("Failed to set SELinux file context on %s: %m", path);
214 if (r < 0 && security_getenforce() == 0)
221 int label_socket_set(const char *label) {
227 if (setsockcreatecon((security_context_t) label) < 0) {
228 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
229 "Failed to set SELinux context (%s) on socket: %m", label);
231 if (security_getenforce() == 1)
239 void label_context_clear(void) {
245 setfscreatecon(NULL);
249 void label_socket_clear(void) {
255 setsockcreatecon(NULL);
259 void label_free(const char *label) {
265 freecon((security_context_t) label);
269 int label_mkdir(const char *path, mode_t mode, bool apply) {
271 /* Creates a directory and labels it according to the SELinux policy */
274 security_context_t fcon = NULL;
276 if (!apply || !use_selinux() || !label_hnd)
279 if (path_is_absolute(path))
280 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
284 newpath = path_make_absolute_cwd(path);
288 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
293 r = setfscreatecon(fcon);
295 if (r < 0 && errno != ENOENT) {
296 log_error("Failed to set security context %s for %s: %m", fcon, path);
298 if (security_getenforce() == 1) {
304 r = mkdir(path, mode);
309 setfscreatecon(NULL);
316 return mkdir(path, mode) < 0 ? -errno : 0;
319 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
321 /* Binds a socket and label its file system object according to the SELinux policy */
325 security_context_t fcon = NULL;
326 const struct sockaddr_un *un;
331 assert(addrlen >= sizeof(sa_family_t));
333 if (!use_selinux() || !label_hnd)
336 /* Filter out non-local sockets */
337 if (addr->sa_family != AF_UNIX)
340 /* Filter out anonymous sockets */
341 if (addrlen < sizeof(sa_family_t) + 1)
344 /* Filter out abstract namespace sockets */
345 un = (const struct sockaddr_un*) addr;
346 if (un->sun_path[0] == 0)
349 path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
353 if (path_is_absolute(path))
354 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
358 newpath = path_make_absolute_cwd(path);
365 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
370 r = setfscreatecon(fcon);
372 if (r < 0 && errno != ENOENT) {
373 log_error("Failed to set security context %s for %s: %m", fcon, path);
375 if (security_getenforce() == 1) {
381 r = bind(fd, addr, addrlen);
386 setfscreatecon(NULL);
394 return bind(fd, addr, addrlen) < 0 ? -errno : 0;