Line data Source code
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 <malloc.h>
24 : #include <sys/un.h>
25 :
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 :
46 : #define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
47 : #endif
48 :
49 2 : bool mac_selinux_use(void) {
50 : #ifdef HAVE_SELINUX
51 : if (cached_use < 0)
52 : cached_use = is_selinux_enabled() > 0;
53 :
54 : return cached_use;
55 : #else
56 2 : return false;
57 : #endif
58 : }
59 :
60 0 : void mac_selinux_retest(void) {
61 : #ifdef HAVE_SELINUX
62 : cached_use = -1;
63 : #endif
64 0 : }
65 :
66 0 : int mac_selinux_init(const char *prefix) {
67 0 : int r = 0;
68 :
69 : #ifdef HAVE_SELINUX
70 : usec_t before_timestamp, after_timestamp;
71 : struct mallinfo before_mallinfo, after_mallinfo;
72 :
73 : if (!mac_selinux_use())
74 : return 0;
75 :
76 : if (label_hnd)
77 : return 0;
78 :
79 : before_mallinfo = mallinfo();
80 : before_timestamp = now(CLOCK_MONOTONIC);
81 :
82 : if (prefix) {
83 : struct selinux_opt options[] = {
84 : { .type = SELABEL_OPT_SUBSET, .value = prefix },
85 : };
86 :
87 : label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
88 : } else
89 : label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
90 :
91 : if (!label_hnd) {
92 : log_enforcing("Failed to initialize SELinux context: %m");
93 : r = security_getenforce() == 1 ? -errno : 0;
94 : } else {
95 : char timespan[FORMAT_TIMESPAN_MAX];
96 : int l;
97 :
98 : after_timestamp = now(CLOCK_MONOTONIC);
99 : after_mallinfo = mallinfo();
100 :
101 : l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
102 :
103 : log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
104 : format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
105 : (l+1023)/1024);
106 : }
107 : #endif
108 :
109 0 : return r;
110 : }
111 :
112 0 : void mac_selinux_finish(void) {
113 :
114 : #ifdef HAVE_SELINUX
115 : if (!label_hnd)
116 : return;
117 :
118 : selabel_close(label_hnd);
119 : label_hnd = NULL;
120 : #endif
121 0 : }
122 :
123 0 : 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 == EOPNOTSUPP)
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 0 : return 0;
169 : }
170 :
171 0 : 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 0 : return 0;
187 : }
188 :
189 0 : int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
190 0 : 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 0 : return r;
217 : }
218 :
219 0 : int mac_selinux_get_our_label(char **label) {
220 0 : int r = -EOPNOTSUPP;
221 :
222 0 : 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 0 : return r;
234 : }
235 :
236 0 : int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
237 0 : 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 : if (!exec_label) {
261 : /* If there is no context set for next exec let's use context
262 : of target executable */
263 : r = getfilecon(exe, &fcon);
264 : if (r < 0)
265 : return -errno;
266 : }
267 :
268 : bcon = context_new(mycon);
269 : if (!bcon)
270 : return -ENOMEM;
271 :
272 : pcon = context_new(peercon);
273 : if (!pcon)
274 : return -ENOMEM;
275 :
276 : range = context_range_get(pcon);
277 : if (!range)
278 : return -errno;
279 :
280 : r = context_range_set(bcon, range);
281 : if (r)
282 : return -errno;
283 :
284 : freecon(mycon);
285 : mycon = strdup(context_str(bcon));
286 : if (!mycon)
287 : return -ENOMEM;
288 :
289 : sclass = string_to_security_class("process");
290 : r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
291 : if (r < 0)
292 : return -errno;
293 : #endif
294 :
295 0 : return r;
296 : }
297 :
298 0 : void mac_selinux_free(char *label) {
299 :
300 : #ifdef HAVE_SELINUX
301 : if (!mac_selinux_use())
302 : return;
303 :
304 : freecon((security_context_t) label);
305 : #endif
306 0 : }
307 :
308 75 : int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
309 75 : int r = 0;
310 :
311 : #ifdef HAVE_SELINUX
312 : _cleanup_security_context_free_ security_context_t filecon = NULL;
313 :
314 : assert(path);
315 :
316 : if (!label_hnd)
317 : return 0;
318 :
319 : if (path_is_absolute(path))
320 : r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
321 : else {
322 : _cleanup_free_ char *newpath;
323 :
324 : newpath = path_make_absolute_cwd(path);
325 : if (!newpath)
326 : return -ENOMEM;
327 :
328 : r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
329 : }
330 :
331 : /* No context specified by the policy? Proceed without setting it. */
332 : if (r < 0 && errno == ENOENT)
333 : return 0;
334 :
335 : if (r < 0)
336 : r = -errno;
337 : else {
338 : r = setfscreatecon(filecon);
339 : if (r < 0) {
340 : log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
341 : r = -errno;
342 : }
343 : }
344 :
345 : if (r < 0 && security_getenforce() == 0)
346 : r = 0;
347 : #endif
348 :
349 75 : return r;
350 : }
351 :
352 75 : void mac_selinux_create_file_clear(void) {
353 :
354 : #ifdef HAVE_SELINUX
355 : PROTECT_ERRNO;
356 :
357 : if (!mac_selinux_use())
358 : return;
359 :
360 : setfscreatecon(NULL);
361 : #endif
362 75 : }
363 :
364 0 : int mac_selinux_create_socket_prepare(const char *label) {
365 :
366 : #ifdef HAVE_SELINUX
367 : if (!mac_selinux_use())
368 : return 0;
369 :
370 : assert(label);
371 :
372 : if (setsockcreatecon((security_context_t) label) < 0) {
373 : log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
374 :
375 : if (security_getenforce() == 1)
376 : return -errno;
377 : }
378 : #endif
379 :
380 0 : return 0;
381 : }
382 :
383 0 : void mac_selinux_create_socket_clear(void) {
384 :
385 : #ifdef HAVE_SELINUX
386 : PROTECT_ERRNO;
387 :
388 : if (!mac_selinux_use())
389 : return;
390 :
391 : setsockcreatecon(NULL);
392 : #endif
393 0 : }
394 :
395 0 : int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
396 :
397 : /* Binds a socket and label its file system object according to the SELinux policy */
398 :
399 : #ifdef HAVE_SELINUX
400 : _cleanup_security_context_free_ security_context_t fcon = NULL;
401 : const struct sockaddr_un *un;
402 : char *path;
403 : int r;
404 :
405 : assert(fd >= 0);
406 : assert(addr);
407 : assert(addrlen >= sizeof(sa_family_t));
408 :
409 : if (!label_hnd)
410 : goto skipped;
411 :
412 : /* Filter out non-local sockets */
413 : if (addr->sa_family != AF_UNIX)
414 : goto skipped;
415 :
416 : /* Filter out anonymous sockets */
417 : if (addrlen < sizeof(sa_family_t) + 1)
418 : goto skipped;
419 :
420 : /* Filter out abstract namespace sockets */
421 : un = (const struct sockaddr_un*) addr;
422 : if (un->sun_path[0] == 0)
423 : goto skipped;
424 :
425 : path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
426 :
427 : if (path_is_absolute(path))
428 : r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
429 : else {
430 : _cleanup_free_ char *newpath;
431 :
432 : newpath = path_make_absolute_cwd(path);
433 : if (!newpath)
434 : return -ENOMEM;
435 :
436 : r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
437 : }
438 :
439 : if (r == 0)
440 : r = setfscreatecon(fcon);
441 :
442 : if (r < 0 && errno != ENOENT) {
443 : log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
444 :
445 : if (security_getenforce() == 1) {
446 : r = -errno;
447 : goto finish;
448 : }
449 : }
450 :
451 : r = bind(fd, addr, addrlen);
452 : if (r < 0)
453 : r = -errno;
454 :
455 : finish:
456 : setfscreatecon(NULL);
457 : return r;
458 :
459 : skipped:
460 : #endif
461 0 : return bind(fd, addr, addrlen) < 0 ? -errno : 0;
462 : }
|