]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/selinux-util.c
Merge pull request #2844 from yarda/uaccess-3dprinters
[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(void) {
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 (label_hnd)
91 return 0;
92
93 if (!mac_selinux_use())
94 return 0;
95
96 before_mallinfo = mallinfo();
97 before_timestamp = now(CLOCK_MONOTONIC);
98
99 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
100 if (!label_hnd) {
101 log_enforcing("Failed to initialize SELinux context: %m");
102 r = security_getenforce() == 1 ? -errno : 0;
103 } else {
104 char timespan[FORMAT_TIMESPAN_MAX];
105 int l;
106
107 after_timestamp = now(CLOCK_MONOTONIC);
108 after_mallinfo = mallinfo();
109
110 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
111
112 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
113 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
114 (l+1023)/1024);
115 }
116 #endif
117
118 return r;
119 }
120
121 void mac_selinux_finish(void) {
122
123 #ifdef HAVE_SELINUX
124 if (!label_hnd)
125 return;
126
127 selabel_close(label_hnd);
128 label_hnd = NULL;
129 #endif
130 }
131
132 int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
133
134 #ifdef HAVE_SELINUX
135 struct stat st;
136 int r;
137
138 assert(path);
139
140 /* if mac_selinux_init() wasn't called before we are a NOOP */
141 if (!label_hnd)
142 return 0;
143
144 r = lstat(path, &st);
145 if (r >= 0) {
146 _cleanup_security_context_free_ security_context_t fcon = NULL;
147
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_raw(path, fcon);
156
157 /* If the FS doesn't support labels, then exit without warning */
158 if (r < 0 && errno == EOPNOTSUPP)
159 return 0;
160 }
161 }
162
163 if (r < 0) {
164 /* Ignore ENOENT in some cases */
165 if (ignore_enoent && errno == ENOENT)
166 return 0;
167
168 if (ignore_erofs && errno == EROFS)
169 return 0;
170
171 log_enforcing("Unable to fix SELinux security context of %s: %m", path);
172 if (security_getenforce() == 1)
173 return -errno;
174 }
175 #endif
176
177 return 0;
178 }
179
180 int mac_selinux_apply(const char *path, const char *label) {
181
182 #ifdef HAVE_SELINUX
183 if (!mac_selinux_use())
184 return 0;
185
186 assert(path);
187 assert(label);
188
189 if (setfilecon(path, (security_context_t) label) < 0) {
190 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
191 if (security_getenforce() > 0)
192 return -errno;
193 }
194 #endif
195 return 0;
196 }
197
198 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
199 int r = -EOPNOTSUPP;
200
201 #ifdef HAVE_SELINUX
202 _cleanup_security_context_free_ security_context_t mycon = NULL, fcon = NULL;
203 security_class_t sclass;
204
205 assert(exe);
206 assert(label);
207
208 if (!mac_selinux_have())
209 return -EOPNOTSUPP;
210
211 r = getcon_raw(&mycon);
212 if (r < 0)
213 return -errno;
214
215 r = getfilecon_raw(exe, &fcon);
216 if (r < 0)
217 return -errno;
218
219 sclass = string_to_security_class("process");
220 r = security_compute_create_raw(mycon, fcon, sclass, (security_context_t *) label);
221 if (r < 0)
222 return -errno;
223 #endif
224
225 return r;
226 }
227
228 int mac_selinux_get_our_label(char **label) {
229 int r = -EOPNOTSUPP;
230
231 assert(label);
232
233 #ifdef HAVE_SELINUX
234 if (!mac_selinux_have())
235 return -EOPNOTSUPP;
236
237 r = getcon_raw(label);
238 if (r < 0)
239 return -errno;
240 #endif
241
242 return r;
243 }
244
245 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
246 int r = -EOPNOTSUPP;
247
248 #ifdef HAVE_SELINUX
249 _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL;
250 _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
251 security_class_t sclass;
252 const char *range = NULL;
253
254 assert(socket_fd >= 0);
255 assert(exe);
256 assert(label);
257
258 if (!mac_selinux_have())
259 return -EOPNOTSUPP;
260
261 r = getcon_raw(&mycon);
262 if (r < 0)
263 return -errno;
264
265 r = getpeercon_raw(socket_fd, &peercon);
266 if (r < 0)
267 return -errno;
268
269 if (!exec_label) {
270 /* If there is no context set for next exec let's use context
271 of target executable */
272 r = getfilecon_raw(exe, &fcon);
273 if (r < 0)
274 return -errno;
275 }
276
277 bcon = context_new(mycon);
278 if (!bcon)
279 return -ENOMEM;
280
281 pcon = context_new(peercon);
282 if (!pcon)
283 return -ENOMEM;
284
285 range = context_range_get(pcon);
286 if (!range)
287 return -errno;
288
289 r = context_range_set(bcon, range);
290 if (r)
291 return -errno;
292
293 freecon(mycon);
294 mycon = strdup(context_str(bcon));
295 if (!mycon)
296 return -ENOMEM;
297
298 sclass = string_to_security_class("process");
299 r = security_compute_create_raw(mycon, fcon, sclass, (security_context_t *) label);
300 if (r < 0)
301 return -errno;
302 #endif
303
304 return r;
305 }
306
307 char* mac_selinux_free(char *label) {
308
309 #ifdef HAVE_SELINUX
310 if (!label)
311 return NULL;
312
313 if (!mac_selinux_have())
314 return NULL;
315
316
317 freecon((security_context_t) label);
318 #endif
319
320 return NULL;
321 }
322
323 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
324
325 #ifdef HAVE_SELINUX
326 _cleanup_security_context_free_ security_context_t filecon = NULL;
327 int r;
328
329 assert(path);
330
331 if (!label_hnd)
332 return 0;
333
334 if (path_is_absolute(path))
335 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
336 else {
337 _cleanup_free_ char *newpath = NULL;
338
339 r = path_make_absolute_cwd(path, &newpath);
340 if (r < 0)
341 return r;
342
343 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
344 }
345
346 if (r < 0) {
347 /* No context specified by the policy? Proceed without setting it. */
348 if (errno == ENOENT)
349 return 0;
350
351 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
352 } else {
353 if (setfscreatecon_raw(filecon) >= 0)
354 return 0; /* Success! */
355
356 log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
357 }
358
359 if (security_getenforce() > 0)
360 return -errno;
361
362 #endif
363 return 0;
364 }
365
366 void mac_selinux_create_file_clear(void) {
367
368 #ifdef HAVE_SELINUX
369 PROTECT_ERRNO;
370
371 if (!mac_selinux_use())
372 return;
373
374 setfscreatecon_raw(NULL);
375 #endif
376 }
377
378 int mac_selinux_create_socket_prepare(const char *label) {
379
380 #ifdef HAVE_SELINUX
381 if (!mac_selinux_use())
382 return 0;
383
384 assert(label);
385
386 if (setsockcreatecon((security_context_t) label) < 0) {
387 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
388
389 if (security_getenforce() == 1)
390 return -errno;
391 }
392 #endif
393
394 return 0;
395 }
396
397 void mac_selinux_create_socket_clear(void) {
398
399 #ifdef HAVE_SELINUX
400 PROTECT_ERRNO;
401
402 if (!mac_selinux_use())
403 return;
404
405 setsockcreatecon_raw(NULL);
406 #endif
407 }
408
409 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
410
411 /* Binds a socket and label its file system object according to the SELinux policy */
412
413 #ifdef HAVE_SELINUX
414 _cleanup_security_context_free_ security_context_t fcon = NULL;
415 const struct sockaddr_un *un;
416 bool context_changed = false;
417 char *path;
418 int r;
419
420 assert(fd >= 0);
421 assert(addr);
422 assert(addrlen >= sizeof(sa_family_t));
423
424 if (!label_hnd)
425 goto skipped;
426
427 /* Filter out non-local sockets */
428 if (addr->sa_family != AF_UNIX)
429 goto skipped;
430
431 /* Filter out anonymous sockets */
432 if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
433 goto skipped;
434
435 /* Filter out abstract namespace sockets */
436 un = (const struct sockaddr_un*) addr;
437 if (un->sun_path[0] == 0)
438 goto skipped;
439
440 path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
441
442 if (path_is_absolute(path))
443 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
444 else {
445 _cleanup_free_ char *newpath = NULL;
446
447 r = path_make_absolute_cwd(path, &newpath);
448 if (r < 0)
449 return r;
450
451 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
452 }
453
454 if (r < 0) {
455 /* No context specified by the policy? Proceed without setting it */
456 if (errno == ENOENT)
457 goto skipped;
458
459 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
460 if (security_getenforce() > 0)
461 return -errno;
462
463 } else {
464 if (setfscreatecon_raw(fcon) < 0) {
465 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
466 if (security_getenforce() > 0)
467 return -errno;
468 } else
469 context_changed = true;
470 }
471
472 r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
473
474 if (context_changed)
475 setfscreatecon_raw(NULL);
476
477 return r;
478
479 skipped:
480 #endif
481 if (bind(fd, addr, addrlen) < 0)
482 return -errno;
483
484 return 0;
485 }