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