1
0
mirror of https://github.com/systemd/systemd.git synced 2025-09-19 01:44:42 +03:00

Merge pull request #16118 from poettering/inaccessible-fixlets

move $XDG_RUNTIME_DIR/inaccessible/ to $XDG_RUNTIME_DIR/systemd/inaccessible
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2020-06-10 10:23:13 +02:00
committed by GitHub
9 changed files with 86 additions and 75 deletions

View File

@@ -14,11 +14,18 @@
#include "stdio-util.h" #include "stdio-util.h"
#include "user-util.h" #include "user-util.h"
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) { int mkdir_safe_internal(
const char *path,
mode_t mode,
uid_t uid, gid_t gid,
MkdirFlags flags,
mkdir_func_t _mkdir) {
struct stat st; struct stat st;
int r; int r;
assert(_mkdir != mkdir); assert(path);
assert(_mkdir && _mkdir != mkdir);
if (_mkdir(path, mode) >= 0) { if (_mkdir(path, mode) >= 0) {
r = chmod_and_chown(path, mode, uid, gid); r = chmod_and_chown(path, mode, uid, gid);
@@ -44,19 +51,16 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd
return -errno; return -errno;
} }
if (!S_ISDIR(st.st_mode)) { if (!S_ISDIR(st.st_mode))
log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ENOTDIR),
"Path \"%s\" already exists and is not a directory, refusing.", path); "Path \"%s\" already exists and is not a directory, refusing.", path);
return -ENOTDIR;
}
if ((st.st_mode & 0007) > (mode & 0007) || if ((st.st_mode & 0007) > (mode & 0007) ||
(st.st_mode & 0070) > (mode & 0070) || (st.st_mode & 0070) > (mode & 0070) ||
(st.st_mode & 0700) > (mode & 0700)) { (st.st_mode & 0700) > (mode & 0700))
log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
"Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.", "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
path, st.st_mode & 0777, mode); path, st.st_mode & 0777, mode);
return -EEXIST;
}
if ((uid != UID_INVALID && st.st_uid != uid) || if ((uid != UID_INVALID && st.st_uid != uid) ||
(gid != GID_INVALID && st.st_gid != gid)) { (gid != GID_INVALID && st.st_gid != gid)) {
char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-"; char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-";
@@ -65,10 +69,9 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd
xsprintf(u, UID_FMT, uid); xsprintf(u, UID_FMT, uid);
if (gid != UID_INVALID) if (gid != UID_INVALID)
xsprintf(g, GID_FMT, gid); xsprintf(g, GID_FMT, gid);
log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
"Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.", "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
path, st.st_uid, st.st_gid, u, g); path, st.st_uid, st.st_gid, u, g);
return -EEXIST;
} }
return 0; return 0;

View File

@@ -535,9 +535,9 @@ int mount_setup(bool loaded_policy, bool leave_propagation) {
(void) mkdir_label("/run/systemd", 0755); (void) mkdir_label("/run/systemd", 0755);
(void) mkdir_label("/run/systemd/system", 0755); (void) mkdir_label("/run/systemd/system", 0755);
/* Also create /run/systemd/inaccessible nodes, so that we always have something to mount inaccessible nodes /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
* from. */ * inaccessible nodes from. */
(void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID); (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
return 0; return 0;
} }

View File

@@ -939,10 +939,10 @@ static int apply_mount(
} }
if (geteuid() == 0) if (geteuid() == 0)
runtime_dir = "/run/systemd"; runtime_dir = "/run";
else { else {
if (asprintf(&tmp, "/run/user/"UID_FMT, geteuid()) < 0) if (asprintf(&tmp, "/run/user/" UID_FMT, geteuid()) < 0)
log_oom(); return -ENOMEM;
runtime_dir = tmp; runtime_dir = tmp;
} }

View File

@@ -78,7 +78,7 @@ static int user_mkdir_runtime_path(
r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options); r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
if (r < 0) { if (r < 0) {
if (!IN_SET(errno, EPERM, EACCES)) { if (!ERRNO_IS_PRIVILEGE(errno)) {
r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path); r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path);
goto fail; goto fail;
} }

View File

@@ -898,7 +898,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) {
return m->graceful ? 0 : r; return m->graceful ? 0 : r;
} }
r = mode_to_inaccessible_node("/run/systemd", st.st_mode, &source); r = mode_to_inaccessible_node(NULL, st.st_mode, &source);
if (r < 0) if (r < 0)
return m->graceful ? 0 : r; return m->graceful ? 0 : r;

View File

