]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/label.c
a8bf6bd4f93982ce46ba83edc28b982d01693def
[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 #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),
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, bool apply) {
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 (!apply || !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 }