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