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