]> git.proxmox.com Git - systemd.git/blob - src/shared/condition.c
New upstream version 236
[systemd.git] / src / shared / condition.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <fnmatch.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <time.h>
30 #include <unistd.h>
31
32 #include "sd-id128.h"
33
34 #include "alloc-util.h"
35 #include "apparmor-util.h"
36 #include "architecture.h"
37 #include "audit-util.h"
38 #include "cap-list.h"
39 #include "condition.h"
40 #include "extract-word.h"
41 #include "fd-util.h"
42 #include "fileio.h"
43 #include "glob-util.h"
44 #include "hostname-util.h"
45 #include "ima-util.h"
46 #include "list.h"
47 #include "macro.h"
48 #include "mount-util.h"
49 #include "parse-util.h"
50 #include "path-util.h"
51 #include "proc-cmdline.h"
52 #include "process-util.h"
53 #include "selinux-util.h"
54 #include "smack-util.h"
55 #include "stat-util.h"
56 #include "string-table.h"
57 #include "string-util.h"
58 #include "tomoyo-util.h"
59 #include "user-util.h"
60 #include "util.h"
61 #include "virt.h"
62
63 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
64 Condition *c;
65 int r;
66
67 assert(type >= 0);
68 assert(type < _CONDITION_TYPE_MAX);
69 assert((!parameter) == (type == CONDITION_NULL));
70
71 c = new0(Condition, 1);
72 if (!c)
73 return NULL;
74
75 c->type = type;
76 c->trigger = trigger;
77 c->negate = negate;
78
79 r = free_and_strdup(&c->parameter, parameter);
80 if (r < 0) {
81 return mfree(c);
82 }
83
84 return c;
85 }
86
87 void condition_free(Condition *c) {
88 assert(c);
89
90 free(c->parameter);
91 free(c);
92 }
93
94 Condition* condition_free_list(Condition *first) {
95 Condition *c, *n;
96
97 LIST_FOREACH_SAFE(conditions, c, n, first)
98 condition_free(c);
99
100 return NULL;
101 }
102
103 static int condition_test_kernel_command_line(Condition *c) {
104 _cleanup_free_ char *line = NULL;
105 const char *p;
106 bool equal;
107 int r;
108
109 assert(c);
110 assert(c->parameter);
111 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
112
113 r = proc_cmdline(&line);
114 if (r < 0)
115 return r;
116
117 equal = !!strchr(c->parameter, '=');
118
119 for (p = line;;) {
120 _cleanup_free_ char *word = NULL;
121 bool found;
122
123 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
124 if (r < 0)
125 return r;
126 if (r == 0)
127 break;
128
129 if (equal)
130 found = streq(word, c->parameter);
131 else {
132 const char *f;
133
134 f = startswith(word, c->parameter);
135 found = f && IN_SET(*f, 0, '=');
136 }
137
138 if (found)
139 return true;
140 }
141
142 return false;
143 }
144
145 static int condition_test_user(Condition *c) {
146 uid_t id;
147 int r;
148 _cleanup_free_ char *username = NULL;
149 const char *u;
150
151 assert(c);
152 assert(c->parameter);
153 assert(c->type == CONDITION_USER);
154
155 r = parse_uid(c->parameter, &id);
156 if (r >= 0)
157 return id == getuid() || id == geteuid();
158
159 if (streq("@system", c->parameter))
160 return uid_is_system(getuid()) || uid_is_system(geteuid());
161
162 username = getusername_malloc();
163 if (!username)
164 return -ENOMEM;
165
166 if (streq(username, c->parameter))
167 return 1;
168
169 if (getpid_cached() == 1)
170 return streq(c->parameter, "root");
171
172 u = c->parameter;
173 r = get_user_creds(&u, &id, NULL, NULL, NULL);
174 if (r < 0)
175 return 0;
176
177 return id == getuid() || id == geteuid();
178 }
179
180 static int condition_test_group(Condition *c) {
181 gid_t id;
182 int r;
183
184 assert(c);
185 assert(c->parameter);
186 assert(c->type == CONDITION_GROUP);
187
188 r = parse_gid(c->parameter, &id);
189 if (r >= 0)
190 return in_gid(id);
191
192 /* Avoid any NSS lookups if we are PID1 */
193 if (getpid_cached() == 1)
194 return streq(c->parameter, "root");
195
196 return in_group(c->parameter) > 0;
197 }
198
199 static int condition_test_virtualization(Condition *c) {
200 int b, v;
201
202 assert(c);
203 assert(c->parameter);
204 assert(c->type == CONDITION_VIRTUALIZATION);
205
206 if (streq(c->parameter, "private-users"))
207 return running_in_userns();
208
209 v = detect_virtualization();
210 if (v < 0)
211 return v;
212
213 /* First, compare with yes/no */
214 b = parse_boolean(c->parameter);
215 if (b >= 0)
216 return b == !!v;
217
218 /* Then, compare categorization */
219 if (streq(c->parameter, "vm"))
220 return VIRTUALIZATION_IS_VM(v);
221
222 if (streq(c->parameter, "container"))
223 return VIRTUALIZATION_IS_CONTAINER(v);
224
225 /* Finally compare id */
226 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
227 }
228
229 static int condition_test_architecture(Condition *c) {
230 int a, b;
231
232 assert(c);
233 assert(c->parameter);
234 assert(c->type == CONDITION_ARCHITECTURE);
235
236 a = uname_architecture();
237 if (a < 0)
238 return a;
239
240 if (streq(c->parameter, "native"))
241 b = native_architecture();
242 else {
243 b = architecture_from_string(c->parameter);
244 if (b < 0) /* unknown architecture? Then it's definitely not ours */
245 return false;
246 }
247
248 return a == b;
249 }
250
251 static int condition_test_host(Condition *c) {
252 _cleanup_free_ char *h = NULL;
253 sd_id128_t x, y;
254 int r;
255
256 assert(c);
257 assert(c->parameter);
258 assert(c->type == CONDITION_HOST);
259
260 if (sd_id128_from_string(c->parameter, &x) >= 0) {
261
262 r = sd_id128_get_machine(&y);
263 if (r < 0)
264 return r;
265
266 return sd_id128_equal(x, y);
267 }
268
269 h = gethostname_malloc();
270 if (!h)
271 return -ENOMEM;
272
273 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
274 }
275
276 static int condition_test_ac_power(Condition *c) {
277 int r;
278
279 assert(c);
280 assert(c->parameter);
281 assert(c->type == CONDITION_AC_POWER);
282
283 r = parse_boolean(c->parameter);
284 if (r < 0)
285 return r;
286
287 return (on_ac_power() != 0) == !!r;
288 }
289
290 static int condition_test_security(Condition *c) {
291 assert(c);
292 assert(c->parameter);
293 assert(c->type == CONDITION_SECURITY);
294
295 if (streq(c->parameter, "selinux"))
296 return mac_selinux_use();
297 if (streq(c->parameter, "smack"))
298 return mac_smack_use();
299 if (streq(c->parameter, "apparmor"))
300 return mac_apparmor_use();
301 if (streq(c->parameter, "audit"))
302 return use_audit();
303 if (streq(c->parameter, "ima"))
304 return use_ima();
305 if (streq(c->parameter, "tomoyo"))
306 return mac_tomoyo_use();
307
308 return false;
309 }
310
311 static int condition_test_capability(Condition *c) {
312 _cleanup_fclose_ FILE *f = NULL;
313 int value;
314 char line[LINE_MAX];
315 unsigned long long capabilities = -1;
316
317 assert(c);
318 assert(c->parameter);
319 assert(c->type == CONDITION_CAPABILITY);
320
321 /* If it's an invalid capability, we don't have it */
322 value = capability_from_name(c->parameter);
323 if (value < 0)
324 return -EINVAL;
325
326 /* If it's a valid capability we default to assume
327 * that we have it */
328
329 f = fopen("/proc/self/status", "re");
330 if (!f)
331 return -errno;
332
333 while (fgets(line, sizeof(line), f)) {
334 truncate_nl(line);
335
336 if (startswith(line, "CapBnd:")) {
337 (void) sscanf(line+7, "%llx", &capabilities);
338 break;
339 }
340 }
341
342 return !!(capabilities & (1ULL << value));
343 }
344
345 static int condition_test_needs_update(Condition *c) {
346 const char *p;
347 struct stat usr, other;
348
349 assert(c);
350 assert(c->parameter);
351 assert(c->type == CONDITION_NEEDS_UPDATE);
352
353 /* If the file system is read-only we shouldn't suggest an update */
354 if (path_is_read_only_fs(c->parameter) > 0)
355 return false;
356
357 /* Any other failure means we should allow the condition to be true,
358 * so that we rather invoke too many update tools than too
359 * few. */
360
361 if (!path_is_absolute(c->parameter))
362 return true;
363
364 p = strjoina(c->parameter, "/.updated");
365 if (lstat(p, &other) < 0)
366 return true;
367
368 if (lstat("/usr/", &usr) < 0)
369 return true;
370
371 /*
372 * First, compare seconds as they are always accurate...
373 */
374 if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
375 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
376
377 /*
378 * ...then compare nanoseconds.
379 *
380 * A false positive is only possible when /usr's nanoseconds > 0
381 * (otherwise /usr cannot be strictly newer than the target file)
382 * AND the target file's nanoseconds == 0
383 * (otherwise the filesystem supports nsec timestamps, see stat(2)).
384 */
385 if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) {
386 _cleanup_free_ char *timestamp_str = NULL;
387 uint64_t timestamp;
388 int r;
389
390 r = parse_env_file(p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
391 if (r < 0) {
392 log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
393 return true;
394 } else if (r == 0) {
395 log_debug("No data in timestamp file '%s', using mtime", p);
396 return true;
397 }
398
399 r = safe_atou64(timestamp_str, &timestamp);
400 if (r < 0) {
401 log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
402 return true;
403 }
404
405 timespec_store(&other.st_mtim, timestamp);
406 }
407
408 return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
409 }
410
411 static int condition_test_first_boot(Condition *c) {
412 int r;
413
414 assert(c);
415 assert(c->parameter);
416 assert(c->type == CONDITION_FIRST_BOOT);
417
418 r = parse_boolean(c->parameter);
419 if (r < 0)
420 return r;
421
422 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
423 }
424
425 static int condition_test_path_exists(Condition *c) {
426 assert(c);
427 assert(c->parameter);
428 assert(c->type == CONDITION_PATH_EXISTS);
429
430 return access(c->parameter, F_OK) >= 0;
431 }
432
433 static int condition_test_path_exists_glob(Condition *c) {
434 assert(c);
435 assert(c->parameter);
436 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
437
438 return glob_exists(c->parameter) > 0;
439 }
440
441 static int condition_test_path_is_directory(Condition *c) {
442 assert(c);
443 assert(c->parameter);
444 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
445
446 return is_dir(c->parameter, true) > 0;
447 }
448
449 static int condition_test_path_is_symbolic_link(Condition *c) {
450 assert(c);
451 assert(c->parameter);
452 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
453
454 return is_symlink(c->parameter) > 0;
455 }
456
457 static int condition_test_path_is_mount_point(Condition *c) {
458 assert(c);
459 assert(c->parameter);
460 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
461
462 return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
463 }
464
465 static int condition_test_path_is_read_write(Condition *c) {
466 assert(c);
467 assert(c->parameter);
468 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
469
470 return path_is_read_only_fs(c->parameter) <= 0;
471 }
472
473 static int condition_test_directory_not_empty(Condition *c) {
474 int r;
475
476 assert(c);
477 assert(c->parameter);
478 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
479
480 r = dir_is_empty(c->parameter);
481 return r <= 0 && r != -ENOENT;
482 }
483
484 static int condition_test_file_not_empty(Condition *c) {
485 struct stat st;
486
487 assert(c);
488 assert(c->parameter);
489 assert(c->type == CONDITION_FILE_NOT_EMPTY);
490
491 return (stat(c->parameter, &st) >= 0 &&
492 S_ISREG(st.st_mode) &&
493 st.st_size > 0);
494 }
495
496 static int condition_test_file_is_executable(Condition *c) {
497 struct stat st;
498
499 assert(c);
500 assert(c->parameter);
501 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
502
503 return (stat(c->parameter, &st) >= 0 &&
504 S_ISREG(st.st_mode) &&
505 (st.st_mode & 0111));
506 }
507
508 static int condition_test_null(Condition *c) {
509 assert(c);
510 assert(c->type == CONDITION_NULL);
511
512 /* Note that during parsing we already evaluate the string and
513 * store it in c->negate */
514 return true;
515 }
516
517 int condition_test(Condition *c) {
518
519 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
520 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
521 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
522 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
523 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
524 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
525 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
526 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
527 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
528 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
529 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
530 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
531 [CONDITION_SECURITY] = condition_test_security,
532 [CONDITION_CAPABILITY] = condition_test_capability,
533 [CONDITION_HOST] = condition_test_host,
534 [CONDITION_AC_POWER] = condition_test_ac_power,
535 [CONDITION_ARCHITECTURE] = condition_test_architecture,
536 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
537 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
538 [CONDITION_USER] = condition_test_user,
539 [CONDITION_GROUP] = condition_test_group,
540 [CONDITION_NULL] = condition_test_null,
541 };
542
543 int r, b;
544
545 assert(c);
546 assert(c->type >= 0);
547 assert(c->type < _CONDITION_TYPE_MAX);
548
549 r = condition_tests[c->type](c);
550 if (r < 0) {
551 c->result = CONDITION_ERROR;
552 return r;
553 }
554
555 b = (r > 0) == !c->negate;
556 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
557 return b;
558 }
559
560 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
561 assert(c);
562 assert(f);
563
564 if (!prefix)
565 prefix = "";
566
567 fprintf(f,
568 "%s\t%s: %s%s%s %s\n",
569 prefix,
570 to_string(c->type),
571 c->trigger ? "|" : "",
572 c->negate ? "!" : "",
573 c->parameter,
574 condition_result_to_string(c->result));
575 }
576
577 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
578 Condition *c;
579
580 LIST_FOREACH(conditions, c, first)
581 condition_dump(c, f, prefix, to_string);
582 }
583
584 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
585 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
586 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
587 [CONDITION_HOST] = "ConditionHost",
588 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
589 [CONDITION_SECURITY] = "ConditionSecurity",
590 [CONDITION_CAPABILITY] = "ConditionCapability",
591 [CONDITION_AC_POWER] = "ConditionACPower",
592 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
593 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
594 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
595 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
596 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
597 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
598 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
599 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
600 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
601 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
602 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
603 [CONDITION_USER] = "ConditionUser",
604 [CONDITION_GROUP] = "ConditionGroup",
605 [CONDITION_NULL] = "ConditionNull"
606 };
607
608 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
609
610 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
611 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
612 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
613 [CONDITION_HOST] = "AssertHost",
614 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
615 [CONDITION_SECURITY] = "AssertSecurity",
616 [CONDITION_CAPABILITY] = "AssertCapability",
617 [CONDITION_AC_POWER] = "AssertACPower",
618 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
619 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
620 [CONDITION_PATH_EXISTS] = "AssertPathExists",
621 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
622 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
623 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
624 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
625 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
626 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
627 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
628 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
629 [CONDITION_USER] = "AssertUser",
630 [CONDITION_GROUP] = "AssertGroup",
631 [CONDITION_NULL] = "AssertNull"
632 };
633
634 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
635
636 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
637 [CONDITION_UNTESTED] = "untested",
638 [CONDITION_SUCCEEDED] = "succeeded",
639 [CONDITION_FAILED] = "failed",
640 [CONDITION_ERROR] = "error",
641 };
642
643 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);