]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/execute: add the magic character '!' to allow privileged execution (#3493)
authorAlessandro Puccetti <alessandro@kinvolk.io>
Fri, 10 Jun 2016 16:19:54 +0000 (18:19 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 10 Jun 2016 16:19:54 +0000 (18:19 +0200)
This patch implements the new magic character '!'. By putting '!' in front
of a command, systemd executes it with full privileges ignoring paramters
such as User, Group, SupplementaryGroups, CapabilityBoundingSet,
AmbientCapabilities, SecureBits, SystemCallFilter, SELinuxContext,
AppArmorProfile, SmackProcessLabel, and RestrictAddressFamilies.

Fixes partially https://github.com/systemd/systemd/issues/3414
Related to https://github.com/coreos/rkt/issues/2482

Testing:
1. Create a user 'bob'
2. Create the unit file /etc/systemd/system/exec-perm.service
   (You can use the example below)
3. sudo systemctl start ext-perm.service
4. Verify that the commands starting with '!' were not executed as bob,
   4.1 Looking to the output of ls -l /tmp/exec-perm
   4.2 Each file contains the result of the id command.

`````````````````````````````````````````````````````````````````
[Unit]
Description=ext-perm

[Service]
Type=oneshot
TimeoutStartSec=0
User=bob
ExecStartPre=!/usr/bin/sh -c "/usr/bin/rm /tmp/exec-perm*" ;
    /usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-start-pre"
ExecStart=/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-start" ;
    !/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-star-2"
ExecStartPost=/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-start-post"
ExecReload=/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-reload"
ExecStop=!/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-stop"
ExecStopPost=/usr/bin/sh -c "/usr/bin/id > /tmp/exec-perm-stop-post"

[Install]
WantedBy=multi-user.target]
`````````````````````````````````````````````````````````````````

man/systemd.exec.xml
man/systemd.service.xml
src/core/execute.c
src/core/execute.h
src/core/load-fragment.c

index 4a3dd14c399a526659830bf4b3c0c52b5ff19702..1c3256a662bb4f28515d3058809aac311f60cdbd 100644 (file)
         <listitem><para>Sets the Unix user or group that the processes
         are executed as, respectively. Takes a single user or group
         name or ID as argument. If no group is set, the default group
-        of the user is chosen.</para></listitem>
+        of the user is chosen. These do not affect commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         this one will have no effect. In any way, this option does not
         override, but extends the list of supplementary groups
         configured in the system group database for the
-        user.</para></listitem>
+        user. This does not affect commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         process are enforced. This option may appear more than once, in which case the bounding sets are merged. If the
         empty string is assigned to this option, the bounding set is reset to the empty capability set, and all prior
         settings have no effect.  If set to <literal>~</literal> (without any further argument), the bounding set is
-        reset to the full set of available capabilities, also undoing any previous settings.</para></listitem>
+        reset to the full set of available capabilities, also undoing any previous settings. This does not affect
+        commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         as a non-privileged user but still want to give it some capabilities.
         Note that in this case option <constant>keep-caps</constant> is
         automatically added to <varname>SecureBits=</varname> to retain the
-        capabilities over the user change.</para></listitem>
+        capabilities over the user change. <varname>AmbientCapabilities=</varname> does not affect
+        commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <option>noroot-locked</option>.
         This option may appear more than once, in which case the secure
         bits are ORed. If the empty string is assigned to this option,
-        the bits are reset to 0. See
-        <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        the bits are reset to 0. This does not affect commands prefixed with <literal>!</literal>.
+        See <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
         for details.</para></listitem>
       </varlistentry>
 
         domain transition. However, the policy still needs to
         authorize the transition. This directive is ignored if SELinux
         is disabled. If prefixed by <literal>-</literal>, all errors
-        will be ignored. See
-        <citerefentry project='die-net'><refentrytitle>setexeccon</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        will be ignored. This does not affect commands prefixed with <literal>!</literal>.
+        See <citerefentry project='die-net'><refentrytitle>setexeccon</refentrytitle><manvolnum>3</manvolnum></citerefentry>
         for details.</para></listitem>
       </varlistentry>
 
         Profiles must already be loaded in the kernel, or the unit
         will fail. This result in a non operation if AppArmor is not
         enabled. If prefixed by <literal>-</literal>, all errors will
-        be ignored. </para></listitem>
+        be ignored. This does not affect commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <para>The value may be prefixed by <literal>-</literal>, in
         which case all errors will be ignored. An empty value may be
-        specified to unset previous assignments.</para>
+        specified to unset previous assignments. This does not affect
+        commands prefixed with <literal>!</literal>.</para>
         </listitem>
       </varlistentry>
 
         listed explicitly. This option may be specified more than once,
         in which case the filter masks are merged. If the empty string
         is assigned, the filter is reset, all prior assignments will
-        have no effect.</para>
+        have no effect. This does not affect commands prefixed with <literal>!</literal>.</para>
 
         <para>If you specify both types of this option (i.e.
         whitelisting and blacklisting), the first encountered will
         family should be included in the configured whitelist as it is
         frequently used for local communication, including for
         <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        logging.</para></listitem>
+        logging. This does not affect commands prefixed with <literal>!</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 6641dfed4f20b4613a87b002aa9b72d77f58fff3..6e969abc258204a90959fe3d951c643d4198fd47 100644 (file)
         If the absolute filename is prefixed with
         <literal>-</literal>, an exit code of the command normally
         considered a failure (i.e. non-zero exit status or abnormal
-        exit due to signal) is ignored and considered success. If both
-        <literal>-</literal> and <literal>@</literal> are used, they
-        can appear in either order.</para>
+        exit due to signal) is ignored and considered success.
+        If the absolute path is prefixed with <literal>!</literal> then
+        it is executed with full privileges. <literal>-</literal>, <literal>@</literal>, and <literal>!</literal>
+        may be used together and they can appear in any order.</para>
 
         <para>If more than one command is specified, the commands are
         invoked sequentially in the order they appear in the unit
index e718c43df9e1a363886ddc0212c048b23ed39a67..802f14d5759f14fc6abdde916e8038d438171652 100644 (file)
@@ -1717,7 +1717,7 @@ static int exec_child(
 
         umask(context->umask);
 
-        if (params->apply_permissions) {
+        if (params->apply_permissions && !command->privileged) {
                 r = enforce_groups(context, username, gid);
                 if (r < 0) {
                         *exit_status = EXIT_GROUP;
@@ -1842,7 +1842,7 @@ static int exec_child(
         }
 
 #ifdef HAVE_SELINUX
-        if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) {
+        if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) {
                 r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
                 if (r < 0) {
                         *exit_status = EXIT_SELINUX_CONTEXT;
@@ -1867,7 +1867,7 @@ static int exec_child(
                 return r;
         }
 
-        if (params->apply_permissions) {
+        if (params->apply_permissions && !command->privileged) {
 
                 bool use_address_families = context->address_families_whitelist ||
                         !set_isempty(context->address_families);
index 464869d22670d7558310d0e0794a1aaf3ffab150..cd1f7b36f647ec4746ec3d997d85ed80b46a2164 100644 (file)
@@ -81,7 +81,8 @@ struct ExecCommand {
         char **argv;
         ExecStatus exec_status;
         LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
-        bool ignore;
+        bool ignore:1;
+        bool privileged:1;
 };
 
 struct ExecRuntime {
index 2d8f6296c88f31e480b9a132c5f6b6dde0f04463..17c72aed880dc1a2e92b44863923884cb01a5140 100644 (file)
@@ -596,7 +596,7 @@ int config_parse_exec(
         p = rvalue;
         do {
                 _cleanup_free_ char *path = NULL, *firstword = NULL;
-                bool separate_argv0 = false, ignore = false;
+                bool separate_argv0 = false, ignore = false, privileged = false;
                 _cleanup_free_ ExecCommand *nce = NULL;
                 _cleanup_strv_free_ char **n = NULL;
                 size_t nlen = 0, nbufsize = 0;
@@ -610,14 +610,18 @@ int config_parse_exec(
                         return 0;
 
                 f = firstword;
-                for (i = 0; i < 2; i++) {
-                        /* We accept an absolute path as first argument, or
-                         * alternatively an absolute prefixed with @ to allow
-                         * overriding of argv[0]. */
+                for (i = 0; i < 3; i++) {
+                        /* We accept an absolute path as first argument.
+                         * If it's prefixed with - and the path doesn't exist,
+                         * we ignore it instead of erroring out;
+                         * if it's prefixed with @, we allow overriding of argv[0];
+                         * and if it's prefixed with !, it will be run with full privileges */
                         if (*f == '-' && !ignore)
                                 ignore = true;
                         else if (*f == '@' && !separate_argv0)
                                 separate_argv0 = true;
+                        else if (*f == '!' && !privileged)
+                                privileged = true;
                         else
                                 break;
                         f++;
@@ -715,6 +719,7 @@ int config_parse_exec(
                 nce->argv = n;
                 nce->path = path;
                 nce->ignore = ignore;
+                nce->privileged = privileged;
 
                 exec_command_append_list(e, nce);