-/*-*- 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 "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"
return r;
equal = !!strchr(c->parameter, '=');
- p = line;
- for (;;) {
+ for (p = line;;) {
_cleanup_free_ char *word = NULL;
bool found;
const char *f;
f = startswith(word, c->parameter);
- found = f && (*f == '=' || *f == 0);
+ found = f && IN_SET(*f, 0, '=');
}
if (found)
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;
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));
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;
}
return use_audit();
if (streq(c->parameter, "ima"))
return use_ima();
+ if (streq(c->parameter, "tomoyo"))
+ return mac_tomoyo_use();
return false;
}
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))
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", ×tamp_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, ×tamp);
+ 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) {
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) {
[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,
};
[CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
[CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
[CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
+ [CONDITION_USER] = "ConditionUser",
+ [CONDITION_GROUP] = "ConditionGroup",
[CONDITION_NULL] = "ConditionNull"
};
[CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
[CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
[CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
+ [CONDITION_USER] = "AssertUser",
+ [CONDITION_GROUP] = "AssertGroup",
[CONDITION_NULL] = "AssertNull"
};