chiark / gitweb /
dce6f45c80a83a0ebb87872b20c822cbccb22dcf
[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
29 #include "label.h"
30 #include "util.h"
31
32 #ifdef HAVE_SELINUX
33 #include <selinux/selinux.h>
34 #include <selinux/label.h>
35
36 static struct selabel_handle *label_hnd = NULL;
37
38 static int use_selinux_cached = -1;
39
40 static inline bool use_selinux(void) {
41
42         if (use_selinux_cached < 0)
43                 use_selinux_cached = is_selinux_enabled() > 0;
44
45         return use_selinux_cached;
46 }
47
48 void label_retest_selinux(void) {
49         use_selinux_cached = -1;
50 }
51
52 #endif
53
54 int label_init(void) {
55         int r = 0;
56
57 #ifdef HAVE_SELINUX
58         usec_t before_timestamp, after_timestamp;
59         struct mallinfo before_mallinfo, after_mallinfo;
60
61         if (!use_selinux())
62                 return 0;
63
64         if (label_hnd)
65                 return 0;
66
67         before_mallinfo = mallinfo();
68         before_timestamp = now(CLOCK_MONOTONIC);
69
70         label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
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_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
85                          format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
86                          (l+1023)/1024);
87         }
88 #endif
89
90         return r;
91 }
92
93 int label_fix(const char *path, bool ignore_enoent) {
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                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
127                          "Unable to fix label of %s: %m", path);
128                 r = security_getenforce() == 1 ? -errno : 0;
129         }
130 #endif
131
132         return r;
133 }
134
135 void label_finish(void) {
136
137 #ifdef HAVE_SELINUX
138         if (use_selinux() && label_hnd)
139                 selabel_close(label_hnd);
140 #endif
141 }
142
143 int label_get_create_label_from_exe(const char *exe, char **label) {
144
145         int r = 0;
146
147 #ifdef HAVE_SELINUX
148         security_context_t mycon = NULL, fcon = NULL;
149         security_class_t sclass;
150
151         if (!use_selinux()) {
152                 *label = NULL;
153                 return 0;
154         }
155
156         r = getcon(&mycon);
157         if (r < 0)
158                 goto fail;
159
160         r = getfilecon(exe, &fcon);
161         if (r < 0)
162                 goto fail;
163
164         sclass = string_to_security_class("process");
165         r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
166         if (r == 0)
167                 log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
168
169 fail:
170         if (r < 0 && security_getenforce() == 1)
171                 r = -errno;
172
173         freecon(mycon);
174         freecon(fcon);
175 #endif
176
177         return r;
178 }
179
180 int label_fifofile_set(const char *path) {
181         int r = 0;
182
183 #ifdef HAVE_SELINUX
184         security_context_t filecon = NULL;
185
186         if (!use_selinux() || !label_hnd)
187                 return 0;
188
189         r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
190         if (r < 0)
191                 r = -errno;
192         else if (r == 0) {
193                 r = setfscreatecon(filecon);
194                 if (r < 0) {
195                         log_error("Failed to set SELinux file context on %s: %m", path);
196                         r = -errno;
197                 }
198
199                 freecon(filecon);
200         }
201
202         if (r < 0 && security_getenforce() == 0)
203                 r = 0;
204 #endif
205
206         return r;
207 }
208
209 int label_symlinkfile_set(const char *path) {
210         int r = 0;
211
212 #ifdef HAVE_SELINUX
213         security_context_t filecon = NULL;
214
215         if (!use_selinux() || !label_hnd)
216                 return 0;
217
218         r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
219         if (r < 0)
220                 r = -errno;
221         else if (r == 0) {
222                 r = setfscreatecon(filecon);
223                 if (r < 0) {
224                         log_error("Failed to set SELinux file context on %s: %m", path);
225                         r = -errno;
226                 }
227
228                 freecon(filecon);
229         }
230
231         if (r < 0 && security_getenforce() == 0)
232                 r = 0;
233 #endif
234
235         return r;
236 }
237
238 int label_socket_set(const char *label) {
239
240 #ifdef HAVE_SELINUX
241         if (!use_selinux())
242                 return 0;
243
244         if (setsockcreatecon((security_context_t) label) < 0) {
245                 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
246                          "Failed to set SELinux context (%s) on socket: %m", label);
247
248                 if (security_getenforce() == 1)
249                         return -errno;
250         }
251 #endif
252
253         return 0;
254 }
255
256 void label_file_clear(void) {
257
258 #ifdef HAVE_SELINUX
259         if (!use_selinux())
260                 return;
261
262         setfscreatecon(NULL);
263 #endif
264 }
265
266 void label_socket_clear(void) {
267
268 #ifdef HAVE_SELINUX
269         if (!use_selinux())
270                 return;
271
272         setsockcreatecon(NULL);
273 #endif
274 }
275
276 void label_free(const char *label) {
277
278 #ifdef HAVE_SELINUX
279         if (!use_selinux())
280                 return;
281
282         freecon((security_context_t) label);
283 #endif
284 }
285
286 int label_mkdir(const char *path, mode_t mode) {
287
288         /* Creates a directory and labels it according to the SELinux policy */
289
290 #ifdef HAVE_SELINUX
291         int r;
292         security_context_t fcon = NULL;
293
294         if (!use_selinux() || !label_hnd)
295                 goto skipped;
296
297         if (path_is_absolute(path))
298                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
299         else {
300                 char *newpath;
301
302                 newpath = path_make_absolute_cwd(path);
303                 if (!newpath)
304                         return -ENOMEM;
305
306                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
307                 free(newpath);
308         }
309
310         if (r == 0)
311                 r = setfscreatecon(fcon);
312
313         if (r < 0 && errno != ENOENT) {
314                 log_error("Failed to set security context %s for %s: %m", fcon, path);
315
316                 if (security_getenforce() == 1) {
317                         r = -errno;
318                         goto finish;
319                 }
320         }
321
322         r = mkdir(path, mode);
323         if (r < 0)
324                 r = -errno;
325
326 finish:
327         setfscreatecon(NULL);
328         freecon(fcon);
329
330         return r;
331
332 skipped:
333 #endif
334         return mkdir(path, mode) < 0 ? -errno : 0;
335 }
336
337 int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
338
339         /* Binds a socket and label its file system object according to the SELinux policy */
340
341 #ifdef HAVE_SELINUX
342         int r;
343         security_context_t fcon = NULL;
344         const struct sockaddr_un *un;
345         char *path = NULL;
346
347         assert(fd >= 0);
348         assert(addr);
349         assert(addrlen >= sizeof(sa_family_t));
350
351         if (!use_selinux() || !label_hnd)
352                 goto skipped;
353
354         /* Filter out non-local sockets */
355         if (addr->sa_family != AF_UNIX)
356                 goto skipped;
357
358         /* Filter out anonymous sockets */
359         if (addrlen < sizeof(sa_family_t) + 1)
360                 goto skipped;
361
362         /* Filter out abstract namespace sockets */
363         un = (const struct sockaddr_un*) addr;
364         if (un->sun_path[0] == 0)
365                 goto skipped;
366
367         path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
368         if (!path)
369                 return -ENOMEM;
370
371         if (path_is_absolute(path))
372                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
373         else {
374                 char *newpath;
375
376                 newpath = path_make_absolute_cwd(path);
377
378                 if (!newpath) {
379                         free(path);
380                         return -ENOMEM;
381                 }
382
383                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
384                 free(newpath);
385         }
386
387         if (r == 0)
388                 r = setfscreatecon(fcon);
389
390         if (r < 0 && errno != ENOENT) {
391                 log_error("Failed to set security context %s for %s: %m", fcon, path);
392
393                 if (security_getenforce() == 1) {
394                         r = -errno;
395                         goto finish;
396                 }
397         }
398
399         r = bind(fd, addr, addrlen);
400         if (r < 0)
401                 r = -errno;
402
403 finish:
404         setfscreatecon(NULL);
405         freecon(fcon);
406         free(path);
407
408         return r;
409
410 skipped:
411 #endif
412         return bind(fd, addr, addrlen) < 0 ? -errno : 0;
413 }