]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/smack-setup.c
Merge pull request #8417 from brauner/2018-03-09/add_bind_mount_fallback_to_private_d...
[thirdparty/systemd.git] / src / core / smack-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2013 Intel Corporation
6 Authors:
7 Nathaniel Chen <nathaniel.chen@intel.com>
8 ***/
9
10 #include <dirent.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <stdio_ext.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "alloc-util.h"
19 #include "dirent-util.h"
20 #include "fd-util.h"
21 #include "fileio.h"
22 #include "log.h"
23 #include "macro.h"
24 #include "smack-setup.h"
25 #include "string-util.h"
26 #include "util.h"
27
28 #if ENABLE_SMACK
29
30 static int write_access2_rules(const char* srcdir) {
31 _cleanup_close_ int load2_fd = -1, change_fd = -1;
32 _cleanup_closedir_ DIR *dir = NULL;
33 struct dirent *entry;
34 char buf[NAME_MAX];
35 int dfd = -1;
36 int r = 0;
37
38 load2_fd = open("/sys/fs/smackfs/load2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
39 if (load2_fd < 0) {
40 if (errno != ENOENT)
41 log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/load2': %m");
42 return -errno; /* negative error */
43 }
44
45 change_fd = open("/sys/fs/smackfs/change-rule", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
46 if (change_fd < 0) {
47 if (errno != ENOENT)
48 log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/change-rule': %m");
49 return -errno; /* negative error */
50 }
51
52 /* write rules to load2 or change-rule from every file in the directory */
53 dir = opendir(srcdir);
54 if (!dir) {
55 if (errno != ENOENT)
56 log_warning_errno(errno, "Failed to opendir '%s': %m", srcdir);
57 return errno; /* positive on purpose */
58 }
59
60 dfd = dirfd(dir);
61 assert(dfd >= 0);
62
63 FOREACH_DIRENT(entry, dir, return 0) {
64 int fd;
65 _cleanup_fclose_ FILE *policy = NULL;
66
67 if (!dirent_is_file(entry))
68 continue;
69
70 fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
71 if (fd < 0) {
72 if (r == 0)
73 r = -errno;
74 log_warning_errno(errno, "Failed to open '%s': %m", entry->d_name);
75 continue;
76 }
77
78 policy = fdopen(fd, "re");
79 if (!policy) {
80 if (r == 0)
81 r = -errno;
82 safe_close(fd);
83 log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
84 continue;
85 }
86
87 /* load2 write rules in the kernel require a line buffered stream */
88 FOREACH_LINE(buf, policy,
89 log_error_errno(errno, "Failed to read line from '%s': %m",
90 entry->d_name)) {
91
92 _cleanup_free_ char *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL;
93
94 if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
95 continue;
96
97 /* if 3 args -> load rule : subject object access1 */
98 /* if 4 args -> change rule : subject object access1 access2 */
99 if (sscanf(buf, "%ms %ms %ms %ms", &sbj, &obj, &acc1, &acc2) < 3) {
100 log_error_errno(errno, "Failed to parse rule '%s' in '%s', ignoring.", buf, entry->d_name);
101 continue;
102 }
103
104 if (write(isempty(acc2) ? load2_fd : change_fd, buf, strlen(buf)) < 0) {
105 if (r == 0)
106 r = -errno;
107 log_error_errno(errno, "Failed to write '%s' to '%s' in '%s'",
108 buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name);
109 }
110 }
111 }
112
113 return r;
114 }
115
116 static int write_cipso2_rules(const char* srcdir) {
117 _cleanup_close_ int cipso2_fd = -1;
118 _cleanup_closedir_ DIR *dir = NULL;
119 struct dirent *entry;
120 char buf[NAME_MAX];
121 int dfd = -1;
122 int r = 0;
123
124 cipso2_fd = open("/sys/fs/smackfs/cipso2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
125 if (cipso2_fd < 0) {
126 if (errno != ENOENT)
127 log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/cipso2': %m");
128 return -errno; /* negative error */
129 }
130
131 /* write rules to cipso2 from every file in the directory */
132 dir = opendir(srcdir);
133 if (!dir) {
134 if (errno != ENOENT)
135 log_warning_errno(errno, "Failed to opendir '%s': %m", srcdir);
136 return errno; /* positive on purpose */
137 }
138
139 dfd = dirfd(dir);
140 assert(dfd >= 0);
141
142 FOREACH_DIRENT(entry, dir, return 0) {
143 int fd;
144 _cleanup_fclose_ FILE *policy = NULL;
145
146 if (!dirent_is_file(entry))
147 continue;
148
149 fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
150 if (fd < 0) {
151 if (r == 0)
152 r = -errno;
153 log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
154 continue;
155 }
156
157 policy = fdopen(fd, "re");
158 if (!policy) {
159 if (r == 0)
160 r = -errno;
161 safe_close(fd);
162 log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
163 continue;
164 }
165
166 /* cipso2 write rules in the kernel require a line buffered stream */
167 FOREACH_LINE(buf, policy,
168 log_error_errno(errno, "Failed to read line from '%s': %m",
169 entry->d_name)) {
170
171 if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
172 continue;
173
174 if (write(cipso2_fd, buf, strlen(buf)) < 0) {
175 if (r == 0)
176 r = -errno;
177 log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'",
178 buf, entry->d_name);
179 break;
180 }
181 }
182 }
183
184 return r;
185 }
186
187 static int write_netlabel_rules(const char* srcdir) {
188 _cleanup_fclose_ FILE *dst = NULL;
189 _cleanup_closedir_ DIR *dir = NULL;
190 struct dirent *entry;
191 char buf[NAME_MAX];
192 int dfd = -1;
193 int r = 0;
194
195 dst = fopen("/sys/fs/smackfs/netlabel", "we");
196 if (!dst) {
197 if (errno != ENOENT)
198 log_warning_errno(errno, "Failed to open /sys/fs/smackfs/netlabel: %m");
199 return -errno; /* negative error */
200 }
201
202 /* write rules to dst from every file in the directory */
203 dir = opendir(srcdir);
204 if (!dir) {
205 if (errno != ENOENT)
206 log_warning_errno(errno, "Failed to opendir %s: %m", srcdir);
207 return errno; /* positive on purpose */
208 }
209
210 dfd = dirfd(dir);
211 assert(dfd >= 0);
212
213 FOREACH_DIRENT(entry, dir, return 0) {
214 int fd;
215 _cleanup_fclose_ FILE *policy = NULL;
216
217 fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
218 if (fd < 0) {
219 if (r == 0)
220 r = -errno;
221 log_warning_errno(errno, "Failed to open %s: %m", entry->d_name);
222 continue;
223 }
224
225 policy = fdopen(fd, "re");
226 if (!policy) {
227 if (r == 0)
228 r = -errno;
229 safe_close(fd);
230 log_error_errno(errno, "Failed to open %s: %m", entry->d_name);
231 continue;
232 }
233
234 (void) __fsetlocking(policy, FSETLOCKING_BYCALLER);
235
236 /* load2 write rules in the kernel require a line buffered stream */
237 FOREACH_LINE(buf, policy,
238 log_error_errno(errno, "Failed to read line from %s: %m", entry->d_name)) {
239
240 int q;
241
242 if (!fputs(buf, dst)) {
243 if (r == 0)
244 r = -EINVAL;
245 log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel");
246 break;
247 }
248 q = fflush_and_check(dst);
249 if (q < 0) {
250 if (r == 0)
251 r = q;
252 log_error_errno(q, "Failed to flush writes to /sys/fs/smackfs/netlabel: %m");
253 break;
254 }
255 }
256 }
257
258 return r;
259 }
260
261 static int write_onlycap_list(void) {
262 _cleanup_close_ int onlycap_fd = -1;
263 _cleanup_free_ char *list = NULL;
264 _cleanup_fclose_ FILE *f = NULL;
265 size_t len = 0, allocated = 0;
266 char buf[LINE_MAX];
267 int r;
268
269 f = fopen("/etc/smack/onlycap", "re");
270 if (!f) {
271 if (errno != ENOENT)
272 log_warning_errno(errno, "Failed to read '/etc/smack/onlycap'");
273 return errno == ENOENT ? ENOENT : -errno;
274 }
275
276 FOREACH_LINE(buf, f, return -errno) {
277 size_t l;
278
279 if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf))
280 continue;
281
282 l = strlen(buf);
283 if (!GREEDY_REALLOC(list, allocated, len + l + 1))
284 return log_oom();
285
286 stpcpy(list + len, buf)[0] = ' ';
287 len += l + 1;
288 }
289
290 if (!len)
291 return 0;
292
293 list[len - 1] = 0;
294
295 onlycap_fd = open("/sys/fs/smackfs/onlycap", O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
296 if (onlycap_fd < 0) {
297 if (errno != ENOENT)
298 log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/onlycap'");
299 return -errno; /* negative error */
300 }
301
302 r = write(onlycap_fd, list, len);
303 if (r < 0)
304 return log_error_errno(errno, "Failed to write onlycap list(%s) to '/sys/fs/smackfs/onlycap'", list);
305
306 return 0;
307 }
308
309 #endif
310
311 int mac_smack_setup(bool *loaded_policy) {
312
313 #if ENABLE_SMACK
314
315 int r;
316
317 assert(loaded_policy);
318
319 r = write_access2_rules("/etc/smack/accesses.d/");
320 switch(r) {
321 case -ENOENT:
322 log_debug("Smack is not enabled in the kernel.");
323 return 0;
324 case ENOENT:
325 log_debug("Smack access rules directory '/etc/smack/accesses.d/' not found");
326 return 0;
327 case 0:
328 log_info("Successfully loaded Smack policies.");
329 break;
330 default:
331 log_warning_errno(r, "Failed to load Smack access rules, ignoring: %m");
332 return 0;
333 }
334
335 #ifdef SMACK_RUN_LABEL
336 r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
337 if (r < 0)
338 log_warning_errno(r, "Failed to set SMACK label \"" SMACK_RUN_LABEL "\" on self: %m");
339 r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, 0);
340 if (r < 0)
341 log_warning_errno(r, "Failed to set SMACK ambient label \"" SMACK_RUN_LABEL "\": %m");
342 r = write_string_file("/sys/fs/smackfs/netlabel",
343 "0.0.0.0/0 " SMACK_RUN_LABEL, 0);
344 if (r < 0)
345 log_warning_errno(r, "Failed to set SMACK netlabel rule \"0.0.0.0/0 " SMACK_RUN_LABEL "\": %m");
346 r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", 0);
347 if (r < 0)
348 log_warning_errno(r, "Failed to set SMACK netlabel rule \"127.0.0.1 -CIPSO\": %m");
349 #endif
350
351 r = write_cipso2_rules("/etc/smack/cipso.d/");
352 switch(r) {
353 case -ENOENT:
354 log_debug("Smack/CIPSO is not enabled in the kernel.");
355 return 0;
356 case ENOENT:
357 log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
358 break;
359 case 0:
360 log_info("Successfully loaded Smack/CIPSO policies.");
361 break;
362 default:
363 log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m");
364 break;
365 }
366
367 r = write_netlabel_rules("/etc/smack/netlabel.d/");
368 switch(r) {
369 case -ENOENT:
370 log_debug("Smack/CIPSO is not enabled in the kernel.");
371 return 0;
372 case ENOENT:
373 log_debug("Smack network host rules directory '/etc/smack/netlabel.d/' not found");
374 break;
375 case 0:
376 log_info("Successfully loaded Smack network host rules.");
377 break;
378 default:
379 log_warning_errno(r, "Failed to load Smack network host rules: %m, ignoring.");
380 break;
381 }
382
383 r = write_onlycap_list();
384 switch(r) {
385 case -ENOENT:
386 log_debug("Smack is not enabled in the kernel.");
387 break;
388 case ENOENT:
389 log_debug("Smack onlycap list file '/etc/smack/onlycap' not found");
390 break;
391 case 0:
392 log_info("Successfully wrote Smack onlycap list.");
393 break;
394 default:
395 log_emergency_errno(r, "Failed to write Smack onlycap list.");
396 return r;
397 }
398
399 *loaded_policy = true;
400
401 #endif
402
403 return 0;
404 }