]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
security: rework selinux, smack, ima, apparmor detection logic
[thirdparty/systemd.git] / src / core / condition.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/capability.h>
27 #include <sys/statvfs.h>
28 #include <fnmatch.h>
29
30 #include <systemd/sd-id128.h>
31 #include "util.h"
32 #include "condition.h"
33 #include "virt.h"
34 #include "path-util.h"
35 #include "fileio.h"
36 #include "unit.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
39 #include "ima-util.h"
40 #include "selinux-util.h"
41
42 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
43 Condition *c;
44
45 assert(type < _CONDITION_TYPE_MAX);
46
47 c = new0(Condition, 1);
48 if (!c)
49 return NULL;
50
51 c->type = type;
52 c->trigger = trigger;
53 c->negate = negate;
54
55 if (parameter) {
56 c->parameter = strdup(parameter);
57 if (!c->parameter) {
58 free(c);
59 return NULL;
60 }
61 }
62
63 return c;
64 }
65
66 void condition_free(Condition *c) {
67 assert(c);
68
69 free(c->parameter);
70 free(c);
71 }
72
73 void condition_free_list(Condition *first) {
74 Condition *c, *n;
75
76 LIST_FOREACH_SAFE(conditions, c, n, first)
77 condition_free(c);
78 }
79
80 static bool test_kernel_command_line(const char *parameter) {
81 char *line, *w, *state, *word = NULL;
82 bool equal;
83 int r;
84 size_t l, pl;
85 bool found = false;
86
87 assert(parameter);
88
89 if (detect_container(NULL) > 0)
90 return false;
91
92 r = read_one_line_file("/proc/cmdline", &line);
93 if (r < 0) {
94 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
95 return false;
96 }
97
98 equal = !!strchr(parameter, '=');
99 pl = strlen(parameter);
100
101 FOREACH_WORD_QUOTED(w, l, line, state) {
102
103 free(word);
104 word = strndup(w, l);
105 if (!word)
106 break;
107
108 if (equal) {
109 if (streq(word, parameter)) {
110 found = true;
111 break;
112 }
113 } else {
114 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
115 found = true;
116 break;
117 }
118 }
119
120 }
121
122 free(word);
123 free(line);
124
125 return found;
126 }
127
128 static bool test_virtualization(const char *parameter) {
129 int b;
130 Virtualization v;
131 const char *id;
132
133 assert(parameter);
134
135 v = detect_virtualization(&id);
136 if (v < 0) {
137 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
138 return false;
139 }
140
141 /* First, compare with yes/no */
142 b = parse_boolean(parameter);
143
144 if (v > 0 && b > 0)
145 return true;
146
147 if (v == 0 && b == 0)
148 return true;
149
150 /* Then, compare categorization */
151 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
152 return true;
153
154 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
155 return true;
156
157 /* Finally compare id */
158 return v > 0 && streq(parameter, id);
159 }
160
161 static bool test_security(const char *parameter) {
162 if (streq(parameter, "selinux"))
163 return use_selinux();
164 if (streq(parameter, "apparmor"))
165 return use_apparmor();
166 if (streq(parameter, "ima"))
167 return use_ima();
168 if (streq(parameter, "smack"))
169 return use_smack();
170 return false;
171 }
172
173 static bool test_capability(const char *parameter) {
174 cap_value_t value;
175 FILE *f;
176 char line[LINE_MAX];
177 unsigned long long capabilities = (unsigned long long) -1;
178
179 /* If it's an invalid capability, we don't have it */
180
181 if (cap_from_name(parameter, &value) < 0)
182 return false;
183
184 /* If it's a valid capability we default to assume
185 * that we have it */
186
187 f = fopen("/proc/self/status", "re");
188 if (!f)
189 return true;
190
191 while (fgets(line, sizeof(line), f)) {
192 truncate_nl(line);
193
194 if (startswith(line, "CapBnd:")) {
195 (void) sscanf(line+7, "%llx", &capabilities);
196 break;
197 }
198 }
199
200 fclose(f);
201
202 return !!(capabilities & (1ULL << value));
203 }
204
205 static bool test_host(const char *parameter) {
206 sd_id128_t x, y;
207 char *h;
208 int r;
209 bool b;
210
211 if (sd_id128_from_string(parameter, &x) >= 0) {
212
213 r = sd_id128_get_machine(&y);
214 if (r < 0)
215 return false;
216
217 return sd_id128_equal(x, y);
218 }
219
220 h = gethostname_malloc();
221 if (!h)
222 return false;
223
224 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
225 free(h);
226
227 return b;
228 }
229
230 static bool test_ac_power(const char *parameter) {
231 int r;
232
233 r = parse_boolean(parameter);
234 if (r < 0)
235 return true;
236
237 return (on_ac_power() != 0) == !!r;
238 }
239
240 static bool condition_test(Condition *c) {
241 assert(c);
242
243 switch(c->type) {
244
245 case CONDITION_PATH_EXISTS:
246 return (access(c->parameter, F_OK) >= 0) == !c->negate;
247
248 case CONDITION_PATH_EXISTS_GLOB:
249 return (glob_exists(c->parameter) > 0) == !c->negate;
250
251 case CONDITION_PATH_IS_DIRECTORY: {
252 struct stat st;
253
254 if (stat(c->parameter, &st) < 0)
255 return c->negate;
256 return S_ISDIR(st.st_mode) == !c->negate;
257 }
258
259 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
260 struct stat st;
261
262 if (lstat(c->parameter, &st) < 0)
263 return c->negate;
264 return S_ISLNK(st.st_mode) == !c->negate;
265 }
266
267 case CONDITION_PATH_IS_MOUNT_POINT:
268 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
269
270 case CONDITION_PATH_IS_READ_WRITE:
271 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
272
273 case CONDITION_DIRECTORY_NOT_EMPTY: {
274 int k;
275
276 k = dir_is_empty(c->parameter);
277 return !(k == -ENOENT || k > 0) == !c->negate;
278 }
279
280 case CONDITION_FILE_NOT_EMPTY: {
281 struct stat st;
282
283 if (stat(c->parameter, &st) < 0)
284 return c->negate;
285
286 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
287 }
288
289 case CONDITION_FILE_IS_EXECUTABLE: {
290 struct stat st;
291
292 if (stat(c->parameter, &st) < 0)
293 return c->negate;
294
295 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
296 }
297
298 case CONDITION_KERNEL_COMMAND_LINE:
299 return test_kernel_command_line(c->parameter) == !c->negate;
300
301 case CONDITION_VIRTUALIZATION:
302 return test_virtualization(c->parameter) == !c->negate;
303
304 case CONDITION_SECURITY:
305 return test_security(c->parameter) == !c->negate;
306
307 case CONDITION_CAPABILITY:
308 return test_capability(c->parameter) == !c->negate;
309
310 case CONDITION_HOST:
311 return test_host(c->parameter) == !c->negate;
312
313 case CONDITION_AC_POWER:
314 return test_ac_power(c->parameter) == !c->negate;
315
316 case CONDITION_NULL:
317 return !c->negate;
318
319 default:
320 assert_not_reached("Invalid condition type.");
321 }
322 }
323
324 bool condition_test_list(const char *unit, Condition *first) {
325 Condition *c;
326 int triggered = -1;
327
328 /* If the condition list is empty, then it is true */
329 if (!first)
330 return true;
331
332 /* Otherwise, if all of the non-trigger conditions apply and
333 * if any of the trigger conditions apply (unless there are
334 * none) we return true */
335 LIST_FOREACH(conditions, c, first) {
336 bool b;
337
338 b = condition_test(c);
339 if (unit)
340 log_debug_unit(unit,
341 "%s=%s%s%s %s for %s.",
342 condition_type_to_string(c->type),
343 c->trigger ? "|" : "",
344 c->negate ? "!" : "",
345 c->parameter,
346 b ? "succeeded" : "failed",
347 unit);
348 c->state = b ? 1 : -1;
349
350 if (!c->trigger && !b)
351 return false;
352
353 if (c->trigger && triggered <= 0)
354 triggered = b;
355 }
356
357 return triggered != 0;
358 }
359
360 void condition_dump(Condition *c, FILE *f, const char *prefix) {
361 assert(c);
362 assert(f);
363
364 if (!prefix)
365 prefix = "";
366
367 fprintf(f,
368 "%s\t%s: %s%s%s %s\n",
369 prefix,
370 condition_type_to_string(c->type),
371 c->trigger ? "|" : "",
372 c->negate ? "!" : "",
373 c->parameter,
374 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
375 }
376
377 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
378 Condition *c;
379
380 LIST_FOREACH(conditions, c, first)
381 condition_dump(c, f, prefix);
382 }
383
384 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
385 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
386 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
387 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
388 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
389 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
390 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
391 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
392 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
393 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
394 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
395 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
396 [CONDITION_SECURITY] = "ConditionSecurity",
397 [CONDITION_CAPABILITY] = "ConditionCapability",
398 [CONDITION_HOST] = "ConditionHost",
399 [CONDITION_AC_POWER] = "ConditionACPower",
400 [CONDITION_NULL] = "ConditionNull"
401 };
402
403 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);