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