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