chiark / gitweb /
mount-setup: don't complain if we try to fix the label of a dir beneath a mount but...
[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, bool ignore_erofs) {
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                 if (ignore_erofs && errno == EROFS)
136                         return 0;
137
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;
141         }
142 #endif
143
144         return r;
145 }
146
147 void label_finish(void) {
148
149 #ifdef HAVE_SELINUX
150         if (use_selinux() && label_hnd)
151                 selabel_close(label_hnd);
152 #endif
153 }
154
155 int label_get_create_label_from_exe(const char *exe, char **label) {
156
157         int r = 0;
158
159 #ifdef HAVE_SELINUX
160         security_context_t mycon = NULL, fcon = NULL;
161         security_class_t sclass;
162
163         if (!use_selinux()) {
164                 *label = NULL;
165                 return 0;
166         }
167
168         r = getcon(&mycon);
169         if (r < 0)
170                 goto fail;
171
172         r = getfilecon(exe, &fcon);
173         if (r < 0)
174                 goto fail;
175
176         sclass = string_to_security_class("process");
177         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
178         if (r == 0)
179                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
180
181 fail:
182         if (r < 0 && security_getenforce() == 1)
183                 r = -errno;
184
185         freecon(mycon);
186         freecon(fcon);
187 #endif
188
189         return r;
190 }
191
192 int label_context_set(const char *path, mode_t mode) {
193         int r = 0;
194
195 #ifdef HAVE_SELINUX
196         security_context_t filecon = NULL;
197
198         if (!use_selinux() || !label_hnd)
199                 return 0;
200
201         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
202         if (r < 0)
203                 r = -errno;
204         else if (r == 0) {
205                 r = setfscreatecon(filecon);
206                 if (r < 0) {
207                         log_error("Failed to set SELinux file context on %s: %m", path);
208                         r = -errno;
209                 }
210
211                 freecon(filecon);
212         }
213
214         if (r < 0 && security_getenforce() == 0)
215                 r = 0;
216 #endif
217
218         return r;
219 }
220
221 int label_socket_set(const char *label) {
222
223 #ifdef HAVE_SELINUX
224         if (!use_selinux())
225                 return 0;
226
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);
230
231                 if (security_getenforce() == 1)
232                         return -errno;
233         }
234 #endif
235
236         return 0;
237 }
238
239 void label_context_clear(void) {
240
241 #ifdef HAVE_SELINUX
242         if (!use_selinux())
243                 return;
244
245         setfscreatecon(NULL);
246 #endif
247 }
248
249 void label_socket_clear(void) {
250
251 #ifdef HAVE_SELINUX
252         if (!use_selinux())
253                 return;
254
255         setsockcreatecon(NULL);
256 #endif
257 }
258
259 void label_free(const char *label) {
260
261 #ifdef HAVE_SELINUX
262         if (!use_selinux())
263                 return;
264
265         freecon((security_context_t) label);
266 #endif
267 }
268
269 int label_mkdir(const char *path, mode_t mode, bool apply) {
270
271         /* Creates a directory and labels it according to the SELinux policy */
272 #ifdef HAVE_SELINUX
273         int r;
274         security_context_t fcon = NULL;
275
276         if (!apply || !use_selinux() || !label_hnd)
277                 goto skipped;
278
279         if (path_is_absolute(path))
280                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
281         else {
282                 char *newpath;
283
284                 newpath = path_make_absolute_cwd(path);
285                 if (!newpath)
286                         return -ENOMEM;
287
288                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
289                 free(newpath);
290         }
291
292         if (r == 0)
293                 r = setfscreatecon(fcon);
294
295         if (r < 0 && errno != ENOENT) {
296                 log_error("Failed to set security context %s for %s: %m", fcon, path);
297
298                 if (security_getenforce() == 1) {
299                         r = -errno;
300                         goto finish;
301                 }
302         }
303
304         r = mkdir(path, mode);
305         if (r < 0)
306                 r = -errno;
307
308 finish:
309         setfscreatecon(NULL);
310         freecon(fcon);
311
312         return r;
313
314 skipped:
315 #endif
316         return mkdir(path, mode) < 0 ? -errno : 0;
317 }
318
319 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
320
321         /* Binds a socket and label its file system object according to the SELinux policy */
322
323 #ifdef HAVE_SELINUX
324         int r;
325         security_context_t fcon = NULL;
326         const struct sockaddr_un *un;
327         char *path = NULL;
328
329         assert(fd >= 0);
330         assert(addr);
331         assert(addrlen >= sizeof(sa_family_t));
332
333         if (!use_selinux() || !label_hnd)
334                 goto skipped;
335
336         /* Filter out non-local sockets */
337         if (addr->sa_family != AF_UNIX)
338                 goto skipped;
339
340         /* Filter out anonymous sockets */
341         if (addrlen < sizeof(sa_family_t) + 1)
342                 goto skipped;
343
344         /* Filter out abstract namespace sockets */
345         un = (const struct sockaddr_un*) addr;
346         if (un->sun_path[0] == 0)
347                 goto skipped;
348
349         path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
350         if (!path)
351                 return -ENOMEM;
352
353         if (path_is_absolute(path))
354                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
355         else {
356                 char *newpath;
357
358                 newpath = path_make_absolute_cwd(path);
359
360                 if (!newpath) {
361                         free(path);
362                         return -ENOMEM;
363                 }
364
365                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
366                 free(newpath);
367         }
368
369         if (r == 0)
370                 r = setfscreatecon(fcon);
371
372         if (r < 0 && errno != ENOENT) {
373                 log_error("Failed to set security context %s for %s: %m", fcon, path);
374
375                 if (security_getenforce() == 1) {
376                         r = -errno;
377                         goto finish;
378                 }
379         }
380
381         r = bind(fd, addr, addrlen);
382         if (r < 0)
383                 r = -errno;
384
385 finish:
386         setfscreatecon(NULL);
387         freecon(fcon);
388         free(path);
389
390         return r;
391
392 skipped:
393 #endif
394         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
395 }