chiark / gitweb /
selinux: fix potential double free crash in child process
[elogind.git] / src / shared / label.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/socket.h>
26 #include <sys/un.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <sys/xattr.h>
31 #ifdef HAVE_SELINUX
32 #include <selinux/selinux.h>
33 #include <selinux/label.h>
34 #include <selinux/context.h>
35 #endif
36
37 #include "label.h"
38 #include "strv.h"
39 #include "util.h"
40 #include "path-util.h"
41 #include "selinux-util.h"
42 #include "smack-util.h"
43
44 #ifdef HAVE_SELINUX
45 DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon);
46 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
47
48 #define _cleanup_security_context_free_ _cleanup_(freeconp)
49 #define _cleanup_context_free_ _cleanup_(context_freep)
50
51 static struct selabel_handle *label_hnd = NULL;
52 #endif
53
54 static int smack_relabel_in_dev(const char *path) {
55         int r = 0;
56
57 #ifdef HAVE_SMACK
58         struct stat sb;
59         const char *label;
60
61         /*
62          * Path must be in /dev and must exist
63          */
64         if (!path_startswith(path, "/dev"))
65                 return 0;
66
67         r = lstat(path, &sb);
68         if (r < 0)
69                 return -errno;
70
71         /*
72          * Label directories and character devices "*".
73          * Label symlinks "_".
74          * Don't change anything else.
75          */
76         if (S_ISDIR(sb.st_mode))
77                 label = SMACK_STAR_LABEL;
78         else if (S_ISLNK(sb.st_mode))
79                 label = SMACK_FLOOR_LABEL;
80         else if (S_ISCHR(sb.st_mode))
81                 label = SMACK_STAR_LABEL;
82         else
83                 return 0;
84
85         r = setxattr(path, "security.SMACK64", label, strlen(label), 0);
86         if (r < 0) {
87                 log_error("Smack relabeling \"%s\" %m", path);
88                 return -errno;
89         }
90 #endif
91
92         return r;
93 }
94
95 int label_init(const char *prefix) {
96         int r = 0;
97
98 #ifdef HAVE_SELINUX
99         usec_t before_timestamp, after_timestamp;
100         struct mallinfo before_mallinfo, after_mallinfo;
101
102         if (!use_selinux())
103                 return 0;
104
105         if (label_hnd)
106                 return 0;
107
108         before_mallinfo = mallinfo();
109         before_timestamp = now(CLOCK_MONOTONIC);
110
111         if (prefix) {
112                 struct selinux_opt options[] = {
113                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
114                 };
115
116                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
117         } else
118                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
119
120         if (!label_hnd) {
121                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
122                          "Failed to initialize SELinux context: %m");
123                 r = security_getenforce() == 1 ? -errno : 0;
124         } else  {
125                 char timespan[FORMAT_TIMESPAN_MAX];
126                 int l;
127
128                 after_timestamp = now(CLOCK_MONOTONIC);
129                 after_mallinfo = mallinfo();
130
131                 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
132
133                 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
134                           format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
135                           (l+1023)/1024);
136         }
137 #endif
138
139         return r;
140 }
141
142 static int label_fix_selinux(const char *path, bool ignore_enoent, bool ignore_erofs) {
143         int r = 0;
144
145 #ifdef HAVE_SELINUX
146         struct stat st;
147         security_context_t fcon;
148
149         if (!label_hnd)
150                 return 0;
151
152         r = lstat(path, &st);
153         if (r == 0) {
154                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
155
156                 /* If there's no label to set, then exit without warning */
157                 if (r < 0 && errno == ENOENT)
158                         return 0;
159
160                 if (r == 0) {
161                         r = lsetfilecon(path, fcon);
162                         freecon(fcon);
163
164                         /* If the FS doesn't support labels, then exit without warning */
165                         if (r < 0 && errno == ENOTSUP)
166                                 return 0;
167                 }
168         }
169
170         if (r < 0) {
171                 /* Ignore ENOENT in some cases */
172                 if (ignore_enoent && errno == ENOENT)
173                         return 0;
174
175                 if (ignore_erofs && errno == EROFS)
176                         return 0;
177
178                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
179                          "Unable to fix label of %s: %m", path);
180                 r = security_getenforce() == 1 ? -errno : 0;
181         }
182 #endif
183
184         return r;
185 }
186
187 int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
188         int r = 0;
189
190         if (use_selinux()) {
191                 r = label_fix_selinux(path, ignore_enoent, ignore_erofs);
192                 if (r < 0)
193                         return r;
194         }
195
196         if (use_smack()) {
197                 r = smack_relabel_in_dev(path);
198                 if (r < 0)
199                         return r;
200         }
201
202         return r;
203 }
204
205 void label_finish(void) {
206
207 #ifdef HAVE_SELINUX
208         if (!use_selinux())
209                 return;
210
211         if (label_hnd)
212                 selabel_close(label_hnd);
213 #endif
214 }
215
216 int label_get_create_label_from_exe(const char *exe, char **label) {
217
218         int r = 0;
219
220 #ifdef HAVE_SELINUX
221         security_context_t mycon = NULL, fcon = NULL;
222         security_class_t sclass;
223
224         if (!use_selinux()) {
225                 *label = NULL;
226                 return 0;
227         }
228
229         r = getcon(&mycon);
230         if (r < 0)
231                 goto fail;
232
233         r = getfilecon(exe, &fcon);
234         if (r < 0)
235                 goto fail;
236
237         sclass = string_to_security_class("process");
238         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
239         if (r == 0)
240                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
241
242 fail:
243         if (r < 0 && security_getenforce() == 1)
244                 r = -errno;
245
246         freecon(mycon);
247         freecon(fcon);
248 #endif
249
250         return r;
251 }
252
253 int label_get_our_label(char **label) {
254         int r = -EOPNOTSUPP;
255
256 #ifdef HAVE_SELINUX
257         char *l = NULL;
258
259         r = getcon(&l);
260         if (r < 0)
261                 return r;
262
263         *label = l;
264 #endif
265
266         return r;
267 }
268
269 int label_get_child_mls_label(int socket_fd, const char *exe, char **label) {
270         int r = -EOPNOTSUPP;
271
272 #ifdef HAVE_SELINUX
273
274         _cleanup_security_context_free_ security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL;
275         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
276         security_class_t sclass;
277
278         const char *range = NULL;
279
280         assert(socket_fd >= 0);
281         assert(exe);
282         assert(label);
283
284         r = getcon(&mycon);
285         if (r < 0) {
286                 r = -EINVAL;
287                 goto out;
288         }
289
290         r = getpeercon(socket_fd, &peercon);
291         if (r < 0) {
292                 r = -EINVAL;
293                 goto out;
294         }
295
296         r = getexeccon(&fcon);
297         if (r < 0) {
298                 r = -EINVAL;
299                 goto out;
300         }
301
302         if (!fcon) {
303                 /* If there is no context set for next exec let's use context
304                    of target executable */
305                 r = getfilecon(exe, &fcon);
306                 if (r < 0) {
307                         r = -errno;
308                         goto out;
309                 }
310         }
311
312         bcon = context_new(mycon);
313         if (!bcon) {
314                 r = -ENOMEM;
315                 goto out;
316         }
317
318         pcon = context_new(peercon);
319         if (!pcon) {
320                 r = -ENOMEM;
321                 goto out;
322         }
323
324         range = context_range_get(pcon);
325         if (!range) {
326                 r = -errno;
327                 goto out;
328         }
329
330         r = context_range_set(bcon, range);
331         if (r) {
332                 r = -errno;
333                 goto out;
334         }
335
336         freecon(mycon);
337         mycon = strdup(context_str(bcon));
338         if (!mycon) {
339                 r = -errno;
340                 goto out;
341         }
342
343         sclass = string_to_security_class("process");
344         r = security_compute_create(mycon, fcon, sclass, &ret);
345         if (r < 0) {
346                 r = -EINVAL;
347                 goto out;
348         }
349
350         *label = ret;
351         ret = NULL;
352         r = 0;
353
354 out:
355         if (r < 0 && security_getenforce() == 1)
356                 return r;
357 #endif
358         return r;
359 }
360
361 int label_context_set(const char *path, mode_t mode) {
362         int r = 0;
363
364 #ifdef HAVE_SELINUX
365         security_context_t filecon = NULL;
366
367         if (!use_selinux() || !label_hnd)
368                 return 0;
369
370         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
371         if (r < 0 && errno != ENOENT)
372                 r = -errno;
373         else if (r == 0) {
374                 r = setfscreatecon(filecon);
375                 if (r < 0) {
376                         log_error("Failed to set SELinux file context on %s: %m", path);
377                         r = -errno;
378                 }
379
380                 freecon(filecon);
381         }
382
383         if (r < 0 && security_getenforce() == 0)
384                 r = 0;
385 #endif
386
387         return r;
388 }
389
390 int label_socket_set(const char *label) {
391
392 #ifdef HAVE_SELINUX
393         if (!use_selinux())
394                 return 0;
395
396         if (setsockcreatecon((security_context_t) label) < 0) {
397                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
398                          "Failed to set SELinux context (%s) on socket: %m", label);
399
400                 if (security_getenforce() == 1)
401                         return -errno;
402         }
403 #endif
404
405         return 0;
406 }
407
408 void label_context_clear(void) {
409
410 #ifdef HAVE_SELINUX
411         PROTECT_ERRNO;
412
413         if (!use_selinux())
414                 return;
415
416         setfscreatecon(NULL);
417 #endif
418 }
419
420 void label_socket_clear(void) {
421
422 #ifdef HAVE_SELINUX
423         PROTECT_ERRNO;
424
425         if (!use_selinux())
426                 return;
427
428         setsockcreatecon(NULL);
429 #endif
430 }
431
432 void label_free(const char *label) {
433
434 #ifdef HAVE_SELINUX
435         if (!use_selinux())
436                 return;
437
438         freecon((security_context_t) label);
439 #endif
440 }
441
442 static int label_mkdir_selinux(const char *path, mode_t mode) {
443         int r = 0;
444
445 #ifdef HAVE_SELINUX
446         /* Creates a directory and labels it according to the SELinux policy */
447         security_context_t fcon = NULL;
448
449         if (!label_hnd)
450                 return 0;
451
452         if (path_is_absolute(path))
453                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
454         else {
455                 _cleanup_free_ char *newpath;
456
457                 newpath = path_make_absolute_cwd(path);
458                 if (!newpath)
459                         return -ENOMEM;
460
461                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
462         }
463
464         if (r == 0)
465                 r = setfscreatecon(fcon);
466
467         if (r < 0 && errno != ENOENT) {
468                 log_error("Failed to set security context %s for %s: %m", fcon, path);
469
470                 if (security_getenforce() == 1) {
471                         r = -errno;
472                         goto finish;
473                 }
474         }
475
476         r = mkdir(path, mode);
477         if (r < 0)
478                 r = -errno;
479
480 finish:
481         setfscreatecon(NULL);
482         freecon(fcon);
483 #endif
484
485         return r;
486 }
487
488 int label_mkdir(const char *path, mode_t mode) {
489         int r;
490
491         if (use_selinux()) {
492                 r = label_mkdir_selinux(path, mode);
493                 if (r < 0)
494                         return r;
495         }
496
497         if (use_smack()) {
498                 r = mkdir(path, mode);
499                 if (r < 0 && errno != EEXIST)
500                         return -errno;
501
502                 r = smack_relabel_in_dev(path);
503                 if (r < 0)
504                         return r;
505         }
506
507         r = mkdir(path, mode);
508         if (r < 0 && errno != EEXIST)
509                 return -errno;
510
511         return 0;
512 }
513
514 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
515
516         /* Binds a socket and label its file system object according to the SELinux policy */
517
518 #ifdef HAVE_SELINUX
519         security_context_t fcon = NULL;
520         const struct sockaddr_un *un;
521         char *path;
522         int r;
523
524         assert(fd >= 0);
525         assert(addr);
526         assert(addrlen >= sizeof(sa_family_t));
527
528         if (!use_selinux() || !label_hnd)
529                 goto skipped;
530
531         /* Filter out non-local sockets */
532         if (addr->sa_family != AF_UNIX)
533                 goto skipped;
534
535         /* Filter out anonymous sockets */
536         if (addrlen < sizeof(sa_family_t) + 1)
537                 goto skipped;
538
539         /* Filter out abstract namespace sockets */
540         un = (const struct sockaddr_un*) addr;
541         if (un->sun_path[0] == 0)
542                 goto skipped;
543
544         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
545
546         if (path_is_absolute(path))
547                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
548         else {
549                 _cleanup_free_ char *newpath;
550
551                 newpath = path_make_absolute_cwd(path);
552                 if (!newpath)
553                         return -ENOMEM;
554
555                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
556         }
557
558         if (r == 0)
559                 r = setfscreatecon(fcon);
560
561         if (r < 0 && errno != ENOENT) {
562                 log_error("Failed to set security context %s for %s: %m", fcon, path);
563
564                 if (security_getenforce() == 1) {
565                         r = -errno;
566                         goto finish;
567                 }
568         }
569
570         r = bind(fd, addr, addrlen);
571         if (r < 0)
572                 r = -errno;
573
574 finish:
575         setfscreatecon(NULL);
576         freecon(fcon);
577
578         return r;
579
580 skipped:
581 #endif
582         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
583 }
584
585 int label_apply(const char *path, const char *label) {
586         int r = 0;
587
588 #ifdef HAVE_SELINUX
589         if (!use_selinux())
590                 return 0;
591
592         r = setfilecon(path, (char *)label);
593 #endif
594         return r;
595 }