]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/label.c
util: make time formatting a bit smarter
[thirdparty/systemd.git] / src / shared / label.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
e51bc1a2
LP
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
5430f7f2
LP
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
e51bc1a2
LP
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
5430f7f2 16 Lesser General Public License for more details.
e51bc1a2 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e51bc1a2
LP
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>
455a946f 25#include <malloc.h>
f13e30d2
LP
26#include <sys/socket.h>
27#include <sys/un.h>
a5c32cff
HH
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
e51bc1a2
LP
31
32#include "label.h"
a5c32cff 33#include "strv.h"
e51bc1a2 34#include "util.h"
9eb977db 35#include "path-util.h"
e51bc1a2
LP
36
37#ifdef HAVE_SELINUX
cad45ba1 38#include "selinux-util.h"
e51bc1a2
LP
39#include <selinux/selinux.h>
40#include <selinux/label.h>
41
42static struct selabel_handle *label_hnd = NULL;
43
e51bc1a2
LP
44#endif
45
0f9963a8 46int label_init(const char *prefix) {
e51bc1a2
LP
47 int r = 0;
48
49#ifdef HAVE_SELINUX
189583d7
LP
50 usec_t before_timestamp, after_timestamp;
51 struct mallinfo before_mallinfo, after_mallinfo;
e51bc1a2
LP
52
53 if (!use_selinux())
54 return 0;
55
c4dcdb9f
LP
56 if (label_hnd)
57 return 0;
58
189583d7
LP
59 before_mallinfo = mallinfo();
60 before_timestamp = now(CLOCK_MONOTONIC);
455a946f 61
0f9963a8 62 if (prefix) {
e9a5ef7c 63 struct selinux_opt options[] = {
0f9963a8 64 { .type = SELABEL_OPT_SUBSET, .value = prefix },
e9a5ef7c
KS
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
e51bc1a2
LP
71 if (!label_hnd) {
72 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
73 "Failed to initialize SELinux context: %m");
049f8642 74 r = security_getenforce() == 1 ? -errno : 0;
871e5809 75 } else {
189583d7 76 char timespan[FORMAT_TIMESPAN_MAX];
455a946f 77 int l;
871e5809 78
189583d7
LP
79 after_timestamp = now(CLOCK_MONOTONIC);
80 after_mallinfo = mallinfo();
871e5809 81
189583d7 82 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
455a946f 83
107a2db9 84 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
2fa4092c 85 format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
107a2db9 86 (l+1023)/1024);
455a946f 87 }
e51bc1a2
LP
88#endif
89
90 return r;
91}
92
c9bc0764 93int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
e51bc1a2
LP
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
5a33f657
LP
107 /* If there's no label to set, then exit without warning */
108 if (r < 0 && errno == ENOENT)
109 return 0;
110
e51bc1a2 111 if (r == 0) {
81c3f1f6 112 r = lsetfilecon(path, fcon);
e51bc1a2 113 freecon(fcon);
d2dfce17
LP
114
115 /* If the FS doesn't support labels, then exit without warning */
116 if (r < 0 && errno == ENOTSUP)
117 return 0;
e51bc1a2
LP
118 }
119 }
5a33f657 120
e51bc1a2 121 if (r < 0) {
b4bd5144
LP
122 /* Ignore ENOENT in some cases */
123 if (ignore_enoent && errno == ENOENT)
124 return 0;
125
c9bc0764
LP
126 if (ignore_erofs && errno == EROFS)
127 return 0;
128
e51bc1a2
LP
129 log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
130 "Unable to fix label of %s: %m", path);
049f8642 131 r = security_getenforce() == 1 ? -errno : 0;
e51bc1a2
LP
132 }
133#endif
134
135 return r;
136}
137
138void label_finish(void) {
139
140#ifdef HAVE_SELINUX
141 if (use_selinux() && label_hnd)
142 selabel_close(label_hnd);
143#endif
144}
145
189583d7 146int label_get_create_label_from_exe(const char *exe, char **label) {
e51bc1a2
LP
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
172fail:
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
e9a5ef7c 183int label_context_set(const char *path, mode_t mode) {
5c0532d1
LP
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
e9a5ef7c 192 r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
080ffcb4 193 if (r < 0 && errno != ENOENT)
382241ee
LP
194 r = -errno;
195 else if (r == 0) {
196 r = setfscreatecon(filecon);
197 if (r < 0) {
5c0532d1
LP
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
e51bc1a2
LP
212int 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
e9a5ef7c 230void label_context_clear(void) {
e51bc1a2
LP
231
232#ifdef HAVE_SELINUX
233 if (!use_selinux())
234 return;
235
236 setfscreatecon(NULL);
237#endif
238}
239
240void label_socket_clear(void) {
241
242#ifdef HAVE_SELINUX
243 if (!use_selinux())
244 return;
245
246 setsockcreatecon(NULL);
247#endif
248}
249
250void 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
c66e7f04 260int label_mkdir(const char *path, mode_t mode, bool apply) {
e51bc1a2
LP
261
262 /* Creates a directory and labels it according to the SELinux policy */
e51bc1a2
LP
263#ifdef HAVE_SELINUX
264 int r;
265 security_context_t fcon = NULL;
266
c66e7f04 267 if (!apply || !use_selinux() || !label_hnd)
f13e30d2 268 goto skipped;
e51bc1a2 269
f13e30d2 270 if (path_is_absolute(path))
382241ee 271 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
f13e30d2
LP
272 else {
273 char *newpath;
e51bc1a2 274
f13e30d2
LP
275 newpath = path_make_absolute_cwd(path);
276 if (!newpath)
277 return -ENOMEM;
e51bc1a2 278
382241ee 279 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
f13e30d2
LP
280 free(newpath);
281 }
282
283 if (r == 0)
284 r = setfscreatecon(fcon);
e51bc1a2 285
f13e30d2
LP
286 if (r < 0 && errno != ENOENT) {
287 log_error("Failed to set security context %s for %s: %m", fcon, path);
e51bc1a2 288
f13e30d2 289 if (security_getenforce() == 1) {
e51bc1a2 290 r = -errno;
f13e30d2 291 goto finish;
e51bc1a2
LP
292 }
293 }
294
f13e30d2
LP
295 r = mkdir(path, mode);
296 if (r < 0)
e51bc1a2
LP
297 r = -errno;
298
299finish:
f13e30d2
LP
300 setfscreatecon(NULL);
301 freecon(fcon);
302
303 return r;
304
305skipped:
306#endif
307 return mkdir(path, mode) < 0 ? -errno : 0;
308}
309
310int 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))
382241ee 345 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
f13e30d2
LP
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
382241ee 356 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
f13e30d2 357 free(newpath);
e51bc1a2
LP
358 }
359
f13e30d2
LP
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
376finish:
377 setfscreatecon(NULL);
378 freecon(fcon);
379 free(path);
380
e51bc1a2 381 return r;
f13e30d2
LP
382
383skipped:
e51bc1a2 384#endif
f13e30d2 385 return bind(fd, addr, addrlen) < 0 ? -errno : 0;
e51bc1a2 386}