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