@@ -3482,7 +3482,7 @@ static int outer_child(
(void) dev_setup(directory, arg_uid_shift, arg_uid_shift); (void) dev_setup(directory, arg_uid_shift, arg_uid_shift);
p = prefix_roota(directory, "/run/systemd"); p = prefix_roota(directory, "/run");
(void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift); (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
r = setup_pts(directory); r = setup_pts(directory);

View File

@@ -56,31 +56,38 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
return 0; return 0;
} }
int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) { int make_inaccessible_nodes(
const char *runtime_dir,
uid_t uid,
gid_t gid) {
static const struct { static const struct {
const char *name; const char *name;
mode_t mode; mode_t mode;
} table[] = { } table[] = {
{ "", S_IFDIR | 0755 }, { "/systemd", S_IFDIR | 0755 },
{ "/inaccessible", S_IFDIR | 0000 }, { "/systemd/inaccessible", S_IFDIR | 0000 },
{ "/inaccessible/reg", S_IFREG | 0000 }, { "/systemd/inaccessible/reg", S_IFREG | 0000 },
{ "/inaccessible/dir", S_IFDIR | 0000 }, { "/systemd/inaccessible/dir", S_IFDIR | 0000 },
{ "/inaccessible/fifo", S_IFIFO | 0000 }, { "/systemd/inaccessible/fifo", S_IFIFO | 0000 },
{ "/inaccessible/sock", S_IFSOCK | 0000 }, { "/systemd/inaccessible/sock", S_IFSOCK | 0000 },
/* The following two are likely to fail if we lack the privs for it (for example in an userns /* The following two are likely to fail if we lack the privs for it (for example in an userns
* environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
* device nodes to be created). But that's entirely fine. Consumers of these files should carry * device nodes to be created). But that's entirely fine. Consumers of these files should carry
* fallback to use a different node then, for example <root>/inaccessible/sock, which is close * fallback to use a different node then, for example <root>/inaccessible/sock, which is close
* enough in behaviour and semantics for most uses. */ * enough in behaviour and semantics for most uses. */
{ "/inaccessible/chr", S_IFCHR | 0000 }, { "/systemd/inaccessible/chr", S_IFCHR | 0000 },
{ "/inaccessible/blk", S_IFBLK | 0000 }, { "/systemd/inaccessible/blk", S_IFBLK | 0000 },
}; };
_cleanup_umask_ mode_t u; _cleanup_umask_ mode_t u;
size_t i; size_t i;
int r; int r;
if (!runtime_dir)
runtime_dir = "/run";
u = umask(0000); u = umask(0000);
/* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
@@ -91,7 +98,7 @@ int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
for (i = 0; i < ELEMENTSOF(table); i++) { for (i = 0; i < ELEMENTSOF(table); i++) {
_cleanup_free_ char *path = NULL; _cleanup_free_ char *path = NULL;
path = path_join(root, table[i].name); path = path_join(runtime_dir, table[i].name);
if (!path) if (!path)
return log_oom(); return log_oom();

View File

@@ -397,71 +397,73 @@ int repeat_unmount(const char *path, int flags) {
} }
} }
int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest) { int mode_to_inaccessible_node(
/* This function maps a node type to a corresponding inaccessible file node. These nodes are created during const char *runtime_dir,
* early boot by PID 1. In some cases we lacked the privs to create the character and block devices (maybe mode_t mode,
* because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a devices policy that excludes char **ret) {
* device nodes with major and minor of 0), but that's fine, in that case we use an AF_UNIX file node instead,
* which is not the same, but close enough for most uses. And most importantly, the kernel allows bind mounts /* This function maps a node type to a corresponding inaccessible file node. These nodes are created
* from socket nodes to any non-directory file nodes, and that's the most important thing that matters. */ * during early boot by PID 1. In some cases we lacked the privs to create the character and block
* devices (maybe because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a
* devices policy that excludes device nodes with major and minor of 0), but that's fine, in that
* case we use an AF_UNIX file node instead, which is not the same, but close enough for most
* uses. And most importantly, the kernel allows bind mounts from socket nodes to any non-directory
* file nodes, and that's the most important thing that matters.
*
* Note that the runtime directory argument shall be the top-level runtime directory, i.e. /run/ if
* we operate in system context and $XDG_RUNTIME_DIR if we operate in user context. */
_cleanup_free_ char *d = NULL; _cleanup_free_ char *d = NULL;
const char *node = NULL; const char *node = NULL;
char *tmp; bool fallback = false;
assert(dest); assert(ret);
if (!runtime_dir)
runtime_dir = "/run";
switch(mode & S_IFMT) { switch(mode & S_IFMT) {
case S_IFREG: case S_IFREG:
node = "/inaccessible/reg"; node = "/systemd/inaccessible/reg";
break; break;
case S_IFDIR: case S_IFDIR:
node = "/inaccessible/dir"; node = "/systemd/inaccessible/dir";
break; break;
case S_IFCHR: case S_IFCHR:
d = path_join(runtime_dir, "/inaccessible/chr"); node = "/systemd/inaccessible/chr";
if (!d) fallback = true;
return log_oom();
if (access(d, F_OK) == 0) {
*dest = TAKE_PTR(d);
return 0;
}
node = "/inaccessible/sock";
break; break;
case S_IFBLK: case S_IFBLK:
d = path_join(runtime_dir, "/inaccessible/blk"); node = "/systemd/inaccessible/blk";
if (!d) fallback = true;
return log_oom();
if (access(d, F_OK) == 0) {
*dest = TAKE_PTR(d);
return 0;
}
node = "/inaccessible/sock";
break; break;
case S_IFIFO: case S_IFIFO:
node = "/inaccessible/fifo"; node = "/systemd/inaccessible/fifo";
break; break;
case S_IFSOCK: case S_IFSOCK:
node = "/inaccessible/sock"; node = "/systemd/inaccessible/sock";
break; break;
} }
if (!node) if (!node)
return -EINVAL; return -EINVAL;
tmp = path_join(runtime_dir, node); d = path_join(runtime_dir, node);
if (!tmp) if (!d)
return log_oom(); return -ENOMEM;
*dest = tmp; if (fallback && access(d, F_OK) < 0) {
free(d);
d = path_join(runtime_dir, "/systemd/inaccessible/sock");
if (!d)
return -ENOMEM;
}
*ret = TAKE_PTR(d);
return 0; return 0;
} }

View File

@@ -20,7 +20,6 @@ int main(int argc, char *argv[]) {
f = prefix_roota(p, "/run"); f = prefix_roota(p, "/run");
assert_se(mkdir(f, 0755) >= 0); assert_se(mkdir(f, 0755) >= 0);
f = prefix_roota(p, "/run/systemd");
assert_se(make_inaccessible_nodes(f, 1, 1) >= 0); assert_se(make_inaccessible_nodes(f, 1, 1) >= 0);
f = prefix_roota(p, "/run/systemd/inaccessible/reg"); f = prefix_roota(p, "/run/systemd/inaccessible/reg");