chiark / gitweb /
Remove utmp stuff that has found it's way back in.
[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 void mac_selinux_free(char *label) {
307
308 #ifdef HAVE_SELINUX
309         if (!mac_selinux_use())
310                 return;
311
312         freecon((security_context_t) label);
313 #endif
314 }
315 #endif // 0
316
317 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
318         int r = 0;
319
320 #ifdef HAVE_SELINUX
321         _cleanup_security_context_free_ security_context_t filecon = NULL;
322
323         assert(path);
324
325         if (!label_hnd)
326                 return 0;
327
328         if (path_is_absolute(path))
329                 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
330         else {
331                 _cleanup_free_ char *newpath;
332
333                 newpath = path_make_absolute_cwd(path);
334                 if (!newpath)
335                         return -ENOMEM;
336
337                 r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
338         }
339
340         /* No context specified by the policy? Proceed without setting it. */
341         if (r < 0 && errno == ENOENT)
342                 return 0;
343
344         if (r < 0)
345                 r = -errno;
346         else {
347                 r = setfscreatecon(filecon);
348                 if (r < 0) {
349                         log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path);
350                         r = -errno;
351                 }
352         }
353
354         if (r < 0 && security_getenforce() == 0)
355                 r = 0;
356 #endif
357
358         return r;
359 }
360
361 void mac_selinux_create_file_clear(void) {
362
363 #ifdef HAVE_SELINUX
364         PROTECT_ERRNO;
365
366         if (!mac_selinux_use())
367                 return;
368
369         setfscreatecon(NULL);
370 #endif
371 }
372
373 /// UNNEEDED by elogind
374 #if 0
375 int mac_selinux_create_socket_prepare(const char *label) {
376
377 #ifdef HAVE_SELINUX
378         if (!mac_selinux_use())
379                 return 0;
380
381         assert(label);
382
383         if (setsockcreatecon((security_context_t) label) < 0) {
384                 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
385
386                 if (security_getenforce() == 1)
387                         return -errno;
388         }
389 #endif
390
391         return 0;
392 }
393
394 void mac_selinux_create_socket_clear(void) {
395
396 #ifdef HAVE_SELINUX
397         PROTECT_ERRNO;
398
399         if (!mac_selinux_use())
400                 return;
401
402         setsockcreatecon(NULL);
403 #endif
404 }
405
406 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
407
408         /* Binds a socket and label its file system object according to the SELinux policy */
409
410 #ifdef HAVE_SELINUX
411         _cleanup_security_context_free_ security_context_t fcon = NULL;
412         const struct sockaddr_un *un;
413         char *path;
414         int r;
415
416         assert(fd >= 0);
417         assert(addr);
418         assert(addrlen >= sizeof(sa_family_t));
419
420         if (!label_hnd)
421                 goto skipped;
422
423         /* Filter out non-local sockets */
424         if (addr->sa_family != AF_UNIX)
425                 goto skipped;
426
427         /* Filter out anonymous sockets */
428         if (addrlen < sizeof(sa_family_t) + 1)
429                 goto skipped;
430
431         /* Filter out abstract namespace sockets */
432         un = (const struct sockaddr_un*) addr;
433         if (un->sun_path[0] == 0)
434                 goto skipped;
435
436         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
437
438         if (path_is_absolute(path))
439                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
440         else {
441                 _cleanup_free_ char *newpath;
442
443                 newpath = path_make_absolute_cwd(path);
444                 if (!newpath)
445                         return -ENOMEM;
446
447                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
448         }
449
450         if (r == 0)
451                 r = setfscreatecon(fcon);
452
453         if (r < 0 && errno != ENOENT) {
454                 log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
455
456                 if (security_getenforce() == 1) {
457                         r = -errno;
458                         goto finish;
459                 }
460         }
461
462         r = bind(fd, addr, addrlen);
463         if (r < 0)
464                 r = -errno;
465
466 finish:
467         setfscreatecon(NULL);
468         return r;
469
470 skipped:
471 #endif
472         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
473 }
474 #endif // 0