chiark / gitweb /
util: simplify proc_cmdline() to reuse get_process_cmdline()
[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
27 #ifdef HAVE_SELINUX
28 #include <selinux/selinux.h>
29 #include <selinux/label.h>
30 #include <selinux/context.h>
31 #endif
32
33 #include "strv.h"
34 #include "path-util.h"
35 #include "selinux-util.h"
36
37 #ifdef HAVE_SELINUX
38 DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
39 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
40
41 #define _cleanup_security_context_free_ _cleanup_(freeconp)
42 #define _cleanup_context_free_ _cleanup_(context_freep)
43
44 static int cached_use = -1;
45 static struct selabel_handle *label_hnd = NULL;
46
47 #define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
48 #endif
49
50 bool mac_selinux_use(void) {
51 #ifdef HAVE_SELINUX
52         if (cached_use < 0)
53                 cached_use = is_selinux_enabled() > 0;
54
55         return cached_use;
56 #else
57         return false;
58 #endif
59 }
60
61 void mac_selinux_retest(void) {
62 #ifdef HAVE_SELINUX
63         cached_use = -1;
64 #endif
65 }
66
67 int mac_selinux_init(const char *prefix) {
68         int r = 0;
69
70 #ifdef HAVE_SELINUX
71         usec_t before_timestamp, after_timestamp;
72         struct mallinfo before_mallinfo, after_mallinfo;
73
74         if (!mac_selinux_use())
75                 return 0;
76
77         if (label_hnd)
78                 return 0;
79
80         before_mallinfo = mallinfo();
81         before_timestamp = now(CLOCK_MONOTONIC);
82
83         if (prefix) {
84                 struct selinux_opt options[] = {
85                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
86                 };
87
88                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
89         } else
90                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
91
92         if (!label_hnd) {
93                 log_enforcing("Failed to initialize SELinux context: %m");
94                 r = security_getenforce() == 1 ? -errno : 0;
95         } else  {
96                 char timespan[FORMAT_TIMESPAN_MAX];
97                 int l;
98
99                 after_timestamp = now(CLOCK_MONOTONIC);
100                 after_mallinfo = mallinfo();
101
102                 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
103
104                 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
105                           format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
106                           (l+1023)/1024);
107         }
108 #endif
109
110         return r;
111 }
112
113 void mac_selinux_finish(void) {
114
115 #ifdef HAVE_SELINUX
116         if (!label_hnd)
117                 return;
118
119         selabel_close(label_hnd);
120 #endif
121 }
122
123 int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
124
125 #ifdef HAVE_SELINUX
126         struct stat st;
127         int r;
128
129         assert(path);
130
131         /* if mac_selinux_init() wasn't called before we are a NOOP */
132         if (!label_hnd)
133                 return 0;
134
135         r = lstat(path, &st);
136         if (r >= 0) {
137                 _cleanup_security_context_free_ security_context_t fcon = NULL;
138
139                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
140
141                 /* If there's no label to set, then exit without warning */
142                 if (r < 0 && errno == ENOENT)
143                         return 0;
144
145                 if (r >= 0) {
146                         r = lsetfilecon(path, fcon);
147
148                         /* If the FS doesn't support labels, then exit without warning */
149                         if (r < 0 && errno == ENOTSUP)
150                                 return 0;
151                 }
152         }
153
154         if (r < 0) {
155                 /* Ignore ENOENT in some cases */
156                 if (ignore_enoent && errno == ENOENT)
157                         return 0;
158
159                 if (ignore_erofs && errno == EROFS)
160                         return 0;
161
162                 log_enforcing("Unable to fix SELinux security context of %s: %m", path);
163                 if (security_getenforce() == 1)
164                         return -errno;
165         }
166 #endif
167
168         return 0;
169 }
170
171 int mac_selinux_apply(const char *path, const char *label) {
172
173 #ifdef HAVE_SELINUX
174         assert(path);
175         assert(label);
176
177         if (!mac_selinux_use())
178                 return 0;
179
180         if (setfilecon(path, (security_context_t) label) < 0) {
181                 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
182                 if (security_getenforce() == 1)
183                         return -errno;
184         }
185 #endif
186         return 0;
187 }
188
189 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
190         int r = -EOPNOTSUPP;
191
192 #ifdef HAVE_SELINUX
193         _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
194         security_class_t sclass;
195
196         assert(exe);
197         assert(label);
198
199         if (!mac_selinux_use())
200                 return -EOPNOTSUPP;
201
202         r = getcon(&mycon);
203         if (r < 0)
204                 return -errno;
205
206         r = getfilecon(exe, &fcon);
207         if (r < 0)
208                 return -errno;
209
210         sclass = string_to_security_class("process");
211         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
212         if (r < 0)
213                 return -errno;
214 #endif
215
216         return r;
217 }
218
219 int mac_selinux_get_our_label(char **label) {
220         int r = -EOPNOTSUPP;
221
222         assert(label);
223
224 #ifdef HAVE_SELINUX
225         if (!mac_selinux_use())
226                 return -EOPNOTSUPP;
227
228         r = getcon(label);
229         if (r < 0)
230                 return -errno;
231 #endif
232
233         return r;
234 }
235
236 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label) {
237         int r = -EOPNOTSUPP;
238
239 #ifdef HAVE_SELINUX
240         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
241         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
242         security_class_t sclass;
243         const char *range = NULL;
244
245         assert(socket_fd >= 0);
246         assert(exe);
247         assert(label);
248
249         if (!mac_selinux_use())
250                 return -EOPNOTSUPP;
251
252         r = getcon(&mycon);
253         if (r < 0)
254                 return -errno;
255
256         r = getpeercon(socket_fd, &peercon);
257         if (r < 0)
258                 return -errno;
259
260         r = getexeccon(&fcon);
261         if (r < 0)
262                 return -errno;
263
264         if (!fcon) {
265                 /* If there is no context set for next exec let's use context
266                    of target executable */
267                 r = getfilecon(exe, &fcon);
268                 if (r < 0)
269                         return -errno;
270         }
271
272         bcon = context_new(mycon);
273         if (!bcon)
274                 return -ENOMEM;
275
276         pcon = context_new(peercon);
277         if (!pcon)
278                 return -ENOMEM;
279
280         range = context_range_get(pcon);
281         if (!range)
282                 return -errno;
283
284         r = context_range_set(bcon, range);
285         if (r)
286                 return -errno;
287
288         freecon(mycon);
289         mycon = strdup(context_str(bcon));
290         if (!mycon)
291                 return -ENOMEM;
292
293         sclass = string_to_security_class("process");
294         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
295         if (r < 0)
296                 return -errno;
297 #endif
298
299         return r;
300 }
301
302 void mac_selinux_free(char *label) {
303
304 #ifdef HAVE_SELINUX
305         if (!mac_selinux_use())
306                 return;
307
308         freecon((security_context_t) label);
309 #endif
310 }
311
312 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
313         int r = 0;
314
315 #ifdef HAVE_SELINUX
316         _cleanup_security_context_free_ security_context_t filecon = NULL;
317
318         assert(path);
319
320         if (!label_hnd)
321                 return 0;
322
323         if (path_is_absolute(path))
324                 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
325         else {
326                 _cleanup_free_ char *newpath;
327
328                 newpath = path_make_absolute_cwd(path);
329                 if (!newpath)
330                         return -ENOMEM;
331
332                 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
333         }
334
335         /* No context specified by the policy? Proceed without setting it. */
336         if (r < 0 && errno == ENOENT)
337                 return 0;
338
339         if (r < 0)
340                 r = -errno;
341         else {
342                 r = setfscreatecon(filecon);
343                 if (r < 0) {
344                         log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
345                         r = -errno;
346                 }
347         }
348
349         if (r < 0 && security_getenforce() == 0)
350                 r = 0;
351 #endif
352
353         return r;
354 }
355
356 void mac_selinux_create_file_clear(void) {
357
358 #ifdef HAVE_SELINUX
359         PROTECT_ERRNO;
360
361         if (!mac_selinux_use())
362                 return;
363
364         setfscreatecon(NULL);
365 #endif
366 }
367
368 int mac_selinux_create_socket_prepare(const char *label) {
369
370 #ifdef HAVE_SELINUX
371         if (!mac_selinux_use())
372                 return 0;
373
374         assert(label);
375
376         if (setsockcreatecon((security_context_t) label) < 0) {
377                 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
378
379                 if (security_getenforce() == 1)
380                         return -errno;
381         }
382 #endif
383
384         return 0;
385 }
386
387 void mac_selinux_create_socket_clear(void) {
388
389 #ifdef HAVE_SELINUX
390         PROTECT_ERRNO;
391
392         if (!mac_selinux_use())
393                 return;
394
395         setsockcreatecon(NULL);
396 #endif
397 }
398
399 int mac_selinux_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         _cleanup_security_context_free_ 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 (!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_enforcing("Failed to set SELinux 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         return r;
462
463 skipped:
464 #endif
465         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
466 }