]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/selinux-util.c
Merge pull request #2495 from heftig/master
[thirdparty/systemd.git] / src / basic / selinux-util.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <malloc.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/un.h>
27 #include <syslog.h>
28
29 #ifdef HAVE_SELINUX
30 #include <selinux/context.h>
31 #include <selinux/label.h>
32 #include <selinux/selinux.h>
33 #endif
34
35 #include "alloc-util.h"
36 #include "log.h"
37 #include "macro.h"
38 #include "path-util.h"
39 #include "selinux-util.h"
40 #include "time-util.h"
41 #include "util.h"
42
43 #ifdef HAVE_SELINUX
44 DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
45 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
46
47 #define _cleanup_security_context_free_ _cleanup_(freeconp)
48 #define _cleanup_context_free_ _cleanup_(context_freep)
49
50 static int cached_use = -1;
51 static struct selabel_handle *label_hnd = NULL;
52
53 #define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
54 #endif
55
56 bool mac_selinux_have(void) {
57 #ifdef HAVE_SELINUX
58 if (cached_use < 0)
59 cached_use = is_selinux_enabled() > 0;
60
61 return cached_use;
62 #else
63 return false;
64 #endif
65 }
66
67 bool mac_selinux_use(void) {
68 if (!mac_selinux_have())
69 return false;
70
71 /* Never try to configure SELinux features if we aren't
72 * root */
73
74 return getuid() == 0;
75 }
76
77 void mac_selinux_retest(void) {
78 #ifdef HAVE_SELINUX
79 cached_use = -1;
80 #endif
81 }
82
83 int mac_selinux_init(const char *prefix) {
84 int r = 0;
85
86 #ifdef HAVE_SELINUX
87 usec_t before_timestamp, after_timestamp;
88 struct mallinfo before_mallinfo, after_mallinfo;
89
90 if (!mac_selinux_use())
91 return 0;
92
93 if (label_hnd)
94 return 0;
95
96 before_mallinfo = mallinfo();
97 before_timestamp = now(CLOCK_MONOTONIC);
98
99 if (prefix) {
100 struct selinux_opt options[] = {
101 { .type = SELABEL_OPT_SUBSET, .value = prefix },
102 };
103
104 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
105 } else
106 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
107
108 if (!label_hnd) {
109 log_enforcing("Failed to initialize SELinux context: %m");
110 r = security_getenforce() == 1 ? -errno : 0;
111 } else {
112 char timespan[FORMAT_TIMESPAN_MAX];
113 int l;
114
115 after_timestamp = now(CLOCK_MONOTONIC);
116 after_mallinfo = mallinfo();
117
118 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
119
120 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
121 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
122 (l+1023)/1024);
123 }
124 #endif
125
126 return r;
127 }
128
129 void mac_selinux_finish(void) {
130
131 #ifdef HAVE_SELINUX
132 if (!label_hnd)
133 return;
134
135 selabel_close(label_hnd);
136 label_hnd = NULL;
137 #endif
138 }
139
140 int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
141
142 #ifdef HAVE_SELINUX
143 struct stat st;
144 int r;
145
146 assert(path);
147
148 /* if mac_selinux_init() wasn't called before we are a NOOP */
149 if (!label_hnd)
150 return 0;
151
152 r = lstat(path, &st);
153 if (r >= 0) {
154 _cleanup_security_context_free_ security_context_t fcon = NULL;
155
156 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
157
158 /* If there's no label to set, then exit without warning */
159 if (r < 0 && errno == ENOENT)
160 return 0;
161
162 if (r >= 0) {
163 r = lsetfilecon(path, fcon);
164
165 /* If the FS doesn't support labels, then exit without warning */
166 if (r < 0 && errno == EOPNOTSUPP)
167 return 0;
168 }
169 }
170
171 if (r < 0) {
172 /* Ignore ENOENT in some cases */
173 if (ignore_enoent && errno == ENOENT)
174 return 0;
175
176 if (ignore_erofs && errno == EROFS)
177 return 0;
178
179 log_enforcing("Unable to fix SELinux security context of %s: %m", path);
180 if (security_getenforce() == 1)
181 return -errno;
182 }
183 #endif
184
185 return 0;
186 }
187
188 int mac_selinux_apply(const char *path, const char *label) {
189
190 #ifdef HAVE_SELINUX
191 if (!mac_selinux_use())
192 return 0;
193
194 assert(path);
195 assert(label);
196
197 if (setfilecon(path, (security_context_t) label) < 0) {
198 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
199 if (security_getenforce() > 0)
200 return -errno;
201 }
202 #endif
203 return 0;
204 }
205
206 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
207 int r = -EOPNOTSUPP;
208
209 #ifdef HAVE_SELINUX
210 _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
211 security_class_t sclass;
212
213 assert(exe);
214 assert(label);
215
216 if (!mac_selinux_have())
217 return -EOPNOTSUPP;
218
219 r = getcon_raw(&mycon);
220 if (r < 0)
221 return -errno;
222
223 r = getfilecon_raw(exe, &fcon);
224 if (r < 0)
225 return -errno;
226
227 sclass = string_to_security_class("process");
228 r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
229 if (r < 0)
230 return -errno;
231 #endif
232
233 return r;
234 }
235
236 int mac_selinux_get_our_label(char **label) {
237 int r = -EOPNOTSUPP;
238
239 assert(label);
240
241 #ifdef HAVE_SELINUX
242 if (!mac_selinux_have())
243 return -EOPNOTSUPP;
244
245 r = getcon_raw(label);
246 if (r < 0)
247 return -errno;
248 #endif
249
250 return r;
251 }
252
253 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
254 int r = -EOPNOTSUPP;
255
256 #ifdef HAVE_SELINUX
257 _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
258 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
259 security_class_t sclass;
260 const char *range = NULL;
261
262 assert(socket_fd >= 0);
263 assert(exe);
264 assert(label);
265
266 if (!mac_selinux_have())
267 return -EOPNOTSUPP;
268
269 r = getcon_raw(&mycon);
270 if (r < 0)
271 return -errno;
272
273 r = getpeercon(socket_fd, &peercon);
274 if (r < 0)
275 return -errno;
276
277 if (!exec_label) {
278 /* If there is no context set for next exec let's use context
279 of target executable */
280 r = getfilecon_raw(exe, &fcon);
281 if (r < 0)
282 return -errno;
283 }
284
285 bcon = context_new(mycon);
286 if (!bcon)
287 return -ENOMEM;
288
289 pcon = context_new(peercon);
290 if (!pcon)
291 return -ENOMEM;
292
293 range = context_range_get(pcon);
294 if (!range)
295 return -errno;
296
297 r = context_range_set(bcon, range);
298 if (r)
299 return -errno;
300
301 freecon(mycon);
302 mycon = strdup(context_str(bcon));
303 if (!mycon)
304 return -ENOMEM;
305
306 sclass = string_to_security_class("process");
307 r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
308 if (r < 0)
309 return -errno;
310 #endif
311
312 return r;
313 }
314
315 char* mac_selinux_free(char *label) {
316
317 #ifdef HAVE_SELINUX
318 if (!label)
319 return NULL;
320
321 if (!mac_selinux_have())
322 return NULL;
323
324
325 freecon((security_context_t) label);
326 #endif
327
328 return NULL;
329 }
330
331 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
332
333 #ifdef HAVE_SELINUX
334 _cleanup_security_context_free_ security_context_t filecon = NULL;
335 int r;
336
337 assert(path);
338
339 if (!label_hnd)
340 return 0;
341
342 if (path_is_absolute(path))
343 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
344 else {
345 _cleanup_free_ char *newpath = NULL;
346
347 r = path_make_absolute_cwd(path, &newpath);
348 if (r < 0)
349 return r;
350
351 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
352 }
353
354 if (r < 0) {
355 /* No context specified by the policy? Proceed without setting it. */
356 if (errno == ENOENT)
357 return 0;
358
359 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
360 } else {
361 if (setfscreatecon(filecon) >= 0)
362 return 0; /* Success! */
363
364 log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
365 }
366
367 if (security_getenforce() > 0)
368 return -errno;
369
370 #endif
371 return 0;
372 }
373
374 void mac_selinux_create_file_clear(void) {
375
376 #ifdef HAVE_SELINUX
377 PROTECT_ERRNO;
378
379 if (!mac_selinux_use())
380 return;
381
382 setfscreatecon(NULL);
383 #endif
384 }
385
386 int mac_selinux_create_socket_prepare(const char *label) {
387
388 #ifdef HAVE_SELINUX
389 if (!mac_selinux_use())
390 return 0;
391
392 assert(label);
393
394 if (setsockcreatecon((security_context_t) label) < 0) {
395 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
396
397 if (security_getenforce() == 1)
398 return -errno;
399 }
400 #endif
401
402 return 0;
403 }
404
405 void mac_selinux_create_socket_clear(void) {
406
407 #ifdef HAVE_SELINUX
408 PROTECT_ERRNO;
409
410 if (!mac_selinux_use())
411 return;
412
413 setsockcreatecon(NULL);
414 #endif
415 }
416
417 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
418
419 /* Binds a socket and label its file system object according to the SELinux policy */
420
421 #ifdef HAVE_SELINUX
422 _cleanup_security_context_free_ security_context_t fcon = NULL;
423 const struct sockaddr_un *un;
424 bool context_changed = false;
425 char *path;
426 int r;
427
428 assert(fd >= 0);
429 assert(addr);
430 assert(addrlen >= sizeof(sa_family_t));
431
432 if (!label_hnd)
433 goto skipped;
434
435 /* Filter out non-local sockets */
436 if (addr->sa_family != AF_UNIX)
437 goto skipped;
438
439 /* Filter out anonymous sockets */
440 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
441 goto skipped;
442
443 /* Filter out abstract namespace sockets */
444 un = (const struct sockaddr_un*) addr;
445 if (un->sun_path[0] == 0)
446 goto skipped;
447
448 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
449
450 if (path_is_absolute(path))
451 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
452 else {
453 _cleanup_free_ char *newpath = NULL;
454
455 r = path_make_absolute_cwd(path, &newpath);
456 if (r < 0)
457 return r;
458
459 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
460 }
461
462 if (r < 0) {
463 /* No context specified by the policy? Proceed without setting it */
464 if (errno == ENOENT)
465 goto skipped;
466
467 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
468 if (security_getenforce() > 0)
469 return -errno;
470
471 } else {
472 if (setfscreatecon(fcon) < 0) {
473 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
474 if (security_getenforce() > 0)
475 return -errno;
476 } else
477 context_changed = true;
478 }
479
480 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
481
482 if (context_changed)
483 setfscreatecon(NULL);
484
485 return r;
486
487 skipped:
488 #endif
489 if (bind(fd, addr, addrlen) < 0)
490 return -errno;
491
492 return 0;
493 }