summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/bus-unit-util.c2
-rw-r--r--src/shared/clean-ipc.c106
-rw-r--r--src/shared/clean-ipc.h4
-rw-r--r--src/shared/install.c100
4 files changed, 137 insertions, 75 deletions
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 28bfa8b522..feb4a06737 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser")) {
+ "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
r = parse_boolean(eq);
if (r < 0)
diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c
index a3ac7aeb82..d5db604f03 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -41,8 +41,20 @@
#include "macro.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
-static int clean_sysvipc_shm(uid_t delete_uid) {
+static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
+
+ if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
+ return true;
+
+ if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
+ return true;
+
+ return false;
+}
+
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
if (n_attached > 0)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
@@ -89,7 +101,8 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV shared memory segment %i: %m",
shmid);
- }
+ } else
+ log_debug("Removed SysV shared memory segment %i.", shmid);
}
return ret;
@@ -98,7 +111,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
}
-static int clean_sysvipc_sem(uid_t delete_uid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
&semid, &uid, &gid, &cuid, &cgid) != 5)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (semctl(semid, 0, IPC_RMID) < 0) {
@@ -140,7 +153,8 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV semaphores object %i: %m",
semid);
- }
+ } else
+ log_debug("Removed SysV semaphore %i.", semid);
}
return ret;
@@ -149,7 +163,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
}
-static int clean_sysvipc_msg(uid_t delete_uid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (msgctl(msgid, IPC_RMID, NULL) < 0) {
@@ -192,7 +206,8 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV message queue %i: %m",
msgid);
- }
+ } else
+ log_debug("Removed SysV message queue %i.", msgid);
}
return ret;
@@ -201,13 +216,13 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
}
-static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
struct dirent *de;
int ret = 0, r;
assert(dir);
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
if (STR_IN_SET(de->d_name, "..", "."))
@@ -217,12 +232,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
+ ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
if (S_ISDIR(st.st_mode)) {
@@ -230,12 +244,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
if (!kid) {
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ if (errno != ENOENT)
+ ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
} else {
- r = clean_posix_shm_internal(kid, uid);
+ r = clean_posix_shm_internal(kid, uid, gid);
if (r < 0)
ret = r;
}
@@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory directory %s", de->d_name);
} else {
if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
@@ -255,20 +267,19 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory segment %s", de->d_name);
}
}
return ret;
fail:
- log_warning_errno(errno, "Failed to read /dev/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /dev/shm: %m");
}
-static int clean_posix_shm(uid_t uid) {
+static int clean_posix_shm(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
dir = opendir("/dev/shm");
@@ -279,10 +290,10 @@ static int clean_posix_shm(uid_t uid) {
return log_warning_errno(errno, "Failed to open /dev/shm: %m");
}
- return clean_posix_shm_internal(dir, uid);
+ return clean_posix_shm_internal(dir, uid, gid);
}
-static int clean_posix_mq(uid_t uid) {
+static int clean_posix_mq(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *de;
int ret = 0;
@@ -295,7 +306,7 @@ static int clean_posix_mq(uid_t uid) {
return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
}
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
char fn[1+strlen(de->d_name)+1];
@@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) {
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
fn[0] = '/';
@@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) {
ret = log_warning_errno(errno,
"Failed to unlink POSIX message queue %s: %m",
fn);
- }
+ } else
+ log_debug("Removed POSIX message queue %s", fn);
}
return ret;
@@ -334,32 +346,44 @@ fail:
return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
}
-int clean_ipc(uid_t uid) {
+int clean_ipc(uid_t uid, gid_t gid) {
int ret = 0, r;
- /* Refuse to clean IPC of the root and system users */
- if (uid <= SYSTEM_UID_MAX)
+ /* Anything to do? */
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ /* Refuse to clean IPC of the root user */
+ if (uid == 0 && gid == 0)
return 0;
- r = clean_sysvipc_shm(uid);
+ r = clean_sysvipc_shm(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_sem(uid);
+ r = clean_sysvipc_sem(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_msg(uid);
+ r = clean_sysvipc_msg(uid, gid);
if (r < 0)
ret = r;
- r = clean_posix_shm(uid);
+ r = clean_posix_shm(uid, gid);
if (r < 0)
ret = r;
- r = clean_posix_mq(uid);
+ r = clean_posix_mq(uid, gid);
if (r < 0)
ret = r;
return ret;
}
+
+int clean_ipc_by_uid(uid_t uid) {
+ return clean_ipc(uid, GID_INVALID);
+}
+
+int clean_ipc_by_gid(gid_t gid) {
+ return clean_ipc(UID_INVALID, gid);
+}
diff --git a/src/shared/clean-ipc.h b/src/shared/clean-ipc.h
index 44a83afcf7..6ca57f44fd 100644
--- a/src/shared/clean-ipc.h
+++ b/src/shared/clean-ipc.h
@@ -21,4 +21,6 @@
#include <sys/types.h>
-int clean_ipc(uid_t uid);
+int clean_ipc(uid_t uid, gid_t gid);
+int clean_ipc_by_uid(uid_t uid);
+int clean_ipc_by_gid(gid_t gid);
diff --git a/src/shared/install.c b/src/shared/install.c
index e740ef3910..6a16f8985b 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -393,19 +393,40 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
log_error_errno(r, "Failed to %s: %m.", verb);
}
+/**
+ * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
+ * wc should be the full path in the host file system.
+ */
+static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
+ assert(path_is_absolute(wd));
+
+ /* This will give incorrect results if the paths are relative and go outside
+ * of the chroot. False negatives are possible. */
+
+ a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
+ b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
+ return path_equal_or_files_same(a, b);
+}
+
static int create_symlink(
+ const LookupPaths *paths,
const char *old_path,
const char *new_path,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
- _cleanup_free_ char *dest = NULL;
+ _cleanup_free_ char *dest = NULL, *dirname = NULL;
+ const char *rp;
int r;
assert(old_path);
assert(new_path);
+ rp = skip_root(paths, old_path);
+ if (rp)
+ old_path = rp;
+
/* Actually create a symlink, and remember that we did. Is
* smart enough to check if there's already a valid symlink in
* place.
@@ -436,7 +457,11 @@ static int create_symlink(
return r;
}
- if (path_equal(dest, old_path))
+ dirname = dirname_malloc(new_path);
+ if (!dirname)
+ return -ENOMEM;
+
+ if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
return 1;
if (!force) {
@@ -620,7 +645,7 @@ static int remove_marked_symlinks(
fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
- return -errno;
+ return errno == ENOENT ? 0 : -errno;
do {
int q, cfd;
@@ -903,6 +928,10 @@ static int install_info_may_process(
return 0;
}
+/**
+ * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
+ * hashmap, or retrieves the existing one if already present.
+ */
static int install_info_add(
InstallContext *c,
const char *name,
@@ -1334,9 +1363,8 @@ static int install_info_follow(
}
/**
- * Search for the unit file. If the unit name is a symlink,
- * follow the symlink to the target, maybe more than once.
- * Propagate the instance name if present.
+ * Search for the unit file. If the unit name is a symlink, follow the symlink to the
+ * target, maybe more than once. Propagate the instance name if present.
*/
static int install_info_traverse(
UnitFileScope scope,
@@ -1421,6 +1449,10 @@ static int install_info_traverse(
return 0;
}
+/**
+ * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
+ * or the name (otherwise). root_dir is prepended to the path.
+ */
static int install_info_add_auto(
InstallContext *c,
const LookupPaths *paths,
@@ -1479,7 +1511,6 @@ static int install_info_symlink_alias(
STRV_FOREACH(s, i->aliases) {
_cleanup_free_ char *alias_path = NULL, *dst = NULL;
- const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1489,9 +1520,7 @@ static int install_info_symlink_alias(
if (!alias_path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes);
+ q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1535,7 +1564,6 @@ static int install_info_symlink_wants(
STRV_FOREACH(s, list) {
_cleanup_free_ char *path = NULL, *dst = NULL;
- const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1550,9 +1578,7 @@ static int install_info_symlink_wants(
if (!path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- q = create_symlink(rp ?: i->path, path, true, changes, n_changes);
+ q = create_symlink(paths, i->path, path, true, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1569,7 +1595,6 @@ static int install_info_symlink_link(
unsigned *n_changes) {
_cleanup_free_ char *path = NULL;
- const char *rp;
int r;
assert(i);
@@ -1587,9 +1612,7 @@ static int install_info_symlink_link(
if (!path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- return create_symlink(rp ?: i->path, path, force, changes, n_changes);
+ return create_symlink(paths, i->path, path, force, changes, n_changes);
}
static int install_info_apply(
@@ -1663,6 +1686,17 @@ static int install_context_apply(
if (r < 0)
return r;
+ /* We can attempt to process a masked unit when a different unit
+ * that we were processing specifies it in DefaultInstance= or Also=. */
+ if (i->type == UNIT_FILE_TYPE_MASKED) {
+ unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
+ if (r >= 0)
+ /* Assume that some *could* have been enabled here, avoid
+ * "empty [Install] section" warning. */
+ r += 1;
+ continue;
+ }
+
if (i->type != UNIT_FILE_TYPE_REGULAR)
continue;
@@ -1765,7 +1799,7 @@ int unit_file_mask(
if (!path)
return -ENOMEM;
- q = create_symlink("/dev/null", path, force, changes, n_changes);
+ q = create_symlink(&paths, "/dev/null", path, force, changes, n_changes);
if (q < 0 && r >= 0)
r = q;
}
@@ -1925,14 +1959,12 @@ int unit_file_link(
r = 0;
STRV_FOREACH(i, todo) {
_cleanup_free_ char *new_path = NULL;
- const char *old_path;
- old_path = skip_root(&paths, *i);
new_path = path_make_absolute(basename(*i), config_path);
if (!new_path)
return -ENOMEM;
- q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes);
+ q = create_symlink(&paths, *i, new_path, force, changes, n_changes);
if (q < 0 && r >= 0)
r = q;
}
@@ -1967,7 +1999,6 @@ int unit_file_revert(
unsigned *n_changes) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- /* _cleanup_(install_context_done) InstallContext c = {}; */
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_strv_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
@@ -2312,7 +2343,7 @@ int unit_file_set_default(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
UnitFileInstallInfo *i;
- const char *new_path, *old_path;
+ const char *new_path;
int r;
assert(scope >= 0);
@@ -2335,10 +2366,8 @@ int unit_file_set_default(
if (r < 0)
return r;
- old_path = skip_root(&paths, i->path);
new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
-
- return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes);
+ return create_symlink(&paths, i->path, new_path, force, changes, n_changes);
}
int unit_file_get_default(
@@ -2685,18 +2714,25 @@ static int preset_prepare_one(
InstallContext *plus,
InstallContext *minus,
LookupPaths *paths,
- UnitFilePresetMode mode,
const char *name,
Presets presets,
UnitFileChange **changes,
unsigned *n_changes) {
+ _cleanup_(install_context_done) InstallContext tmp = {};
UnitFileInstallInfo *i;
int r;
- if (install_info_find(plus, name) ||
- install_info_find(minus, name))
+ if (install_info_find(plus, name) || install_info_find(minus, name))
+ return 0;
+
+ r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
+ if (!streq(name, i->name)) {
+ log_debug("Skipping %s because is an alias for %s", name, i->name);
return 0;
+ }
r = query_presets(name, presets);
if (r < 0)
@@ -2748,7 +2784,7 @@ int unit_file_preset(
return r;
STRV_FOREACH(i, files) {
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
if (r < 0)
return r;
}
@@ -2809,7 +2845,7 @@ int unit_file_preset_all(
continue;
/* we don't pass changes[] in, because we want to handle errors on our own */
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
if (r == -ERFKILL)
r = unit_file_changes_add(changes, n_changes,
UNIT_FILE_IS_MASKED, de->d_name, NULL);