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