]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/condition.c
6987cf71207cac95e1301f2c54839edf06e6ae62
[thirdparty/systemd.git] / src / shared / 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 <errno.h>
23 #include <fnmatch.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "sd-id128.h"
29
30 #include "apparmor-util.h"
31 #include "architecture.h"
32 #include "audit.h"
33 #include "cap-list.h"
34 #include "extract-word.h"
35 #include "hostname-util.h"
36 #include "ima-util.h"
37 #include "path-util.h"
38 #include "selinux-util.h"
39 #include "smack-util.h"
40 #include "util.h"
41 #include "virt.h"
42 #include "condition.h"
43
44 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
45 Condition *c;
46 int r;
47
48 assert(type >= 0);
49 assert(type < _CONDITION_TYPE_MAX);
50 assert((!parameter) == (type == CONDITION_NULL));
51
52 c = new0(Condition, 1);
53 if (!c)
54 return NULL;
55
56 c->type = type;
57 c->trigger = trigger;
58 c->negate = negate;
59
60 r = free_and_strdup(&c->parameter, parameter);
61 if (r < 0) {
62 free(c);
63 return NULL;
64 }
65
66 return c;
67 }
68
69 void condition_free(Condition *c) {
70 assert(c);
71
72 free(c->parameter);
73 free(c);
74 }
75
76 Condition* condition_free_list(Condition *first) {
77 Condition *c, *n;
78
79 LIST_FOREACH_SAFE(conditions, c, n, first)
80 condition_free(c);
81
82 return NULL;
83 }
84
85 static int condition_test_kernel_command_line(Condition *c) {
86 _cleanup_free_ char *line = NULL;
87 const char *p;
88 bool equal;
89 int r;
90
91 assert(c);
92 assert(c->parameter);
93 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
94
95 r = proc_cmdline(&line);
96 if (r < 0)
97 return r;
98
99 equal = !!strchr(c->parameter, '=');
100 p = line;
101
102 for (;;) {
103 _cleanup_free_ char *word = NULL;
104 bool found;
105
106 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
107 if (r < 0)
108 return r;
109 if (r == 0)
110 break;
111
112 if (equal)
113 found = streq(word, c->parameter);
114 else {
115 const char *f;
116
117 f = startswith(word, c->parameter);
118 found = f && (*f == '=' || *f == 0);
119 }
120
121 if (found)
122 return true;
123 }
124
125 return false;
126 }
127
128 static int condition_test_virtualization(Condition *c) {
129 int b, v;
130
131 assert(c);
132 assert(c->parameter);
133 assert(c->type == CONDITION_VIRTUALIZATION);
134
135 v = detect_virtualization();
136 if (v < 0)
137 return v;
138
139 /* First, compare with yes/no */
140 b = parse_boolean(c->parameter);
141
142 if (v > 0 && b > 0)
143 return true;
144
145 if (v == 0 && b == 0)
146 return true;
147
148 /* Then, compare categorization */
149 if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm"))
150 return true;
151
152 if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container"))
153 return true;
154
155 /* Finally compare id */
156 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
157 }
158
159 static int condition_test_architecture(Condition *c) {
160 int a, b;
161
162 assert(c);
163 assert(c->parameter);
164 assert(c->type == CONDITION_ARCHITECTURE);
165
166 a = uname_architecture();
167 if (a < 0)
168 return a;
169
170 if (streq(c->parameter, "native"))
171 b = native_architecture();
172 else
173 b = architecture_from_string(c->parameter);
174 if (b < 0)
175 return b;
176
177 return a == b;
178 }
179
180 static int condition_test_host(Condition *c) {
181 _cleanup_free_ char *h = NULL;
182 sd_id128_t x, y;
183 int r;
184
185 assert(c);
186 assert(c->parameter);
187 assert(c->type == CONDITION_HOST);
188
189 if (sd_id128_from_string(c->parameter, &x) >= 0) {
190
191 r = sd_id128_get_machine(&y);
192 if (r < 0)
193 return r;
194
195 return sd_id128_equal(x, y);
196 }
197
198 h = gethostname_malloc();
199 if (!h)
200 return -ENOMEM;
201
202 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
203 }
204
205 static int condition_test_ac_power(Condition *c) {
206 int r;
207
208 assert(c);
209 assert(c->parameter);
210 assert(c->type == CONDITION_AC_POWER);
211
212 r = parse_boolean(c->parameter);
213 if (r < 0)
214 return r;
215
216 return (on_ac_power() != 0) == !!r;
217 }
218
219 static int condition_test_security(Condition *c) {
220 assert(c);
221 assert(c->parameter);
222 assert(c->type == CONDITION_SECURITY);
223
224 if (streq(c->parameter, "selinux"))
225 return mac_selinux_use();
226 if (streq(c->parameter, "smack"))
227 return mac_smack_use();
228 if (streq(c->parameter, "apparmor"))
229 return mac_apparmor_use();
230 if (streq(c->parameter, "audit"))
231 return use_audit();
232 if (streq(c->parameter, "ima"))
233 return use_ima();
234
235 return false;
236 }
237
238 static int condition_test_capability(Condition *c) {
239 _cleanup_fclose_ FILE *f = NULL;
240 int value;
241 char line[LINE_MAX];
242 unsigned long long capabilities = -1;
243
244 assert(c);
245 assert(c->parameter);
246 assert(c->type == CONDITION_CAPABILITY);
247
248 /* If it's an invalid capability, we don't have it */
249 value = capability_from_name(c->parameter);
250 if (value < 0)
251 return -EINVAL;
252
253 /* If it's a valid capability we default to assume
254 * that we have it */
255
256 f = fopen("/proc/self/status", "re");
257 if (!f)
258 return -errno;
259
260 while (fgets(line, sizeof(line), f)) {
261 truncate_nl(line);
262
263 if (startswith(line, "CapBnd:")) {
264 (void) sscanf(line+7, "%llx", &capabilities);
265 break;
266 }
267 }
268
269 return !!(capabilities & (1ULL << value));
270 }
271
272 static int condition_test_needs_update(Condition *c) {
273 const char *p;
274 struct stat usr, other;
275
276 assert(c);
277 assert(c->parameter);
278 assert(c->type == CONDITION_NEEDS_UPDATE);
279
280 /* If the file system is read-only we shouldn't suggest an update */
281 if (path_is_read_only_fs(c->parameter) > 0)
282 return false;
283
284 /* Any other failure means we should allow the condition to be true,
285 * so that we rather invoke too many update tools then too
286 * few. */
287
288 if (!path_is_absolute(c->parameter))
289 return true;
290
291 p = strjoina(c->parameter, "/.updated");
292 if (lstat(p, &other) < 0)
293 return true;
294
295 if (lstat("/usr/", &usr) < 0)
296 return true;
297
298 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
299 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
300 }
301
302 static int condition_test_first_boot(Condition *c) {
303 int r;
304
305 assert(c);
306 assert(c->parameter);
307 assert(c->type == CONDITION_FIRST_BOOT);
308
309 r = parse_boolean(c->parameter);
310 if (r < 0)
311 return r;
312
313 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
314 }
315
316 static int condition_test_path_exists(Condition *c) {
317 assert(c);
318 assert(c->parameter);
319 assert(c->type == CONDITION_PATH_EXISTS);
320
321 return access(c->parameter, F_OK) >= 0;
322 }
323
324 static int condition_test_path_exists_glob(Condition *c) {
325 assert(c);
326 assert(c->parameter);
327 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
328
329 return glob_exists(c->parameter) > 0;
330 }
331
332 static int condition_test_path_is_directory(Condition *c) {
333 assert(c);
334 assert(c->parameter);
335 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
336
337 return is_dir(c->parameter, true) > 0;
338 }
339
340 static int condition_test_path_is_symbolic_link(Condition *c) {
341 assert(c);
342 assert(c->parameter);
343 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
344
345 return is_symlink(c->parameter) > 0;
346 }
347
348 static int condition_test_path_is_mount_point(Condition *c) {
349 assert(c);
350 assert(c->parameter);
351 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
352
353 return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
354 }
355
356 static int condition_test_path_is_read_write(Condition *c) {
357 assert(c);
358 assert(c->parameter);
359 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
360
361 return path_is_read_only_fs(c->parameter) <= 0;
362 }
363
364 static int condition_test_directory_not_empty(Condition *c) {
365 int r;
366
367 assert(c);
368 assert(c->parameter);
369 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
370
371 r = dir_is_empty(c->parameter);
372 return r <= 0 && r != -ENOENT;
373 }
374
375 static int condition_test_file_not_empty(Condition *c) {
376 struct stat st;
377
378 assert(c);
379 assert(c->parameter);
380 assert(c->type == CONDITION_FILE_NOT_EMPTY);
381
382 return (stat(c->parameter, &st) >= 0 &&
383 S_ISREG(st.st_mode) &&
384 st.st_size > 0);
385 }
386
387 static int condition_test_file_is_executable(Condition *c) {
388 struct stat st;
389
390 assert(c);
391 assert(c->parameter);
392 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
393
394 return (stat(c->parameter, &st) >= 0 &&
395 S_ISREG(st.st_mode) &&
396 (st.st_mode & 0111));
397 }
398
399 static int condition_test_null(Condition *c) {
400 assert(c);
401 assert(c->type == CONDITION_NULL);
402
403 /* Note that during parsing we already evaluate the string and
404 * store it in c->negate */
405 return true;
406 }
407
408 int condition_test(Condition *c) {
409
410 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
411 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
412 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
413 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
414 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
415 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
416 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
417 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
418 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
419 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
420 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
421 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
422 [CONDITION_SECURITY] = condition_test_security,
423 [CONDITION_CAPABILITY] = condition_test_capability,
424 [CONDITION_HOST] = condition_test_host,
425 [CONDITION_AC_POWER] = condition_test_ac_power,
426 [CONDITION_ARCHITECTURE] = condition_test_architecture,
427 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
428 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
429 [CONDITION_NULL] = condition_test_null,
430 };
431
432 int r, b;
433
434 assert(c);
435 assert(c->type >= 0);
436 assert(c->type < _CONDITION_TYPE_MAX);
437
438 r = condition_tests[c->type](c);
439 if (r < 0) {
440 c->result = CONDITION_ERROR;
441 return r;
442 }
443
444 b = (r > 0) == !c->negate;
445 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
446 return b;
447 }
448
449 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
450 assert(c);
451 assert(f);
452
453 if (!prefix)
454 prefix = "";
455
456 fprintf(f,
457 "%s\t%s: %s%s%s %s\n",
458 prefix,
459 to_string(c->type),
460 c->trigger ? "|" : "",
461 c->negate ? "!" : "",
462 c->parameter,
463 condition_result_to_string(c->result));
464 }
465
466 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
467 Condition *c;
468
469 LIST_FOREACH(conditions, c, first)
470 condition_dump(c, f, prefix, to_string);
471 }
472
473 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
474 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
475 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
476 [CONDITION_HOST] = "ConditionHost",
477 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
478 [CONDITION_SECURITY] = "ConditionSecurity",
479 [CONDITION_CAPABILITY] = "ConditionCapability",
480 [CONDITION_AC_POWER] = "ConditionACPower",
481 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
482 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
483 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
484 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
485 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
486 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
487 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
488 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
489 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
490 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
491 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
492 [CONDITION_NULL] = "ConditionNull"
493 };
494
495 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
496
497 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
498 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
499 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
500 [CONDITION_HOST] = "AssertHost",
501 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
502 [CONDITION_SECURITY] = "AssertSecurity",
503 [CONDITION_CAPABILITY] = "AssertCapability",
504 [CONDITION_AC_POWER] = "AssertACPower",
505 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
506 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
507 [CONDITION_PATH_EXISTS] = "AssertPathExists",
508 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
509 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
510 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
511 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
512 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
513 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
514 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
515 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
516 [CONDITION_NULL] = "AssertNull"
517 };
518
519 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
520
521 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
522 [CONDITION_UNTESTED] = "untested",
523 [CONDITION_SUCCEEDED] = "succeeded",
524 [CONDITION_FAILED] = "failed",
525 [CONDITION_ERROR] = "error",
526 };
527
528 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);