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