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