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