chiark / gitweb /
util: fix tty_is_vc_resolve() in a container where /sys/class/tty/console/active...
[elogind.git] / src / shared / label.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 <sys/stat.h>
24 #include <unistd.h>
25 #include <malloc.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28
29 #include "label.h"
30 #include "util.h"
31
32 #ifdef HAVE_SELINUX
33 #include <selinux/selinux.h>
34 #include <selinux/label.h>
35
36 static struct selabel_handle *label_hnd = NULL;
37
38 static int use_selinux_cached = -1;
39
40 static inline bool use_selinux(void) {
41
42         if (use_selinux_cached < 0)
43                 use_selinux_cached = is_selinux_enabled() > 0;
44
45         return use_selinux_cached;
46 }
47
48 void label_retest_selinux(void) {
49         use_selinux_cached = -1;
50 }
51
52 #endif
53
54 int label_init(const char *prefix) {
55         int r = 0;
56
57 #ifdef HAVE_SELINUX
58         usec_t before_timestamp, after_timestamp;
59         struct mallinfo before_mallinfo, after_mallinfo;
60
61         if (!use_selinux())
62                 return 0;
63
64         if (label_hnd)
65                 return 0;
66
67         before_mallinfo = mallinfo();
68         before_timestamp = now(CLOCK_MONOTONIC);
69
70         if (prefix) {
71                 struct selinux_opt options[] = {
72                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
73                 };
74
75                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
76         } else
77                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
78
79         if (!label_hnd) {
80                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
81                          "Failed to initialize SELinux context: %m");
82                 r = security_getenforce() == 1 ? -errno : 0;
83         } else  {
84                 char timespan[FORMAT_TIMESPAN_MAX];
85                 int l;
86
87                 after_timestamp = now(CLOCK_MONOTONIC);
88                 after_mallinfo = mallinfo();
89
90                 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
91
92                 log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
93                          format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
94                          (l+1023)/1024);
95         }
96 #endif
97
98         return r;
99 }
100
101 int label_fix(const char *path, bool ignore_enoent) {
102         int r = 0;
103
104 #ifdef HAVE_SELINUX
105         struct stat st;
106         security_context_t fcon;
107
108         if (!use_selinux() || !label_hnd)
109                 return 0;
110
111         r = lstat(path, &st);
112         if (r == 0) {
113                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
114
115                 /* If there's no label to set, then exit without warning */
116                 if (r < 0 && errno == ENOENT)
117                         return 0;
118
119                 if (r == 0) {
120                         r = lsetfilecon(path, fcon);
121                         freecon(fcon);
122
123                         /* If the FS doesn't support labels, then exit without warning */
124                         if (r < 0 && errno == ENOTSUP)
125                                 return 0;
126                 }
127         }
128
129         if (r < 0) {
130                 /* Ignore ENOENT in some cases */
131                 if (ignore_enoent && errno == ENOENT)
132                         return 0;
133
134                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
135                          "Unable to fix label of %s: %m", path);
136                 r = security_getenforce() == 1 ? -errno : 0;
137         }
138 #endif
139
140         return r;
141 }
142
143 void label_finish(void) {
144
145 #ifdef HAVE_SELINUX
146         if (use_selinux() && label_hnd)
147                 selabel_close(label_hnd);
148 #endif
149 }
150
151 int label_get_create_label_from_exe(const char *exe, char **label) {
152
153         int r = 0;
154
155 #ifdef HAVE_SELINUX
156         security_context_t mycon = NULL, fcon = NULL;
157         security_class_t sclass;
158
159         if (!use_selinux()) {
160                 *label = NULL;
161                 return 0;
162         }
163
164         r = getcon(&mycon);
165         if (r < 0)
166                 goto fail;
167
168         r = getfilecon(exe, &fcon);
169         if (r < 0)
170                 goto fail;
171
172         sclass = string_to_security_class("process");
173         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
174         if (r == 0)
175                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
176
177 fail:
178         if (r < 0 && security_getenforce() == 1)
179                 r = -errno;
180
181         freecon(mycon);
182         freecon(fcon);
183 #endif
184
185         return r;
186 }
187
188 int label_context_set(const char *path, mode_t mode) {
189         int r = 0;
190
191 #ifdef HAVE_SELINUX
192         security_context_t filecon = NULL;
193
194         if (!use_selinux() || !label_hnd)
195                 return 0;
196
197         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
198         if (r < 0)
199                 r = -errno;
200         else if (r == 0) {
201                 r = setfscreatecon(filecon);
202                 if (r < 0) {
203                         log_error("Failed to set SELinux file context on %s: %m", path);
204                         r = -errno;
205                 }
206
207                 freecon(filecon);
208         }
209
210         if (r < 0 && security_getenforce() == 0)
211                 r = 0;
212 #endif
213
214         return r;
215 }
216
217 int label_socket_set(const char *label) {
218
219 #ifdef HAVE_SELINUX
220         if (!use_selinux())
221                 return 0;
222
223         if (setsockcreatecon((security_context_t) label) < 0) {
224                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
225                          "Failed to set SELinux context (%s) on socket: %m", label);
226
227                 if (security_getenforce() == 1)
228                         return -errno;
229         }
230 #endif
231
232         return 0;
233 }
234
235 void label_context_clear(void) {
236
237 #ifdef HAVE_SELINUX
238         if (!use_selinux())
239                 return;
240
241         setfscreatecon(NULL);
242 #endif
243 }
244
245 void label_socket_clear(void) {
246
247 #ifdef HAVE_SELINUX
248         if (!use_selinux())
249                 return;
250
251         setsockcreatecon(NULL);
252 #endif
253 }
254
255 void label_free(const char *label) {
256
257 #ifdef HAVE_SELINUX
258         if (!use_selinux())
259                 return;
260
261         freecon((security_context_t) label);
262 #endif
263 }
264
265 int label_mkdir(const char *path, mode_t mode) {
266
267         /* Creates a directory and labels it according to the SELinux policy */
268
269 #ifdef HAVE_SELINUX
270         int r;
271         security_context_t fcon = NULL;
272
273         if (!use_selinux() || !label_hnd)
274                 goto skipped;
275
276         if (path_is_absolute(path))
277                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
278         else {
279                 char *newpath;
280
281                 newpath = path_make_absolute_cwd(path);
282                 if (!newpath)
283                         return -ENOMEM;
284
285                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
286                 free(newpath);
287         }
288
289         if (r == 0)
290                 r = setfscreatecon(fcon);
291
292         if (r < 0 && errno != ENOENT) {
293                 log_error("Failed to set security context %s for %s: %m", fcon, path);
294
295                 if (security_getenforce() == 1) {
296                         r = -errno;
297                         goto finish;
298                 }
299         }
300
301         r = mkdir(path, mode);
302         if (r < 0)
303                 r = -errno;
304
305 finish:
306         setfscreatecon(NULL);
307         freecon(fcon);
308
309         return r;
310
311 skipped:
312 #endif
313         return mkdir(path, mode) < 0 ? -errno : 0;
314 }
315
316 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
317
318         /* Binds a socket and label its file system object according to the SELinux policy */
319
320 #ifdef HAVE_SELINUX
321         int r;
322         security_context_t fcon = NULL;
323         const struct sockaddr_un *un;
324         char *path = NULL;
325
326         assert(fd >= 0);
327         assert(addr);
328         assert(addrlen >= sizeof(sa_family_t));
329
330         if (!use_selinux() || !label_hnd)
331                 goto skipped;
332
333         /* Filter out non-local sockets */
334         if (addr->sa_family != AF_UNIX)
335                 goto skipped;
336
337         /* Filter out anonymous sockets */
338         if (addrlen < sizeof(sa_family_t) + 1)
339                 goto skipped;
340
341         /* Filter out abstract namespace sockets */
342         un = (const struct sockaddr_un*) addr;
343         if (un->sun_path[0] == 0)
344                 goto skipped;
345
346         path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
347         if (!path)
348                 return -ENOMEM;
349
350         if (path_is_absolute(path))
351                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
352         else {
353                 char *newpath;
354
355                 newpath = path_make_absolute_cwd(path);
356
357                 if (!newpath) {
358                         free(path);
359                         return -ENOMEM;
360                 }
361
362                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
363                 free(newpath);
364         }
365
366         if (r == 0)
367                 r = setfscreatecon(fcon);
368
369         if (r < 0 && errno != ENOENT) {
370                 log_error("Failed to set security context %s for %s: %m", fcon, path);
371
372                 if (security_getenforce() == 1) {
373                         r = -errno;
374                         goto finish;
375                 }
376         }
377
378         r = bind(fd, addr, addrlen);
379         if (r < 0)
380                 r = -errno;
381
382 finish:
383         setfscreatecon(NULL);
384         freecon(fcon);
385         free(path);
386
387         return r;
388
389 skipped:
390 #endif
391         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
392 }