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