]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/smack-setup.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / core / smack-setup.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ffbd2c4d
NC
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
07630cea 23#include <dirent.h>
ffbd2c4d 24#include <errno.h>
ffbd2c4d 25#include <fcntl.h>
07630cea
LP
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
ffbd2c4d 29
b5efdb8a 30#include "alloc-util.h"
a0956174 31#include "dirent-util.h"
3ffd4af2 32#include "fd-util.h"
8b197c3a 33#include "fileio.h"
ffbd2c4d 34#include "log.h"
07630cea 35#include "macro.h"
3ffd4af2 36#include "smack-setup.h"
07630cea
LP
37#include "string-util.h"
38#include "util.h"
ffbd2c4d 39
f9fa32f0 40#if ENABLE_SMACK
2b3e18de 41
6656aefb
WC
42static int write_access2_rules(const char* srcdir) {
43 _cleanup_close_ int load2_fd = -1, change_fd = -1;
ffbd2c4d
NC
44 _cleanup_closedir_ DIR *dir = NULL;
45 struct dirent *entry;
46 char buf[NAME_MAX];
47 int dfd = -1;
a4783bd1 48 int r = 0;
ffbd2c4d 49
6656aefb
WC
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) {
a4783bd1 59 if (errno != ENOENT)
6656aefb 60 log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/change-rule': %m");
a4783bd1 61 return -errno; /* negative error */
ffbd2c4d
NC
62 }
63
6656aefb 64 /* write rules to load2 or change-rule from every file in the directory */
a4783bd1 65 dir = opendir(srcdir);
ffbd2c4d 66 if (!dir) {
a4783bd1 67 if (errno != ENOENT)
6656aefb 68 log_warning_errno(errno, "Failed to opendir '%s': %m", srcdir);
a4783bd1 69 return errno; /* positive on purpose */
ffbd2c4d
NC
70 }
71
72 dfd = dirfd(dir);
fea7838e 73 assert(dfd >= 0);
ffbd2c4d
NC
74
75 FOREACH_DIRENT(entry, dir, return 0) {
a4783bd1 76 int fd;
ffbd2c4d 77 _cleanup_fclose_ FILE *policy = NULL;
ffbd2c4d 78
6656aefb
WC
79 if (!dirent_is_file(entry))
80 continue;
81
a4783bd1
ZJS
82 fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
83 if (fd < 0) {
84 if (r == 0)
85 r = -errno;
6656aefb 86 log_warning_errno(errno, "Failed to open '%s': %m", entry->d_name);
ffbd2c4d
NC
87 continue;
88 }
89
a4783bd1 90 policy = fdopen(fd, "re");
ffbd2c4d 91 if (!policy) {
a4783bd1
ZJS
92 if (r == 0)
93 r = -errno;
03e334a1 94 safe_close(fd);
6656aefb 95 log_error_errno(errno, "Failed to open '%s': %m", entry->d_name);
ffbd2c4d
NC
96 continue;
97 }
98
ffbd2c4d 99 /* load2 write rules in the kernel require a line buffered stream */
fea7838e 100 FOREACH_LINE(buf, policy,
6656aefb
WC
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) {
a4783bd1 117 if (r == 0)
6656aefb
WC
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);
a4783bd1 121 }
6656aefb
WC
122 }
123 }
124
125 return r;
126}
127
128static 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) {
a4783bd1
ZJS
187 if (r == 0)
188 r = -errno;
6656aefb
WC
189 log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'",
190 buf, entry->d_name);
a4783bd1
ZJS
191 break;
192 }
ffbd2c4d
NC
193 }
194 }
195
6656aefb 196 return r;
a4783bd1
ZJS
197}
198
ae176752
CS
199static 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)) {
4b61c875 250 if (!fputs_unlocked(buf, dst)) {
ae176752
CS
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
7f508f2c 265 return r;
ae176752
CS
266}
267
217f95db
WC
268static 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
2b3e18de 316#endif
ffbd2c4d 317
8a188de9 318int mac_smack_setup(bool *loaded_policy) {
2b3e18de 319
f9fa32f0 320#if ENABLE_SMACK
2b3e18de 321
a4783bd1
ZJS
322 int r;
323
e49d3c01
ŁS
324 assert(loaded_policy);
325
6656aefb 326 r = write_access2_rules("/etc/smack/accesses.d/");
a4783bd1
ZJS
327 switch(r) {
328 case -ENOENT:
329 log_debug("Smack is not enabled in the kernel.");
330 return 0;
331 case ENOENT:
6656aefb 332 log_debug("Smack access rules directory '/etc/smack/accesses.d/' not found");
a4783bd1
ZJS
333 return 0;
334 case 0:
335 log_info("Successfully loaded Smack policies.");
abbacb1d
NC
336 break;
337 default:
e53fc357 338 log_warning_errno(r, "Failed to load Smack access rules, ignoring: %m");
abbacb1d
NC
339 return 0;
340 }
341
8b197c3a 342#ifdef SMACK_RUN_LABEL
4c1fc3e4 343 r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
ae176752
CS
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");
8b197c3a
AK
356#endif
357
6656aefb 358 r = write_cipso2_rules("/etc/smack/cipso.d/");
abbacb1d
NC
359 switch(r) {
360 case -ENOENT:
361 log_debug("Smack/CIPSO is not enabled in the kernel.");
362 return 0;
363 case ENOENT:
6656aefb 364 log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
ae176752 365 break;
abbacb1d
NC
366 case 0:
367 log_info("Successfully loaded Smack/CIPSO policies.");
b9289d4c 368 break;
a4783bd1 369 default:
e53fc357 370 log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m");
ae176752
CS
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.");
a4783bd1 378 return 0;
ae176752
CS
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;
a4783bd1 388 }
2b3e18de 389
217f95db
WC
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
e49d3c01
ŁS
406 *loaded_policy = true;
407
2b3e18de
KL
408#endif
409
410 return 0;
ffbd2c4d 411}