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