diff options
-rw-r--r-- | man/systemd.resource-control.xml | 26 | ||||
-rw-r--r-- | src/core/cgroup.c | 86 | ||||
-rw-r--r-- | src/core/dbus-cgroup.c | 7 | ||||
-rw-r--r-- | src/core/load-fragment.c | 10 |
4 files changed, 115 insertions, 14 deletions
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index fcfe861256..0ee983b1c3 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -247,17 +247,31 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. <listitem> <para>Control access to specific device nodes by the executed processes. Takes two space-separated strings: a - device node path (such as <filename>/dev/null</filename>) - followed by a combination of <constant>r</constant>, - <constant>w</constant>, <constant>m</constant> to control + device node specifier followed by a combination of + <constant>r</constant>, <constant>w</constant>, + <constant>m</constant> to control <emphasis>r</emphasis>eading, <emphasis>w</emphasis>riting, - or creation of the specific device node by the unit + or creation of the specific device node(s) by the unit (<emphasis>m</emphasis>knod), respectively. This controls the <literal>devices.allow</literal> and <literal>devices.deny</literal> control group - attributes. For details about these control group attributes, - see <ulink + attributes. For details about these control group + attributes, see <ulink url="https://www.kernel.org/doc/Documentation/cgroups/devices.txt">devices.txt</ulink>.</para> + + <para>The device node specifier is either a path to a device + node in the file system, starting with + <filename>/dev/</filename>, or a string starting with either + <literal>char-</literal> or <literal>block-</literal> + followed by a device group name, as listed in + <filename>/proc/devices</filename>. The latter is useful to + whitelist all current and future devices belonging to a + specific device group at once. Examples: + <filename>/dev/sda5</filename> is a path to a device node, + referring to an ATA or SCSI block + device. <literal>char-pts</literal> and + <literal>char-alsa</literal> are specifiers for all pseudo + TTYs and all ALSA sound devices, respectively.</para> </listitem> </varlistentry> diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 707ce470a1..50de02d0ce 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -191,6 +191,82 @@ static int whitelist_device(const char *path, const char *node, const char *acc) return r; } +static int whitelist_major(const char *path, const char *name, char type, const char *acc) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + bool good = false; + int r; + + assert(path); + assert(acc); + assert(type == 'b' || type == 'c'); + + f = fopen("/proc/devices", "re"); + if (!f) { + log_warning("Cannot open /proc/devices to resolve %s (%c): %m", name, type); + return -errno; + } + + FOREACH_LINE(line, f, goto fail) { + char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w; + unsigned maj; + + truncate_nl(line); + + if (type == 'c' && streq(line, "Character devices:")) { + good = true; + continue; + } + + if (type == 'b' && streq(line, "Block devices:")) { + good = true; + continue; + } + + if (isempty(line)) { + good = false; + continue; + } + + if (!good) + continue; + + p = strstrip(line); + + w = strpbrk(p, WHITESPACE); + if (!w) + continue; + *w = 0; + + r = safe_atou(p, &maj); + if (r < 0) + continue; + if (maj <= 0) + continue; + + w++; + w += strspn(w, WHITESPACE); + if (!streq(w, name)) + continue; + + sprintf(buf, + "%c %u:* %s", + type, + maj, + acc); + + r = cg_set_attribute("devices", path, "devices.allow", buf); + if (r < 0) + log_warning("Failed to set devices.allow on %s: %s", path, strerror(-r)); + } + + return 0; + +fail: + log_warning("Failed to read /proc/devices: %m"); + return -errno; +} + void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) { int r; @@ -306,7 +382,15 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha continue; acc[k++] = 0; - whitelist_device(path, a->path, acc); + + if (startswith(a->path, "/dev/")) + whitelist_device(path, a->path, acc); + else if (startswith(a->path, "block-")) + whitelist_major(path, a->path + 6, 'b', acc); + else if (startswith(a->path, "char-")) + whitelist_major(path, a->path + 5, 'c', acc); + else + log_debug("Ignoring device %s while writing cgroup attribute.", a->path); } } } diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 792f37eef5..b8a77254d9 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -442,8 +442,11 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { - if (!path_startswith(path, "/dev")) - return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node"); + if ((!startswith(path, "/dev/") && + !startswith(path, "block-") && + !startswith(path, "char-")) || + strpbrk(path, WHITESPACE)) + return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node"); if (isempty(rwm)) rwm = "rwm"; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e74a790e25..5b1e990921 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2368,9 +2368,10 @@ int config_parse_device_allow( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device node path '%s'. Ignoring.", path); + if (!startswith(path, "/dev/") && + !startswith(path, "block-") && + !startswith(path, "char-")) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -2379,8 +2380,7 @@ int config_parse_device_allow( m = "rwm"; if (!in_charset(m, "rwm")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device rights '%s'. Ignoring.", m); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m); return 0; } |