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