]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/condition.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / shared / condition.c
index d06120f0d754d23532575cb51d3c7a48f32f6407..11555b474cec72a7ad4635f0f127df18407ee8a3 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 ***/
 
 #include <errno.h>
+#include <fcntl.h>
 #include <fnmatch.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
 #include "sd-id128.h"
 
+#include "alloc-util.h"
 #include "apparmor-util.h"
 #include "architecture.h"
 #include "audit-util.h"
 #include "condition.h"
 #include "extract-word.h"
 #include "fd-util.h"
+#include "fileio.h"
+#include "glob-util.h"
 #include "hostname-util.h"
 #include "ima-util.h"
+#include "list.h"
+#include "macro.h"
 #include "mount-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
+#include "process-util.h"
 #include "selinux-util.h"
 #include "smack-util.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "tomoyo-util.h"
+#include "user-util.h"
 #include "util.h"
 #include "virt.h"
 
@@ -104,9 +116,8 @@ static int condition_test_kernel_command_line(Condition *c) {
                 return r;
 
         equal = !!strchr(c->parameter, '=');
-        p = line;
 
-        for (;;) {
+        for (p = line;;) {
                 _cleanup_free_ char *word = NULL;
                 bool found;
 
@@ -122,7 +133,7 @@ static int condition_test_kernel_command_line(Condition *c) {
                         const char *f;
 
                         f = startswith(word, c->parameter);
-                        found = f && (*f == '=' || *f == 0);
+                        found = f && IN_SET(*f, 0, '=');
                 }
 
                 if (found)
@@ -132,6 +143,60 @@ static int condition_test_kernel_command_line(Condition *c) {
         return false;
 }
 
+static int condition_test_user(Condition *c) {
+        uid_t id;
+        int r;
+        _cleanup_free_ char *username = NULL;
+        const char *u;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_USER);
+
+        r = parse_uid(c->parameter, &id);
+        if (r >= 0)
+                return id == getuid() || id == geteuid();
+
+        if (streq("@system", c->parameter))
+                return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX;
+
+        username = getusername_malloc();
+        if (!username)
+                return -ENOMEM;
+
+        if (streq(username, c->parameter))
+                return 1;
+
+        if (getpid_cached() == 1)
+                return streq(c->parameter, "root");
+
+        u = c->parameter;
+        r = get_user_creds(&u, &id, NULL, NULL, NULL);
+        if (r < 0)
+                return 0;
+
+        return id == getuid() || id == geteuid();
+}
+
+static int condition_test_group(Condition *c) {
+        gid_t id;
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_GROUP);
+
+        r = parse_gid(c->parameter, &id);
+        if (r >= 0)
+                return in_gid(id);
+
+        /* Avoid any NSS lookups if we are PID1 */
+        if (getpid_cached() == 1)
+                return streq(c->parameter, "root");
+
+        return in_group(c->parameter) > 0;
+}
+
 static int condition_test_virtualization(Condition *c) {
         int b, v;
 
@@ -139,25 +204,24 @@ static int condition_test_virtualization(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_VIRTUALIZATION);
 
+        if (streq(c->parameter, "private-users"))
+                return running_in_userns();
+
         v = detect_virtualization();
         if (v < 0)
                 return v;
 
         /* First, compare with yes/no */
         b = parse_boolean(c->parameter);
-
-        if (v > 0 && b > 0)
-                return true;
-
-        if (v == 0 && b == 0)
-                return true;
+        if (b >= 0)
+                return b == !!v;
 
         /* Then, compare categorization */
-        if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm"))
-                return true;
+        if (streq(c->parameter, "vm"))
+                return VIRTUALIZATION_IS_VM(v);
 
-        if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container"))
-                return true;
+        if (streq(c->parameter, "container"))
+                return VIRTUALIZATION_IS_CONTAINER(v);
 
         /* Finally compare id */
         return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
@@ -176,10 +240,11 @@ static int condition_test_architecture(Condition *c) {
 
         if (streq(c->parameter, "native"))
                 b = native_architecture();
-        else
+        else {
                 b = architecture_from_string(c->parameter);
-        if (b < 0)
-                return b;
+                if (b < 0) /* unknown architecture? Then it's definitely not ours */
+                        return false;
+        }
 
         return a == b;
 }
@@ -238,6 +303,8 @@ static int condition_test_security(Condition *c) {
                 return use_audit();
         if (streq(c->parameter, "ima"))
                 return use_ima();
+        if (streq(c->parameter, "tomoyo"))
+                return mac_tomoyo_use();
 
         return false;
 }
@@ -289,7 +356,7 @@ static int condition_test_needs_update(Condition *c) {
                 return false;
 
         /* Any other failure means we should allow the condition to be true,
-         * so that we rather invoke too many update tools then too
+         * so that we rather invoke too many update tools than too
          * few. */
 
         if (!path_is_absolute(c->parameter))
@@ -302,8 +369,44 @@ static int condition_test_needs_update(Condition *c) {
         if (lstat("/usr/", &usr) < 0)
                 return true;
 
-        return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
-                (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
+        /*
+         * First, compare seconds as they are always accurate...
+         */
+        if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
+                return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
+
+        /*
+         * ...then compare nanoseconds.
+         *
+         * A false positive is only possible when /usr's nanoseconds > 0
+         * (otherwise /usr cannot be strictly newer than the target file)
+         * AND the target file's nanoseconds == 0
+         * (otherwise the filesystem supports nsec timestamps, see stat(2)).
+         */
+        if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) {
+                _cleanup_free_ char *timestamp_str = NULL;
+                uint64_t timestamp;
+                int r;
+
+                r = parse_env_file(p, NULL, "TIMESTAMP_NSEC", &timestamp_str, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
+                        return true;
+                } else if (r == 0) {
+                        log_debug("No data in timestamp file '%s', using mtime", p);
+                        return true;
+                }
+
+                r = safe_atou64(timestamp_str, &timestamp);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
+                        return true;
+                }
+
+                timespec_store(&other.st_mtim, timestamp);
+        }
+
+        return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
 }
 
 static int condition_test_first_boot(Condition *c) {
@@ -357,7 +460,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
 
-        return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
+        return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
 }
 
 static int condition_test_path_is_read_write(Condition *c) {
@@ -433,6 +536,8 @@ int condition_test(Condition *c) {
                 [CONDITION_ARCHITECTURE] = condition_test_architecture,
                 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
+                [CONDITION_USER] = condition_test_user,
+                [CONDITION_GROUP] = condition_test_group,
                 [CONDITION_NULL] = condition_test_null,
         };
 
@@ -496,6 +601,8 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
+        [CONDITION_USER] = "ConditionUser",
+        [CONDITION_GROUP] = "ConditionGroup",
         [CONDITION_NULL] = "ConditionNull"
 };
 
@@ -520,6 +627,8 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
+        [CONDITION_USER] = "AssertUser",
+        [CONDITION_GROUP] = "AssertGroup",
         [CONDITION_NULL] = "AssertNull"
 };