]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/label.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.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 }