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