chiark / gitweb /
9a5f79d7aef91d928d448557fc5a55bb15443006
[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 *prefix) {
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 (prefix) {
72                 struct selinux_opt options[] = {
73                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
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, bool apply) {
267
268         /* Creates a directory and labels it according to the SELinux policy */
269 #ifdef HAVE_SELINUX
270         int r;
271         security_context_t fcon = NULL;
272
273         if (!apply || !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 }