chiark / gitweb /
Use first partition in /proc/swaps for hibernation test
[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 <sys/stat.h>
24 #include <unistd.h>
25 #include <malloc.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include "label.h"
33 #include "strv.h"
34 #include "util.h"
35 #include "path-util.h"
36
37 #ifdef HAVE_SELINUX
38 #include "selinux-util.h"
39 #include <selinux/selinux.h>
40 #include <selinux/label.h>
41
42 static struct selabel_handle *label_hnd = NULL;
43
44 #endif
45
46 int label_init(const char *prefix) {
47         int r = 0;
48
49 #ifdef HAVE_SELINUX
50         usec_t before_timestamp, after_timestamp;
51         struct mallinfo before_mallinfo, after_mallinfo;
52
53         if (!use_selinux())
54                 return 0;
55
56         if (label_hnd)
57                 return 0;
58
59         before_mallinfo = mallinfo();
60         before_timestamp = now(CLOCK_MONOTONIC);
61
62         if (prefix) {
63                 struct selinux_opt options[] = {
64                         { .type = SELABEL_OPT_SUBSET, .value = prefix },
65                 };
66
67                 label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options));
68         } else
69                 label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
70
71         if (!label_hnd) {
72                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
73                          "Failed to initialize SELinux context: %m");
74                 r = security_getenforce() == 1 ? -errno : 0;
75         } else  {
76                 char timespan[FORMAT_TIMESPAN_MAX];
77                 int l;
78
79                 after_timestamp = now(CLOCK_MONOTONIC);
80                 after_mallinfo = mallinfo();
81
82                 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
83
84                 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
85                           format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
86                           (l+1023)/1024);
87         }
88 #endif
89
90         return r;
91 }
92
93 int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
94         int r = 0;
95
96 #ifdef HAVE_SELINUX
97         struct stat st;
98         security_context_t fcon;
99
100         if (!use_selinux() || !label_hnd)
101                 return 0;
102
103         r = lstat(path, &st);
104         if (r == 0) {
105                 r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
106
107                 /* If there's no label to set, then exit without warning */
108                 if (r < 0 && errno == ENOENT)
109                         return 0;
110
111                 if (r == 0) {
112                         r = lsetfilecon(path, fcon);
113                         freecon(fcon);
114
115                         /* If the FS doesn't support labels, then exit without warning */
116                         if (r < 0 && errno == ENOTSUP)
117                                 return 0;
118                 }
119         }
120
121         if (r < 0) {
122                 /* Ignore ENOENT in some cases */
123                 if (ignore_enoent && errno == ENOENT)
124                         return 0;
125
126                 if (ignore_erofs && errno == EROFS)
127                         return 0;
128
129                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
130                          "Unable to fix label of %s: %m", path);
131                 r = security_getenforce() == 1 ? -errno : 0;
132         }
133 #endif
134
135         return r;
136 }
137
138 void label_finish(void) {
139
140 #ifdef HAVE_SELINUX
141         if (use_selinux() && label_hnd)
142                 selabel_close(label_hnd);
143 #endif
144 }
145
146 int label_get_create_label_from_exe(const char *exe, char **label) {
147
148         int r = 0;
149
150 #ifdef HAVE_SELINUX
151         security_context_t mycon = NULL, fcon = NULL;
152         security_class_t sclass;
153
154         if (!use_selinux()) {
155                 *label = NULL;
156                 return 0;
157         }
158
159         r = getcon(&mycon);
160         if (r < 0)
161                 goto fail;
162
163         r = getfilecon(exe, &fcon);
164         if (r < 0)
165                 goto fail;
166
167         sclass = string_to_security_class("process");
168         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
169         if (r == 0)
170                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
171
172 fail:
173         if (r < 0 && security_getenforce() == 1)
174                 r = -errno;
175
176         freecon(mycon);
177         freecon(fcon);
178 #endif
179
180         return r;
181 }
182
183 int label_context_set(const char *path, mode_t mode) {
184         int r = 0;
185
186 #ifdef HAVE_SELINUX
187         security_context_t filecon = NULL;
188
189         if (!use_selinux() || !label_hnd)
190                 return 0;
191
192         r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
193         if (r < 0 && errno != ENOENT)
194                 r = -errno;
195         else if (r == 0) {
196                 r = setfscreatecon(filecon);
197                 if (r < 0) {
198                         log_error("Failed to set SELinux file context on %s: %m", path);
199                         r = -errno;
200                 }
201
202                 freecon(filecon);
203         }
204
205         if (r < 0 && security_getenforce() == 0)
206                 r = 0;
207 #endif
208
209         return r;
210 }
211
212 int label_socket_set(const char *label) {
213
214 #ifdef HAVE_SELINUX
215         if (!use_selinux())
216                 return 0;
217
218         if (setsockcreatecon((security_context_t) label) < 0) {
219                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
220                          "Failed to set SELinux context (%s) on socket: %m", label);
221
222                 if (security_getenforce() == 1)
223                         return -errno;
224         }
225 #endif
226
227         return 0;
228 }
229
230 void label_context_clear(void) {
231
232 #ifdef HAVE_SELINUX
233         if (!use_selinux())
234                 return;
235
236         setfscreatecon(NULL);
237 #endif
238 }
239
240 void label_socket_clear(void) {
241
242 #ifdef HAVE_SELINUX
243         if (!use_selinux())
244                 return;
245
246         setsockcreatecon(NULL);
247 #endif
248 }
249
250 void label_free(const char *label) {
251
252 #ifdef HAVE_SELINUX
253         if (!use_selinux())
254                 return;
255
256         freecon((security_context_t) label);
257 #endif
258 }
259
260 int label_mkdir(const char *path, mode_t mode) {
261
262         /* Creates a directory and labels it according to the SELinux policy */
263 #ifdef HAVE_SELINUX
264         int r;
265         security_context_t fcon = NULL;
266
267         if (!use_selinux() || !label_hnd)
268                 goto skipped;
269
270         if (path_is_absolute(path))
271                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
272         else {
273                 char *newpath;
274
275                 newpath = path_make_absolute_cwd(path);
276                 if (!newpath)
277                         return -ENOMEM;
278
279                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
280                 free(newpath);
281         }
282
283         if (r == 0)
284                 r = setfscreatecon(fcon);
285
286         if (r < 0 && errno != ENOENT) {
287                 log_error("Failed to set security context %s for %s: %m", fcon, path);
288
289                 if (security_getenforce() == 1) {
290                         r = -errno;
291                         goto finish;
292                 }
293         }
294
295         r = mkdir(path, mode);
296         if (r < 0)
297                 r = -errno;
298
299 finish:
300         setfscreatecon(NULL);
301         freecon(fcon);
302
303         return r;
304
305 skipped:
306 #endif
307         return mkdir(path, mode) < 0 ? -errno : 0;
308 }
309
310 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
311
312         /* Binds a socket and label its file system object according to the SELinux policy */
313
314 #ifdef HAVE_SELINUX
315         int r;
316         security_context_t fcon = NULL;
317         const struct sockaddr_un *un;
318         char *path = NULL;
319
320         assert(fd >= 0);
321         assert(addr);
322         assert(addrlen >= sizeof(sa_family_t));
323
324         if (!use_selinux() || !label_hnd)
325                 goto skipped;
326
327         /* Filter out non-local sockets */
328         if (addr->sa_family != AF_UNIX)
329                 goto skipped;
330
331         /* Filter out anonymous sockets */
332         if (addrlen < sizeof(sa_family_t) + 1)
333                 goto skipped;
334
335         /* Filter out abstract namespace sockets */
336         un = (const struct sockaddr_un*) addr;
337         if (un->sun_path[0] == 0)
338                 goto skipped;
339
340         path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
341         if (!path)
342                 return -ENOMEM;
343
344         if (path_is_absolute(path))
345                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
346         else {
347                 char *newpath;
348
349                 newpath = path_make_absolute_cwd(path);
350
351                 if (!newpath) {
352                         free(path);
353                         return -ENOMEM;
354                 }
355
356                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
357                 free(newpath);
358         }
359
360         if (r == 0)
361                 r = setfscreatecon(fcon);
362
363         if (r < 0 && errno != ENOENT) {
364                 log_error("Failed to set security context %s for %s: %m", fcon, path);
365
366                 if (security_getenforce() == 1) {
367                         r = -errno;
368                         goto finish;
369                 }
370         }
371
372         r = bind(fd, addr, addrlen);
373         if (r < 0)
374                 r = -errno;
375
376 finish:
377         setfscreatecon(NULL);
378         freecon(fcon);
379         free(path);
380
381         return r;
382
383 skipped:
384 #endif
385         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
386 }