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