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