chiark / gitweb /
systemd-detect-virt: detect s390 virtualization
[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         PROTECT_ERRNO;
297
298         if (!use_selinux())
299                 return;
300
301         setfscreatecon(NULL);
302 #endif
303 }
304
305 void label_socket_clear(void) {
306
307 #ifdef HAVE_SELINUX
308         PROTECT_ERRNO;
309
310         if (!use_selinux())
311                 return;
312
313         setsockcreatecon(NULL);
314 #endif
315 }
316
317 void label_free(const char *label) {
318
319 #ifdef HAVE_SELINUX
320         if (!use_selinux())
321                 return;
322
323         freecon((security_context_t) label);
324 #endif
325 }
326
327 static int label_mkdir_selinux(const char *path, mode_t mode) {
328         int r = 0;
329
330 #ifdef HAVE_SELINUX
331         /* Creates a directory and labels it according to the SELinux policy */
332         security_context_t fcon = NULL;
333
334         if (!label_hnd)
335                 return 0;
336
337         if (path_is_absolute(path))
338                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
339         else {
340                 _cleanup_free_ char *newpath;
341
342                 newpath = path_make_absolute_cwd(path);
343                 if (!newpath)
344                         return -ENOMEM;
345
346                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
347         }
348
349         if (r == 0)
350                 r = setfscreatecon(fcon);
351
352         if (r < 0 && errno != ENOENT) {
353                 log_error("Failed to set security context %s for %s: %m", fcon, path);
354
355                 if (security_getenforce() == 1) {
356                         r = -errno;
357                         goto finish;
358                 }
359         }
360
361         r = mkdir(path, mode);
362         if (r < 0)
363                 r = -errno;
364
365 finish:
366         setfscreatecon(NULL);
367         freecon(fcon);
368 #endif
369
370         return r;
371 }
372
373 int label_mkdir(const char *path, mode_t mode) {
374         int r;
375
376         if (use_selinux()) {
377                 r = label_mkdir_selinux(path, mode);
378                 if (r < 0)
379                         return r;
380         }
381
382         if (use_smack()) {
383                 r = mkdir(path, mode);
384                 if (r < 0 && errno != EEXIST)
385                         return -errno;
386
387                 r = smack_relabel_in_dev(path);
388                 if (r < 0)
389                         return r;
390         }
391
392         r = mkdir(path, mode);
393         if (r < 0 && errno != EEXIST)
394                 return -errno;
395
396         return 0;
397 }
398
399 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
400
401         /* Binds a socket and label its file system object according to the SELinux policy */
402
403 #ifdef HAVE_SELINUX
404         security_context_t fcon = NULL;
405         const struct sockaddr_un *un;
406         char *path;
407         int r;
408
409         assert(fd >= 0);
410         assert(addr);
411         assert(addrlen >= sizeof(sa_family_t));
412
413         if (!use_selinux() || !label_hnd)
414                 goto skipped;
415
416         /* Filter out non-local sockets */
417         if (addr->sa_family != AF_UNIX)
418                 goto skipped;
419
420         /* Filter out anonymous sockets */
421         if (addrlen < sizeof(sa_family_t) + 1)
422                 goto skipped;
423
424         /* Filter out abstract namespace sockets */
425         un = (const struct sockaddr_un*) addr;
426         if (un->sun_path[0] == 0)
427                 goto skipped;
428
429         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
430
431         if (path_is_absolute(path))
432                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
433         else {
434                 _cleanup_free_ char *newpath;
435
436                 newpath = path_make_absolute_cwd(path);
437                 if (!newpath)
438                         return -ENOMEM;
439
440                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
441         }
442
443         if (r == 0)
444                 r = setfscreatecon(fcon);
445
446         if (r < 0 && errno != ENOENT) {
447                 log_error("Failed to set security context %s for %s: %m", fcon, path);
448
449                 if (security_getenforce() == 1) {
450                         r = -errno;
451                         goto finish;
452                 }
453         }
454
455         r = bind(fd, addr, addrlen);
456         if (r < 0)
457                 r = -errno;
458
459 finish:
460         setfscreatecon(NULL);
461         freecon(fcon);
462
463         return r;
464
465 skipped:
466 #endif
467         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
468 }
469
470 int label_apply(const char *path, const char *label) {
471         int r = 0;
472
473 #ifdef HAVE_SELINUX
474         if (!use_selinux())
475                 return 0;
476
477         r = setfilecon(path, (char *)label);
478 #endif
479         return r;
480 }