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