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