core: add special handling for devices cgroup allow lists for /dev/block/* and /dev/char/* device nodes

This adds some code to hanlde /dev/block/* and /dev/char/* device node
paths specially: instead of actually stat()ing them we'll just parse the
major/minor name from the name. This is useful 'hack' to allow clients
to install whitelists for devices that don't actually have to exist.

Also, let's similarly handle /run/systemd/inaccessible/{blk|chr}. This
allows us to simplify our built-in default whitelist to not require a
"ignore_enoent" mode for these nodes.

In general we should be careful with hardcoding major/minor numbers, but
in this case this should safe.
This commit is contained in:
Lennart Poettering 2018-06-29 12:03:33 +02:00
parent 3a47c40d97
commit 74c48bf5a8
1 changed files with 64 additions and 18 deletions

View File

@ -19,6 +19,7 @@
#include "process-util.h"
#include "procfs-util.h"
#include "special.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@ -407,31 +408,76 @@ static int lookup_block_device(const char *p, dev_t *ret) {
return 0;
}
static int shortcut_special_device_path(const char *p, struct stat *ret) {
const char *w;
mode_t mode;
dev_t devt;
int r;
assert(p);
assert(ret);
if (path_equal(p, "/run/systemd/inaccessible/chr")) {
*ret = (struct stat) {
.st_mode = S_IFCHR,
.st_rdev = makedev(0, 0),
};
return 0;
}
if (path_equal(p, "/run/systemd/inaccessible/blk")) {
*ret = (struct stat) {
.st_mode = S_IFBLK,
.st_rdev = makedev(0, 0),
};
return 0;
}
w = path_startswith(p, "/dev/block/");
if (w)
mode = S_IFBLK;
else {
w = path_startswith(p, "/dev/char/");
if (!w)
return -ENODEV;
mode = S_IFCHR;
}
r = parse_dev(w, &devt);
if (r < 0)
return r;
*ret = (struct stat) {
.st_mode = mode,
.st_rdev = devt,
};
return 0;
}
static int whitelist_device(BPFProgram *prog, const char *path, const char *node, const char *acc) {
struct stat st;
bool ignore_notfound;
int r;
assert(path);
assert(acc);
if (node[0] == '-') {
/* Non-existent paths starting with "-" must be silently ignored */
node++;
ignore_notfound = true;
} else
ignore_notfound = false;
/* Some special handling for /dev/block/%u:%u, /dev/char/%u:%u, /run/systemd/inaccessible/chr and
* /run/systemd/inaccessible/blk paths. Instead of stat()ing these we parse out the major/minor directly. This
* means clients can use these path without the device node actually around */
r = shortcut_special_device_path(node, &st);
if (r < 0) {
if (r != -ENODEV)
return log_warning_errno(r, "Couldn't parse major/minor from device path '%s': %m", node);
if (stat(node, &st) < 0) {
if (errno == ENOENT && ignore_notfound)
return 0;
if (stat(node, &st) < 0)
return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
return log_warning_errno(errno, "Couldn't stat device %s: %m", node);
}
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_warning("%s is not a device.", node);
return -ENODEV;
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_warning("%s is not a device.", node);
return -ENODEV;
}
}
if (cg_all_unified() > 0) {
@ -1098,8 +1144,8 @@ static void cgroup_context_apply(
"/dev/tty\0" "rwm\0"
"/dev/ptmx\0" "rwm\0"
/* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */
"-/run/systemd/inaccessible/chr\0" "rwm\0"
"-/run/systemd/inaccessible/blk\0" "rwm\0";
"/run/systemd/inaccessible/chr\0" "rwm\0"
"/run/systemd/inaccessible/blk\0" "rwm\0";
const char *x, *y;