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>
34 #include "alloc-util.h"
35 #include "apparmor-util.h"
36 #include "architecture.h"
37 #include "audit-util.h"
39 #include "condition.h"
40 #include "extract-word.h"
43 #include "glob-util.h"
44 #include "hostname-util.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"
63 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
68 assert(type
< _CONDITION_TYPE_MAX
);
69 assert((!parameter
) == (type
== CONDITION_NULL
));
71 c
= new0(Condition
, 1);
79 r
= free_and_strdup(&c
->parameter
, parameter
);
87 void condition_free(Condition
*c
) {
94 Condition
* condition_free_list(Condition
*first
) {
97 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
103 static int condition_test_kernel_command_line(Condition
*c
) {
104 _cleanup_free_
char *line
= NULL
;
110 assert(c
->parameter
);
111 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
113 r
= proc_cmdline(&line
);
117 equal
= !!strchr(c
->parameter
, '=');
120 _cleanup_free_
char *word
= NULL
;
123 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
130 found
= streq(word
, c
->parameter
);
134 f
= startswith(word
, c
->parameter
);
135 found
= f
&& IN_SET(*f
, 0, '=');
145 static int condition_test_user(Condition
*c
) {
148 _cleanup_free_
char *username
= NULL
;
152 assert(c
->parameter
);
153 assert(c
->type
== CONDITION_USER
);
155 r
= parse_uid(c
->parameter
, &id
);
157 return id
== getuid() || id
== geteuid();
159 if (streq("@system", c
->parameter
))
160 return uid_is_system(getuid()) || uid_is_system(geteuid());
162 username
= getusername_malloc();
166 if (streq(username
, c
->parameter
))
169 if (getpid_cached() == 1)
170 return streq(c
->parameter
, "root");
173 r
= get_user_creds(&u
, &id
, NULL
, NULL
, NULL
);
177 return id
== getuid() || id
== geteuid();
180 static int condition_test_group(Condition
*c
) {
185 assert(c
->parameter
);
186 assert(c
->type
== CONDITION_GROUP
);
188 r
= parse_gid(c
->parameter
, &id
);
192 /* Avoid any NSS lookups if we are PID1 */
193 if (getpid_cached() == 1)
194 return streq(c
->parameter
, "root");
196 return in_group(c
->parameter
) > 0;
199 static int condition_test_virtualization(Condition
*c
) {
203 assert(c
->parameter
);
204 assert(c
->type
== CONDITION_VIRTUALIZATION
);
206 if (streq(c
->parameter
, "private-users"))
207 return running_in_userns();
209 v
= detect_virtualization();
213 /* First, compare with yes/no */
214 b
= parse_boolean(c
->parameter
);
218 /* Then, compare categorization */
219 if (streq(c
->parameter
, "vm"))
220 return VIRTUALIZATION_IS_VM(v
);
222 if (streq(c
->parameter
, "container"))
223 return VIRTUALIZATION_IS_CONTAINER(v
);
225 /* Finally compare id */
226 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
229 static int condition_test_architecture(Condition
*c
) {
233 assert(c
->parameter
);
234 assert(c
->type
== CONDITION_ARCHITECTURE
);
236 a
= uname_architecture();
240 if (streq(c
->parameter
, "native"))
241 b
= native_architecture();
243 b
= architecture_from_string(c
->parameter
);
244 if (b
< 0) /* unknown architecture? Then it's definitely not ours */
251 static int condition_test_host(Condition
*c
) {
252 _cleanup_free_
char *h
= NULL
;
257 assert(c
->parameter
);
258 assert(c
->type
== CONDITION_HOST
);
260 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
262 r
= sd_id128_get_machine(&y
);
266 return sd_id128_equal(x
, y
);
269 h
= gethostname_malloc();
273 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
276 static int condition_test_ac_power(Condition
*c
) {
280 assert(c
->parameter
);
281 assert(c
->type
== CONDITION_AC_POWER
);
283 r
= parse_boolean(c
->parameter
);
287 return (on_ac_power() != 0) == !!r
;
290 static int condition_test_security(Condition
*c
) {
292 assert(c
->parameter
);
293 assert(c
->type
== CONDITION_SECURITY
);
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"))
303 if (streq(c
->parameter
, "ima"))
305 if (streq(c
->parameter
, "tomoyo"))
306 return mac_tomoyo_use();
311 static int condition_test_capability(Condition
*c
) {
312 _cleanup_fclose_
FILE *f
= NULL
;
315 unsigned long long capabilities
= -1;
318 assert(c
->parameter
);
319 assert(c
->type
== CONDITION_CAPABILITY
);
321 /* If it's an invalid capability, we don't have it */
322 value
= capability_from_name(c
->parameter
);
326 /* If it's a valid capability we default to assume
329 f
= fopen("/proc/self/status", "re");
333 while (fgets(line
, sizeof(line
), f
)) {
336 if (startswith(line
, "CapBnd:")) {
337 (void) sscanf(line
+7, "%llx", &capabilities
);
342 return !!(capabilities
& (1ULL << value
));
345 static int condition_test_needs_update(Condition
*c
) {
347 struct stat usr
, other
;
350 assert(c
->parameter
);
351 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
353 /* If the file system is read-only we shouldn't suggest an update */
354 if (path_is_read_only_fs(c
->parameter
) > 0)
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
361 if (!path_is_absolute(c
->parameter
))
364 p
= strjoina(c
->parameter
, "/.updated");
365 if (lstat(p
, &other
) < 0)
368 if (lstat("/usr/", &usr
) < 0)
372 * First, compare seconds as they are always accurate...
374 if (usr
.st_mtim
.tv_sec
!= other
.st_mtim
.tv_sec
)
375 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
;
378 * ...then compare nanoseconds.
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)).
385 if (usr
.st_mtim
.tv_nsec
> 0 && other
.st_mtim
.tv_nsec
== 0) {
386 _cleanup_free_
char *timestamp_str
= NULL
;
390 r
= parse_env_file(p
, NULL
, "TIMESTAMP_NSEC", ×tamp_str
, NULL
);
392 log_error_errno(r
, "Failed to parse timestamp file '%s', using mtime: %m", p
);
395 log_debug("No data in timestamp file '%s', using mtime", p
);
399 r
= safe_atou64(timestamp_str
, ×tamp
);
401 log_error_errno(r
, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str
, p
);
405 timespec_store(&other
.st_mtim
, timestamp
);
408 return usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
;
411 static int condition_test_first_boot(Condition
*c
) {
415 assert(c
->parameter
);
416 assert(c
->type
== CONDITION_FIRST_BOOT
);
418 r
= parse_boolean(c
->parameter
);
422 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
425 static int condition_test_path_exists(Condition
*c
) {
427 assert(c
->parameter
);
428 assert(c
->type
== CONDITION_PATH_EXISTS
);
430 return access(c
->parameter
, F_OK
) >= 0;
433 static int condition_test_path_exists_glob(Condition
*c
) {
435 assert(c
->parameter
);
436 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
438 return glob_exists(c
->parameter
) > 0;
441 static int condition_test_path_is_directory(Condition
*c
) {
443 assert(c
->parameter
);
444 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
446 return is_dir(c
->parameter
, true) > 0;
449 static int condition_test_path_is_symbolic_link(Condition
*c
) {
451 assert(c
->parameter
);
452 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
454 return is_symlink(c
->parameter
) > 0;
457 static int condition_test_path_is_mount_point(Condition
*c
) {
459 assert(c
->parameter
);
460 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
462 return path_is_mount_point(c
->parameter
, NULL
, AT_SYMLINK_FOLLOW
) > 0;
465 static int condition_test_path_is_read_write(Condition
*c
) {
467 assert(c
->parameter
);
468 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
470 return path_is_read_only_fs(c
->parameter
) <= 0;
473 static int condition_test_directory_not_empty(Condition
*c
) {
477 assert(c
->parameter
);
478 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
480 r
= dir_is_empty(c
->parameter
);
481 return r
<= 0 && r
!= -ENOENT
;
484 static int condition_test_file_not_empty(Condition
*c
) {
488 assert(c
->parameter
);
489 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
491 return (stat(c
->parameter
, &st
) >= 0 &&
492 S_ISREG(st
.st_mode
) &&
496 static int condition_test_file_is_executable(Condition
*c
) {
500 assert(c
->parameter
);
501 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
503 return (stat(c
->parameter
, &st
) >= 0 &&
504 S_ISREG(st
.st_mode
) &&
505 (st
.st_mode
& 0111));
508 static int condition_test_null(Condition
*c
) {
510 assert(c
->type
== CONDITION_NULL
);
512 /* Note that during parsing we already evaluate the string and
513 * store it in c->negate */
517 int condition_test(Condition
*c
) {
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
,
546 assert(c
->type
>= 0);
547 assert(c
->type
< _CONDITION_TYPE_MAX
);
549 r
= condition_tests
[c
->type
](c
);
551 c
->result
= CONDITION_ERROR
;
555 b
= (r
> 0) == !c
->negate
;
556 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
560 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
568 "%s\t%s: %s%s%s %s\n",
571 c
->trigger
? "|" : "",
572 c
->negate
? "!" : "",
574 condition_result_to_string(c
->result
));
577 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
580 LIST_FOREACH(conditions
, c
, first
)
581 condition_dump(c
, f
, prefix
, to_string
);
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"
608 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
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"
634 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
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",
643 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);