chiark / gitweb /
cgroup: Extend DeviceAllow= syntax to whitelist groups of devices, not just particula...
authorLennart Poettering <lennart@poettering.net>
Sat, 22 Feb 2014 01:47:29 +0000 (02:47 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 22 Feb 2014 02:05:34 +0000 (03:05 +0100)
man/systemd.resource-control.xml
src/core/cgroup.c
src/core/dbus-cgroup.c
src/core/load-fragment.c

index fcfe861256ce3ad584285fdf255b905c43dab564..0ee983b1c3d5d213a8879c219e1ecf44ee3b5ef2 100644 (file)
@@ -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>
 
index 707ce470a123ba8a4d7f0a08e85ae93e69b18765..50de02d0ce041a52ade7ca9cc64706c74007a59e 100644 (file)
@@ -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);
                 }
         }
 }
index 792f37eef588e8ac5add4d38283b5fc2dc7b57a5..b8a77254d94d20da932ca57a7143b6c58a5c694b 100644 (file)
@@ -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";
index e74a790e25542cdf155dd3fcb4ed5a8af0849239..5b1e99092115215363f2b4dcc02ba20fab7d2e13 100644 (file)
@@ -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;
         }