chiark / gitweb /
Prep v234: Apply missing upstream fixes in src/basic (1/6)
[elogind.git] / src / basic / selinux-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <malloc.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/un.h>
27 #include <syslog.h>
28
29 #ifdef HAVE_SELINUX
30 #include <selinux/context.h>
31 #include <selinux/label.h>
32 #include <selinux/selinux.h>
33 #endif
34
35 #include "alloc-util.h"
36 #include "log.h"
37 #include "macro.h"
38 #include "path-util.h"
39 #include "selinux-util.h"
40 #include "time-util.h"
41 #include "util.h"
42
43 #ifdef HAVE_SELINUX
44 DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
45 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
46
47 #define _cleanup_freecon_ _cleanup_(freeconp)
48 #define _cleanup_context_free_ _cleanup_(context_freep)
49
50 static int cached_use = -1;
51 static struct selabel_handle *label_hnd = NULL;
52
53 #define log_enforcing(...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, errno, __VA_ARGS__)
54 #endif
55
56 bool mac_selinux_use(void) {
57 #ifdef HAVE_SELINUX
58         if (cached_use < 0)
59                 cached_use = is_selinux_enabled() > 0;
60
61         return cached_use;
62 #else
63         return false;
64 #endif
65 }
66
67 void mac_selinux_retest(void) {
68 #ifdef HAVE_SELINUX
69         cached_use = -1;
70 #endif
71 }
72
73 int mac_selinux_init(void) {
74         int r = 0;
75
76 #ifdef HAVE_SELINUX
77         usec_t before_timestamp, after_timestamp;
78         struct mallinfo before_mallinfo, after_mallinfo;
79
80         if (label_hnd)
81                 return 0;
82
83         if (!mac_selinux_use())
84                 return 0;
85
86         before_mallinfo = mallinfo();
87         before_timestamp = now(CLOCK_MONOTONIC);
88
89         label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
90         if (!label_hnd) {
91                 log_enforcing("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 void mac_selinux_finish(void) {
112
113 #ifdef HAVE_SELINUX
114         if (!label_hnd)
115                 return;
116
117         selabel_close(label_hnd);
118         label_hnd = NULL;
119 #endif
120 }
121
122 int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
123
124 #ifdef HAVE_SELINUX
125         struct stat st;
126         int r;
127
128         assert(path);
129
130         /* if mac_selinux_init() wasn't called before we are a NOOP */
131         if (!label_hnd)
132                 return 0;
133
134         r = lstat(path, &st);
135         if (r >= 0) {
136                 _cleanup_freecon_ char* fcon = NULL;
137
138                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
139
140                 /* If there's no label to set, then exit without warning */
141                 if (r < 0 && errno == ENOENT)
142                         return 0;
143
144                 if (r >= 0) {
145                         r = lsetfilecon_raw(path, fcon);
146
147                         /* If the FS doesn't support labels, then exit without warning */
148                         if (r < 0 && errno == EOPNOTSUPP)
149                                 return 0;
150                 }
151         }
152
153         if (r < 0) {
154                 /* Ignore ENOENT in some cases */
155                 if (ignore_enoent && errno == ENOENT)
156                         return 0;
157
158                 if (ignore_erofs && errno == EROFS)
159                         return 0;
160
161                 log_enforcing("Unable to fix SELinux security context of %s: %m", path);
162                 if (security_getenforce() == 1)
163                         return -errno;
164         }
165 #endif
166
167         return 0;
168 }
169
170 #if 0 /// UNNEDED by elogind
171 int mac_selinux_apply(const char *path, const char *label) {
172
173 #ifdef HAVE_SELINUX
174         if (!mac_selinux_use())
175                 return 0;
176
177         assert(path);
178         assert(label);
179
180         if (setfilecon(path, label) < 0) {
181                 log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path);
182                 if (security_getenforce() > 0)
183                         return -errno;
184         }
185 #endif
186         return 0;
187 }
188
189 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
190         int r = -EOPNOTSUPP;
191
192 #ifdef HAVE_SELINUX
193         _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
194         security_class_t sclass;
195
196         assert(exe);
197         assert(label);
198
199         if (!mac_selinux_use())
200                 return -EOPNOTSUPP;
201
202         r = getcon_raw(&mycon);
203         if (r < 0)
204                 return -errno;
205
206         r = getfilecon_raw(exe, &fcon);
207         if (r < 0)
208                 return -errno;
209
210         sclass = string_to_security_class("process");
211         r = security_compute_create_raw(mycon, fcon, sclass, label);
212         if (r < 0)
213                 return -errno;
214 #endif
215
216         return r;
217 }
218
219 int mac_selinux_get_our_label(char **label) {
220         int r = -EOPNOTSUPP;
221
222         assert(label);
223
224 #ifdef HAVE_SELINUX
225         if (!mac_selinux_use())
226                 return -EOPNOTSUPP;
227
228         r = getcon_raw(label);
229         if (r < 0)
230                 return -errno;
231 #endif
232
233         return r;
234 }
235
236 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
237         int r = -EOPNOTSUPP;
238
239 #ifdef HAVE_SELINUX
240         _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
241         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
242         security_class_t sclass;
243         const char *range = NULL;
244
245         assert(socket_fd >= 0);
246         assert(exe);
247         assert(label);
248
249         if (!mac_selinux_use())
250                 return -EOPNOTSUPP;
251
252         r = getcon_raw(&mycon);
253         if (r < 0)
254                 return -errno;
255
256         r = getpeercon_raw(socket_fd, &peercon);
257         if (r < 0)
258                 return -errno;
259
260         if (!exec_label) {
261                 /* If there is no context set for next exec let's use context
262                    of target executable */
263                 r = getfilecon_raw(exe, &fcon);
264                 if (r < 0)
265                         return -errno;
266         }
267
268         bcon = context_new(mycon);
269         if (!bcon)
270                 return -ENOMEM;
271
272         pcon = context_new(peercon);
273         if (!pcon)
274                 return -ENOMEM;
275
276         range = context_range_get(pcon);
277         if (!range)
278                 return -errno;
279
280         r = context_range_set(bcon, range);
281         if (r)
282                 return -errno;
283
284         freecon(mycon);
285         mycon = strdup(context_str(bcon));
286         if (!mycon)
287                 return -ENOMEM;
288
289         sclass = string_to_security_class("process");
290         r = security_compute_create_raw(mycon, fcon, sclass, label);
291         if (r < 0)
292                 return -errno;
293 #endif
294
295         return r;
296 }
297
298 char* mac_selinux_free(char *label) {
299
300 #ifdef HAVE_SELINUX
301         if (!label)
302                 return NULL;
303
304         if (!mac_selinux_use())
305                 return NULL;
306
307
308         freecon(label);
309 #endif
310
311         return NULL;
312 }
313 #endif // 0
314
315 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
316
317 #ifdef HAVE_SELINUX
318         _cleanup_freecon_ char *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_raw(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_raw(NULL);
367 #endif
368 }
369
370 #if 0 /// UNNEEDED by elogind
371 int mac_selinux_create_socket_prepare(const char *label) {
372
373 #ifdef HAVE_SELINUX
374         if (!mac_selinux_use())
375                 return 0;
376
377         assert(label);
378
379         if (setsockcreatecon(label) < 0) {
380                 log_enforcing("Failed to set SELinux security context %s for sockets: %m", label);
381
382                 if (security_getenforce() == 1)
383                         return -errno;
384         }
385 #endif
386
387         return 0;
388 }
389
390 void mac_selinux_create_socket_clear(void) {
391
392 #ifdef HAVE_SELINUX
393         PROTECT_ERRNO;
394
395         if (!mac_selinux_use())
396                 return;
397
398         setsockcreatecon_raw(NULL);
399 #endif
400 }
401
402 int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
403
404         /* Binds a socket and label its file system object according to the SELinux policy */
405
406 #ifdef HAVE_SELINUX
407         _cleanup_freecon_ char *fcon = NULL;
408         const struct sockaddr_un *un;
409         bool context_changed = false;
410         char *path;
411         int r;
412
413         assert(fd >= 0);
414         assert(addr);
415         assert(addrlen >= sizeof(sa_family_t));
416
417         if (!label_hnd)
418                 goto skipped;
419
420         /* Filter out non-local sockets */
421         if (addr->sa_family != AF_UNIX)
422                 goto skipped;
423
424         /* Filter out anonymous sockets */
425         if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
426                 goto skipped;
427
428         /* Filter out abstract namespace sockets */
429         un = (const struct sockaddr_un*) addr;
430         if (un->sun_path[0] == 0)
431                 goto skipped;
432
433         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
434
435         if (path_is_absolute(path))
436                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
437         else {
438                 _cleanup_free_ char *newpath = NULL;
439
440                 r = path_make_absolute_cwd(path, &newpath);
441                 if (r < 0)
442                         return r;
443
444                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
445         }
446
447         if (r < 0) {
448                 /* No context specified by the policy? Proceed without setting it */
449                 if (errno == ENOENT)
450                         goto skipped;
451
452                 log_enforcing("Failed to determine SELinux security context for %s: %m", path);
453                 if (security_getenforce() > 0)
454                         return -errno;
455
456         } else {
457                 if (setfscreatecon_raw(fcon) < 0) {
458                         log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path);
459                         if (security_getenforce() > 0)
460                                 return -errno;
461                 } else
462                         context_changed = true;
463         }
464
465         r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
466
467         if (context_changed)
468                 setfscreatecon_raw(NULL);
469
470         return r;
471
472 skipped:
473 #endif
474         if (bind(fd, addr, addrlen) < 0)
475                 return -errno;
476
477         return 0;
478 }
479 #endif // 0