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
);
88 void condition_free(Condition
*c
) {
95 Condition
* condition_free_list(Condition
*first
) {
98 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
104 static int condition_test_kernel_command_line(Condition
*c
) {
105 _cleanup_free_
char *line
= NULL
;
111 assert(c
->parameter
);
112 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
114 r
= proc_cmdline(&line
);
118 equal
= !!strchr(c
->parameter
, '=');
121 _cleanup_free_
char *word
= NULL
;
124 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
131 found
= streq(word
, c
->parameter
);
135 f
= startswith(word
, c
->parameter
);
136 found
= f
&& IN_SET(*f
, 0, '=');
146 static int condition_test_user(Condition
*c
) {
149 _cleanup_free_
char *username
= NULL
;
153 assert(c
->parameter
);
154 assert(c
->type
== CONDITION_USER
);
156 r
= parse_uid(c
->parameter
, &id
);
158 return id
== getuid() || id
== geteuid();
160 if (streq("@system", c
->parameter
))
161 return getuid() <= SYSTEM_UID_MAX
|| geteuid() <= SYSTEM_UID_MAX
;
163 username
= getusername_malloc();
167 if (streq(username
, c
->parameter
))
170 if (getpid_cached() == 1)
171 return streq(c
->parameter
, "root");
174 r
= get_user_creds(&u
, &id
, NULL
, NULL
, NULL
);
178 return id
== getuid() || id
== geteuid();
181 static int condition_test_group(Condition
*c
) {
186 assert(c
->parameter
);
187 assert(c
->type
== CONDITION_GROUP
);
189 r
= parse_gid(c
->parameter
, &id
);
193 /* Avoid any NSS lookups if we are PID1 */
194 if (getpid_cached() == 1)
195 return streq(c
->parameter
, "root");
197 return in_group(c
->parameter
) > 0;
200 static int condition_test_virtualization(Condition
*c
) {
204 assert(c
->parameter
);
205 assert(c
->type
== CONDITION_VIRTUALIZATION
);
207 if (streq(c
->parameter
, "private-users"))
208 return running_in_userns();
210 v
= detect_virtualization();
214 /* First, compare with yes/no */
215 b
= parse_boolean(c
->parameter
);
219 /* Then, compare categorization */
220 if (streq(c
->parameter
, "vm"))
221 return VIRTUALIZATION_IS_VM(v
);
223 if (streq(c
->parameter
, "container"))
224 return VIRTUALIZATION_IS_CONTAINER(v
);
226 /* Finally compare id */
227 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
230 static int condition_test_architecture(Condition
*c
) {
234 assert(c
->parameter
);
235 assert(c
->type
== CONDITION_ARCHITECTURE
);
237 a
= uname_architecture();
241 if (streq(c
->parameter
, "native"))
242 b
= native_architecture();
244 b
= architecture_from_string(c
->parameter
);
245 if (b
< 0) /* unknown architecture? Then it's definitely not ours */
252 static int condition_test_host(Condition
*c
) {
253 _cleanup_free_
char *h
= NULL
;
258 assert(c
->parameter
);
259 assert(c
->type
== CONDITION_HOST
);
261 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
263 r
= sd_id128_get_machine(&y
);
267 return sd_id128_equal(x
, y
);
270 h
= gethostname_malloc();
274 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
277 static int condition_test_ac_power(Condition
*c
) {
281 assert(c
->parameter
);
282 assert(c
->type
== CONDITION_AC_POWER
);
284 r
= parse_boolean(c
->parameter
);
288 return (on_ac_power() != 0) == !!r
;
291 static int condition_test_security(Condition
*c
) {
293 assert(c
->parameter
);
294 assert(c
->type
== CONDITION_SECURITY
);
296 if (streq(c
->parameter
, "selinux"))
297 return mac_selinux_use();
298 if (streq(c
->parameter
, "smack"))
299 return mac_smack_use();
300 if (streq(c
->parameter
, "apparmor"))
301 return mac_apparmor_use();
302 if (streq(c
->parameter
, "audit"))
304 if (streq(c
->parameter
, "ima"))
306 if (streq(c
->parameter
, "tomoyo"))
307 return mac_tomoyo_use();
312 static int condition_test_capability(Condition
*c
) {
313 _cleanup_fclose_
FILE *f
= NULL
;
316 unsigned long long capabilities
= -1;
319 assert(c
->parameter
);
320 assert(c
->type
== CONDITION_CAPABILITY
);
322 /* If it's an invalid capability, we don't have it */
323 value
= capability_from_name(c
->parameter
);
327 /* If it's a valid capability we default to assume
330 f
= fopen("/proc/self/status", "re");
334 while (fgets(line
, sizeof(line
), f
)) {
337 if (startswith(line
, "CapBnd:")) {
338 (void) sscanf(line
+7, "%llx", &capabilities
);
343 return !!(capabilities
& (1ULL << value
));
346 static int condition_test_needs_update(Condition
*c
) {
348 struct stat usr
, other
;
351 assert(c
->parameter
);
352 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
354 /* If the file system is read-only we shouldn't suggest an update */
355 if (path_is_read_only_fs(c
->parameter
) > 0)
358 /* Any other failure means we should allow the condition to be true,
359 * so that we rather invoke too many update tools than too
362 if (!path_is_absolute(c
->parameter
))
365 p
= strjoina(c
->parameter
, "/.updated");
366 if (lstat(p
, &other
) < 0)
369 if (lstat("/usr/", &usr
) < 0)
373 * First, compare seconds as they are always accurate...
375 if (usr
.st_mtim
.tv_sec
!= other
.st_mtim
.tv_sec
)
376 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
;
379 * ...then compare nanoseconds.
381 * A false positive is only possible when /usr's nanoseconds > 0
382 * (otherwise /usr cannot be strictly newer than the target file)
383 * AND the target file's nanoseconds == 0
384 * (otherwise the filesystem supports nsec timestamps, see stat(2)).
386 if (usr
.st_mtim
.tv_nsec
> 0 && other
.st_mtim
.tv_nsec
== 0) {
387 _cleanup_free_
char *timestamp_str
= NULL
;
391 r
= parse_env_file(p
, NULL
, "TIMESTAMP_NSEC", ×tamp_str
, NULL
);
393 log_error_errno(r
, "Failed to parse timestamp file '%s', using mtime: %m", p
);
396 log_debug("No data in timestamp file '%s', using mtime", p
);
400 r
= safe_atou64(timestamp_str
, ×tamp
);
402 log_error_errno(r
, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str
, p
);
406 timespec_store(&other
.st_mtim
, timestamp
);
409 return usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
;
412 static int condition_test_first_boot(Condition
*c
) {
416 assert(c
->parameter
);
417 assert(c
->type
== CONDITION_FIRST_BOOT
);
419 r
= parse_boolean(c
->parameter
);
423 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
426 static int condition_test_path_exists(Condition
*c
) {
428 assert(c
->parameter
);
429 assert(c
->type
== CONDITION_PATH_EXISTS
);
431 return access(c
->parameter
, F_OK
) >= 0;
434 static int condition_test_path_exists_glob(Condition
*c
) {
436 assert(c
->parameter
);
437 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
439 return glob_exists(c
->parameter
) > 0;
442 static int condition_test_path_is_directory(Condition
*c
) {
444 assert(c
->parameter
);
445 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
447 return is_dir(c
->parameter
, true) > 0;
450 static int condition_test_path_is_symbolic_link(Condition
*c
) {
452 assert(c
->parameter
);
453 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
455 return is_symlink(c
->parameter
) > 0;
458 static int condition_test_path_is_mount_point(Condition
*c
) {
460 assert(c
->parameter
);
461 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
463 return path_is_mount_point(c
->parameter
, NULL
, AT_SYMLINK_FOLLOW
) > 0;
466 static int condition_test_path_is_read_write(Condition
*c
) {
468 assert(c
->parameter
);
469 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
471 return path_is_read_only_fs(c
->parameter
) <= 0;
474 static int condition_test_directory_not_empty(Condition
*c
) {
478 assert(c
->parameter
);
479 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
481 r
= dir_is_empty(c
->parameter
);
482 return r
<= 0 && r
!= -ENOENT
;
485 static int condition_test_file_not_empty(Condition
*c
) {
489 assert(c
->parameter
);
490 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
492 return (stat(c
->parameter
, &st
) >= 0 &&
493 S_ISREG(st
.st_mode
) &&
497 static int condition_test_file_is_executable(Condition
*c
) {
501 assert(c
->parameter
);
502 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
504 return (stat(c
->parameter
, &st
) >= 0 &&
505 S_ISREG(st
.st_mode
) &&
506 (st
.st_mode
& 0111));
509 static int condition_test_null(Condition
*c
) {
511 assert(c
->type
== CONDITION_NULL
);
513 /* Note that during parsing we already evaluate the string and
514 * store it in c->negate */
518 int condition_test(Condition
*c
) {
520 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
521 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
522 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
523 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
524 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
525 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
526 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
527 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
528 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
529 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
530 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
531 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
532 [CONDITION_SECURITY
] = condition_test_security
,
533 [CONDITION_CAPABILITY
] = condition_test_capability
,
534 [CONDITION_HOST
] = condition_test_host
,
535 [CONDITION_AC_POWER
] = condition_test_ac_power
,
536 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
537 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
538 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
539 [CONDITION_USER
] = condition_test_user
,
540 [CONDITION_GROUP
] = condition_test_group
,
541 [CONDITION_NULL
] = condition_test_null
,
547 assert(c
->type
>= 0);
548 assert(c
->type
< _CONDITION_TYPE_MAX
);
550 r
= condition_tests
[c
->type
](c
);
552 c
->result
= CONDITION_ERROR
;
556 b
= (r
> 0) == !c
->negate
;
557 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
561 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
569 "%s\t%s: %s%s%s %s\n",
572 c
->trigger
? "|" : "",
573 c
->negate
? "!" : "",
575 condition_result_to_string(c
->result
));
578 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
581 LIST_FOREACH(conditions
, c
, first
)
582 condition_dump(c
, f
, prefix
, to_string
);
585 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
586 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
587 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
588 [CONDITION_HOST
] = "ConditionHost",
589 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
590 [CONDITION_SECURITY
] = "ConditionSecurity",
591 [CONDITION_CAPABILITY
] = "ConditionCapability",
592 [CONDITION_AC_POWER
] = "ConditionACPower",
593 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
594 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
595 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
596 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
597 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
598 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
599 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
600 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
601 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
602 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
603 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
604 [CONDITION_USER
] = "ConditionUser",
605 [CONDITION_GROUP
] = "ConditionGroup",
606 [CONDITION_NULL
] = "ConditionNull"
609 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
611 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
612 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
613 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
614 [CONDITION_HOST
] = "AssertHost",
615 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
616 [CONDITION_SECURITY
] = "AssertSecurity",
617 [CONDITION_CAPABILITY
] = "AssertCapability",
618 [CONDITION_AC_POWER
] = "AssertACPower",
619 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
620 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
621 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
622 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
623 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
624 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
625 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
626 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
627 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
628 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
629 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
630 [CONDITION_USER
] = "AssertUser",
631 [CONDITION_GROUP
] = "AssertGroup",
632 [CONDITION_NULL
] = "AssertNull"
635 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
637 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
638 [CONDITION_UNTESTED
] = "untested",
639 [CONDITION_SUCCEEDED
] = "succeeded",
640 [CONDITION_FAILED
] = "failed",
641 [CONDITION_ERROR
] = "error",
644 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);