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