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