chiark / gitweb /
systemctl: add add-wants and add-requires verbs
[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
256 #ifdef HAVE_SELINUX
257         char *l = NULL;
258
259         r = getcon(&l);
260         if (r < 0)
261                 return r;
262
263         *label = l;
264 #endif
265
266         return r;
267 }
268
269 int label_get_child_mls_label(int socket_fd, const char *exe, char **label) {
270         int r = -EOPNOTSUPP;
271
272 #ifdef HAVE_SELINUX
273
274         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL;
275         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
276         security_class_t sclass;
277
278         const char *range = NULL;
279
280         assert(socket_fd >= 0);
281         assert(exe);
282         assert(label);
283
284         r = getcon(&mycon);
285         if (r < 0) {
286                 r = -EINVAL;
287                 goto out;
288         }
289
290         r = getpeercon(socket_fd, &peercon);
291         if (r < 0) {
292                 r = -EINVAL;
293                 goto out;
294         }
295
296         r = getexeccon(&fcon);
297         if (r < 0) {
298                 r = -EINVAL;
299                 goto out;
300         }
301
302         if (!fcon) {
303                 /* If there is no context set for next exec let's use context
304                    of target executable */
305                 r = getfilecon(exe, &fcon);
306                 if (r < 0) {
307                         r = -errno;
308                         goto out;
309                 }
310         }
311
312         bcon = context_new(mycon);
313         if (!bcon) {
314                 r = -ENOMEM;
315                 goto out;
316         }
317
318         pcon = context_new(peercon);
319         if (!pcon) {
320                 r = -ENOMEM;
321                 goto out;
322         }
323
324         range = context_range_get(pcon);
325         if (!range) {
326                 r = -errno;
327                 goto out;
328         }
329
330         r = context_range_set(bcon, range);
331         if (r) {
332                 r = -errno;
333                 goto out;
334         }
335
336         freecon(mycon);
337         mycon = context_str(bcon);
338         if (!mycon) {
339                 r = -errno;
340                 goto out;
341         }
342
343         sclass = string_to_security_class("process");
344         r = security_compute_create(mycon, fcon, sclass, &ret);
345         if (r < 0) {
346                 r = -EINVAL;
347                 goto out;
348         }
349
350         *label = ret;
351         r = 0;
352
353 out:
354         if (r < 0 && security_getenforce() == 1)
355                 return r;
356 #endif
357         return r;
358 }
359
360 int label_context_set(const char *path, mode_t mode) {
361         int r = 0;
362
363 #ifdef HAVE_SELINUX
364         security_context_t filecon = NULL;
365
366         if (!use_selinux() || !label_hnd)
367                 return 0;
368
369         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
370         if (r < 0 && errno != ENOENT)
371                 r = -errno;
372         else if (r == 0) {
373                 r = setfscreatecon(filecon);
374                 if (r < 0) {
375                         log_error("Failed to set SELinux file context on %s: %m", path);
376                         r = -errno;
377                 }
378
379                 freecon(filecon);
380         }
381
382         if (r < 0 && security_getenforce() == 0)
383                 r = 0;
384 #endif
385
386         return r;
387 }
388
389 int label_socket_set(const char *label) {
390
391 #ifdef HAVE_SELINUX
392         if (!use_selinux())
393                 return 0;
394
395         if (setsockcreatecon((security_context_t) label) < 0) {
396                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
397                          "Failed to set SELinux context (%s) on socket: %m", label);
398
399                 if (security_getenforce() == 1)
400                         return -errno;
401         }
402 #endif
403
404         return 0;
405 }
406
407 void label_context_clear(void) {
408
409 #ifdef HAVE_SELINUX
410         PROTECT_ERRNO;
411
412         if (!use_selinux())
413                 return;
414
415         setfscreatecon(NULL);
416 #endif
417 }
418
419 void label_socket_clear(void) {
420
421 #ifdef HAVE_SELINUX
422         PROTECT_ERRNO;
423
424         if (!use_selinux())
425                 return;
426
427         setsockcreatecon(NULL);
428 #endif
429 }
430
431 void label_free(const char *label) {
432
433 #ifdef HAVE_SELINUX
434         if (!use_selinux())
435                 return;
436
437         freecon((security_context_t) label);
438 #endif
439 }
440
441 static int label_mkdir_selinux(const char *path, mode_t mode) {
442         int r = 0;
443
444 #ifdef HAVE_SELINUX
445         /* Creates a directory and labels it according to the SELinux policy */
446         security_context_t fcon = NULL;
447
448         if (!label_hnd)
449                 return 0;
450
451         if (path_is_absolute(path))
452                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
453         else {
454                 _cleanup_free_ char *newpath;
455
456                 newpath = path_make_absolute_cwd(path);
457                 if (!newpath)
458                         return -ENOMEM;
459
460                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
461         }
462
463         if (r == 0)
464                 r = setfscreatecon(fcon);
465
466         if (r < 0 && errno != ENOENT) {
467                 log_error("Failed to set security context %s for %s: %m", fcon, path);
468
469                 if (security_getenforce() == 1) {
470                         r = -errno;
471                         goto finish;
472                 }
473         }
474
475         r = mkdir(path, mode);
476         if (r < 0)
477                 r = -errno;
478
479 finish:
480         setfscreatecon(NULL);
481         freecon(fcon);
482 #endif
483
484         return r;
485 }
486
487 int label_mkdir(const char *path, mode_t mode) {
488         int r;
489
490         if (use_selinux()) {
491                 r = label_mkdir_selinux(path, mode);
492                 if (r < 0)
493                         return r;
494         }
495
496         if (use_smack()) {
497                 r = mkdir(path, mode);
498                 if (r < 0 && errno != EEXIST)
499                         return -errno;
500
501                 r = smack_relabel_in_dev(path);
502                 if (r < 0)
503                         return r;
504         }
505
506         r = mkdir(path, mode);
507         if (r < 0 && errno != EEXIST)
508                 return -errno;
509
510         return 0;
511 }
512
513 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
514
515         /* Binds a socket and label its file system object according to the SELinux policy */
516
517 #ifdef HAVE_SELINUX
518         security_context_t fcon = NULL;
519         const struct sockaddr_un *un;
520         char *path;
521         int r;
522
523         assert(fd >= 0);
524         assert(addr);
525         assert(addrlen >= sizeof(sa_family_t));
526
527         if (!use_selinux() || !label_hnd)
528                 goto skipped;
529
530         /* Filter out non-local sockets */
531         if (addr->sa_family != AF_UNIX)
532                 goto skipped;
533
534         /* Filter out anonymous sockets */
535         if (addrlen < sizeof(sa_family_t) + 1)
536                 goto skipped;
537
538         /* Filter out abstract namespace sockets */
539         un = (const struct sockaddr_un*) addr;
540         if (un->sun_path[0] == 0)
541                 goto skipped;
542
543         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
544
545         if (path_is_absolute(path))
546                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
547         else {
548                 _cleanup_free_ char *newpath;
549
550                 newpath = path_make_absolute_cwd(path);
551                 if (!newpath)
552                         return -ENOMEM;
553
554                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
555         }
556
557         if (r == 0)
558                 r = setfscreatecon(fcon);
559
560         if (r < 0 && errno != ENOENT) {
561                 log_error("Failed to set security context %s for %s: %m", fcon, path);
562
563                 if (security_getenforce() == 1) {
564                         r = -errno;
565                         goto finish;
566                 }
567         }
568
569         r = bind(fd, addr, addrlen);
570         if (r < 0)
571                 r = -errno;
572
573 finish:
574         setfscreatecon(NULL);
575         freecon(fcon);
576
577         return r;
578
579 skipped:
580 #endif
581         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
582 }
583
584 int label_apply(const char *path, const char *label) {
585         int r = 0;
586
587 #ifdef HAVE_SELINUX
588         if (!use_selinux())
589                 return 0;
590
591         r = setfilecon(path, (char *)label);
592 #endif
593         return r;
594 }