1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
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.
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.
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/>.
28 #include <sys/types.h>
29 #include <sys/utsname.h>
35 #include "alloc-util.h"
36 #include "apparmor-util.h"
37 #include "architecture.h"
38 #include "audit-util.h"
40 #include "cgroup-util.h"
41 #include "condition.h"
42 #include "extract-word.h"
45 #include "glob-util.h"
46 #include "hostname-util.h"
50 #include "mount-util.h"
51 #include "parse-util.h"
52 #include "path-util.h"
53 #include "proc-cmdline.h"
54 #include "process-util.h"
55 #include "selinux-util.h"
56 #include "smack-util.h"
57 #include "stat-util.h"
58 #include "string-table.h"
59 #include "string-util.h"
60 #include "tomoyo-util.h"
61 #include "user-util.h"
65 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
70 assert(type
< _CONDITION_TYPE_MAX
);
71 assert((!parameter
) == (type
== CONDITION_NULL
));
73 c
= new0(Condition
, 1);
81 r
= free_and_strdup(&c
->parameter
, parameter
);
89 void condition_free(Condition
*c
) {
96 Condition
* condition_free_list(Condition
*first
) {
99 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
105 static int condition_test_kernel_command_line(Condition
*c
) {
106 _cleanup_free_
char *line
= NULL
;
112 assert(c
->parameter
);
113 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
115 r
= proc_cmdline(&line
);
119 equal
= !!strchr(c
->parameter
, '=');
122 _cleanup_free_
char *word
= NULL
;
125 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
132 found
= streq(word
, c
->parameter
);
136 f
= startswith(word
, c
->parameter
);
137 found
= f
&& IN_SET(*f
, 0, '=');
147 static int condition_test_kernel_version(Condition
*c
) {
149 /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
150 * should be listed first. */
159 static const char *const prefix
[_ORDER_MAX
] = {
160 [LOWER_OR_EQUAL
] = "<=",
161 [GREATER_OR_EQUAL
] = ">=",
166 const char *p
= NULL
;
172 assert(c
->parameter
);
173 assert(c
->type
== CONDITION_KERNEL_VERSION
);
175 assert_se(uname(&u
) >= 0);
177 for (i
= 0; i
< _ORDER_MAX
; i
++) {
178 p
= startswith(c
->parameter
, prefix
[i
]);
183 /* No prefix? Then treat as glob string */
185 return fnmatch(skip_leading_chars(c
->parameter
, NULL
), u
.release
, 0) == 0;
187 k
= str_verscmp(u
.release
, skip_leading_chars(p
, NULL
));
200 case GREATER_OR_EQUAL
:
207 assert_not_reached("Can't compare");
211 static int condition_test_user(Condition
*c
) {
214 _cleanup_free_
char *username
= NULL
;
218 assert(c
->parameter
);
219 assert(c
->type
== CONDITION_USER
);
221 r
= parse_uid(c
->parameter
, &id
);
223 return id
== getuid() || id
== geteuid();
225 if (streq("@system", c
->parameter
))
226 return uid_is_system(getuid()) || uid_is_system(geteuid());
228 username
= getusername_malloc();
232 if (streq(username
, c
->parameter
))
235 if (getpid_cached() == 1)
236 return streq(c
->parameter
, "root");
239 r
= get_user_creds(&u
, &id
, NULL
, NULL
, NULL
);
243 return id
== getuid() || id
== geteuid();
246 static int condition_test_control_group_controller(Condition
*c
) {
248 CGroupMask system_mask
, wanted_mask
= 0;
251 assert(c
->parameter
);
252 assert(c
->type
== CONDITION_CONTROL_GROUP_CONTROLLER
);
254 r
= cg_mask_supported(&system_mask
);
256 return log_debug_errno(r
, "Failed to determine supported controllers: %m");
258 r
= cg_mask_from_string(c
->parameter
, &wanted_mask
);
259 if (r
< 0 || wanted_mask
<= 0) {
260 /* This won't catch the case that we have an unknown controller
261 * mixed in with valid ones -- these are only assessed on the
262 * validity of the valid controllers found. */
263 log_debug("Failed to parse cgroup string: %s", c
->parameter
);
267 return (system_mask
& wanted_mask
) == wanted_mask
;
270 static int condition_test_group(Condition
*c
) {
275 assert(c
->parameter
);
276 assert(c
->type
== CONDITION_GROUP
);
278 r
= parse_gid(c
->parameter
, &id
);
282 /* Avoid any NSS lookups if we are PID1 */
283 if (getpid_cached() == 1)
284 return streq(c
->parameter
, "root");
286 return in_group(c
->parameter
) > 0;
289 static int condition_test_virtualization(Condition
*c
) {
293 assert(c
->parameter
);
294 assert(c
->type
== CONDITION_VIRTUALIZATION
);
296 if (streq(c
->parameter
, "private-users"))
297 return running_in_userns();
299 v
= detect_virtualization();
303 /* First, compare with yes/no */
304 b
= parse_boolean(c
->parameter
);
308 /* Then, compare categorization */
309 if (streq(c
->parameter
, "vm"))
310 return VIRTUALIZATION_IS_VM(v
);
312 if (streq(c
->parameter
, "container"))
313 return VIRTUALIZATION_IS_CONTAINER(v
);
315 /* Finally compare id */
316 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
319 static int condition_test_architecture(Condition
*c
) {
323 assert(c
->parameter
);
324 assert(c
->type
== CONDITION_ARCHITECTURE
);
326 a
= uname_architecture();
330 if (streq(c
->parameter
, "native"))
331 b
= native_architecture();
333 b
= architecture_from_string(c
->parameter
);
334 if (b
< 0) /* unknown architecture? Then it's definitely not ours */
341 static int condition_test_host(Condition
*c
) {
342 _cleanup_free_
char *h
= NULL
;
347 assert(c
->parameter
);
348 assert(c
->type
== CONDITION_HOST
);
350 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
352 r
= sd_id128_get_machine(&y
);
356 return sd_id128_equal(x
, y
);
359 h
= gethostname_malloc();
363 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
366 static int condition_test_ac_power(Condition
*c
) {
370 assert(c
->parameter
);
371 assert(c
->type
== CONDITION_AC_POWER
);
373 r
= parse_boolean(c
->parameter
);
377 return (on_ac_power() != 0) == !!r
;
380 static int condition_test_security(Condition
*c
) {
382 assert(c
->parameter
);
383 assert(c
->type
== CONDITION_SECURITY
);
385 if (streq(c
->parameter
, "selinux"))
386 return mac_selinux_use();
387 if (streq(c
->parameter
, "smack"))
388 return mac_smack_use();
389 if (streq(c
->parameter
, "apparmor"))
390 return mac_apparmor_use();
391 if (streq(c
->parameter
, "audit"))
393 if (streq(c
->parameter
, "ima"))
395 if (streq(c
->parameter
, "tomoyo"))
396 return mac_tomoyo_use();
401 static int condition_test_capability(Condition
*c
) {
402 _cleanup_fclose_
FILE *f
= NULL
;
405 unsigned long long capabilities
= -1;
408 assert(c
->parameter
);
409 assert(c
->type
== CONDITION_CAPABILITY
);
411 /* If it's an invalid capability, we don't have it */
412 value
= capability_from_name(c
->parameter
);
416 /* If it's a valid capability we default to assume
419 f
= fopen("/proc/self/status", "re");
423 while (fgets(line
, sizeof(line
), f
)) {
426 if (startswith(line
, "CapBnd:")) {
427 (void) sscanf(line
+7, "%llx", &capabilities
);
432 return !!(capabilities
& (1ULL << value
));
435 static int condition_test_needs_update(Condition
*c
) {
437 struct stat usr
, other
;
440 assert(c
->parameter
);
441 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
443 /* If the file system is read-only we shouldn't suggest an update */
444 if (path_is_read_only_fs(c
->parameter
) > 0)
447 /* Any other failure means we should allow the condition to be true,
448 * so that we rather invoke too many update tools than too
451 if (!path_is_absolute(c
->parameter
))
454 p
= strjoina(c
->parameter
, "/.updated");
455 if (lstat(p
, &other
) < 0)
458 if (lstat("/usr/", &usr
) < 0)
462 * First, compare seconds as they are always accurate...
464 if (usr
.st_mtim
.tv_sec
!= other
.st_mtim
.tv_sec
)
465 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
;
468 * ...then compare nanoseconds.
470 * A false positive is only possible when /usr's nanoseconds > 0
471 * (otherwise /usr cannot be strictly newer than the target file)
472 * AND the target file's nanoseconds == 0
473 * (otherwise the filesystem supports nsec timestamps, see stat(2)).
475 if (usr
.st_mtim
.tv_nsec
> 0 && other
.st_mtim
.tv_nsec
== 0) {
476 _cleanup_free_
char *timestamp_str
= NULL
;
480 r
= parse_env_file(p
, NULL
, "TIMESTAMP_NSEC", ×tamp_str
, NULL
);
482 log_error_errno(r
, "Failed to parse timestamp file '%s', using mtime: %m", p
);
485 log_debug("No data in timestamp file '%s', using mtime", p
);
489 r
= safe_atou64(timestamp_str
, ×tamp
);
491 log_error_errno(r
, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str
, p
);
495 timespec_store(&other
.st_mtim
, timestamp
);
498 return usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
;
501 static int condition_test_first_boot(Condition
*c
) {
505 assert(c
->parameter
);
506 assert(c
->type
== CONDITION_FIRST_BOOT
);
508 r
= parse_boolean(c
->parameter
);
512 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
515 static int condition_test_path_exists(Condition
*c
) {
517 assert(c
->parameter
);
518 assert(c
->type
== CONDITION_PATH_EXISTS
);
520 return access(c
->parameter
, F_OK
) >= 0;
523 static int condition_test_path_exists_glob(Condition
*c
) {
525 assert(c
->parameter
);
526 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
528 return glob_exists(c
->parameter
) > 0;
531 static int condition_test_path_is_directory(Condition
*c
) {
533 assert(c
->parameter
);
534 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
536 return is_dir(c
->parameter
, true) > 0;
539 static int condition_test_path_is_symbolic_link(Condition
*c
) {
541 assert(c
->parameter
);
542 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
544 return is_symlink(c
->parameter
) > 0;
547 static int condition_test_path_is_mount_point(Condition
*c
) {
549 assert(c
->parameter
);
550 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
552 return path_is_mount_point(c
->parameter
, NULL
, AT_SYMLINK_FOLLOW
) > 0;
555 static int condition_test_path_is_read_write(Condition
*c
) {
557 assert(c
->parameter
);
558 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
560 return path_is_read_only_fs(c
->parameter
) <= 0;
563 static int condition_test_directory_not_empty(Condition
*c
) {
567 assert(c
->parameter
);
568 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
570 r
= dir_is_empty(c
->parameter
);
571 return r
<= 0 && r
!= -ENOENT
;
574 static int condition_test_file_not_empty(Condition
*c
) {
578 assert(c
->parameter
);
579 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
581 return (stat(c
->parameter
, &st
) >= 0 &&
582 S_ISREG(st
.st_mode
) &&
586 static int condition_test_file_is_executable(Condition
*c
) {
590 assert(c
->parameter
);
591 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
593 return (stat(c
->parameter
, &st
) >= 0 &&
594 S_ISREG(st
.st_mode
) &&
595 (st
.st_mode
& 0111));
598 static int condition_test_null(Condition
*c
) {
600 assert(c
->type
== CONDITION_NULL
);
602 /* Note that during parsing we already evaluate the string and
603 * store it in c->negate */
607 int condition_test(Condition
*c
) {
609 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
610 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
611 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
612 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
613 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
614 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
615 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
616 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
617 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
618 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
619 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
620 [CONDITION_KERNEL_VERSION
] = condition_test_kernel_version
,
621 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
622 [CONDITION_SECURITY
] = condition_test_security
,
623 [CONDITION_CAPABILITY
] = condition_test_capability
,
624 [CONDITION_HOST
] = condition_test_host
,
625 [CONDITION_AC_POWER
] = condition_test_ac_power
,
626 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
627 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
628 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
629 [CONDITION_USER
] = condition_test_user
,
630 [CONDITION_GROUP
] = condition_test_group
,
631 [CONDITION_CONTROL_GROUP_CONTROLLER
] = condition_test_control_group_controller
,
632 [CONDITION_NULL
] = condition_test_null
,
638 assert(c
->type
>= 0);
639 assert(c
->type
< _CONDITION_TYPE_MAX
);
641 r
= condition_tests
[c
->type
](c
);
643 c
->result
= CONDITION_ERROR
;
647 b
= (r
> 0) == !c
->negate
;
648 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
652 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
656 prefix
= strempty(prefix
);
659 "%s\t%s: %s%s%s %s\n",
662 c
->trigger
? "|" : "",
663 c
->negate
? "!" : "",
665 condition_result_to_string(c
->result
));
668 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
671 LIST_FOREACH(conditions
, c
, first
)
672 condition_dump(c
, f
, prefix
, to_string
);
675 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
676 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
677 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
678 [CONDITION_HOST
] = "ConditionHost",
679 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
680 [CONDITION_KERNEL_VERSION
] = "ConditionKernelVersion",
681 [CONDITION_SECURITY
] = "ConditionSecurity",
682 [CONDITION_CAPABILITY
] = "ConditionCapability",
683 [CONDITION_AC_POWER
] = "ConditionACPower",
684 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
685 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
686 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
687 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
688 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
689 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
690 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
691 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
692 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
693 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
694 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
695 [CONDITION_USER
] = "ConditionUser",
696 [CONDITION_GROUP
] = "ConditionGroup",
697 [CONDITION_CONTROL_GROUP_CONTROLLER
] = "ConditionControlGroupController",
698 [CONDITION_NULL
] = "ConditionNull"
701 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
703 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
704 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
705 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
706 [CONDITION_HOST
] = "AssertHost",
707 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
708 [CONDITION_KERNEL_VERSION
] = "AssertKernelVersion",
709 [CONDITION_SECURITY
] = "AssertSecurity",
710 [CONDITION_CAPABILITY
] = "AssertCapability",
711 [CONDITION_AC_POWER
] = "AssertACPower",
712 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
713 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
714 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
715 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
716 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
717 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
718 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
719 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
720 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
721 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
722 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
723 [CONDITION_USER
] = "AssertUser",
724 [CONDITION_GROUP
] = "AssertGroup",
725 [CONDITION_CONTROL_GROUP_CONTROLLER
] = "AssertControlGroupController",
726 [CONDITION_NULL
] = "AssertNull"
729 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
731 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
732 [CONDITION_UNTESTED
] = "untested",
733 [CONDITION_SUCCEEDED
] = "succeeded",
734 [CONDITION_FAILED
] = "failed",
735 [CONDITION_ERROR
] = "error",
738 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);