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