file: extend exec acl checks to commands with arguments
authorJo-Philipp Wich <jo@mein.io>
Tue, 10 Dec 2019 15:49:55 +0000 (16:49 +0100)
committerJo-Philipp Wich <jo@mein.io>
Wed, 11 Dec 2019 08:13:05 +0000 (09:13 +0100)
When the initial exec permission check on the executable path fails,
concatenate the command line with spaces and use the resulting string
as lookup path for a second exec permission check.

This allows for exec acls similar to this example:

    "file": {
        "/usr/bin/program --flag --option=1 arg *": [ "exec" ]
    }

The example above would allow executing `/usr/bin/program` with the
arguments `--flag`, `--option=1` and `arg` in exactly this order,
followed by any number of optional arguments as denoted by the
asterisk.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
file.c

diff --git a/file.c b/file.c
index 4ed2e5cae2a5bb277a55dd371c17535caf76e6f1..bd7af5695e4554d8cd02320e6a3cd5644754b3d9 100644 (file)
--- a/file.c
+++ b/file.c
@@ -43,6 +43,9 @@
 /* limit of regular files and command output data */
 #define RPC_FILE_MAX_SIZE              (4096 * 64)
 
+/* limit of command line length for exec acl checks */
+#define RPC_CMDLINE_MAX_SIZE   (1024)
+
 #define ustream_for_each_read_buffer(stream, ptr, len) \
        for (ptr = ustream_get_read_buf(stream, &len);     \
             ptr != NULL && len > 0;                       \
@@ -71,6 +74,7 @@ struct rpc_file_exec_context {
 
 static struct blob_buf buf;
 static char *canonpath;
+static char cmdstr[RPC_CMDLINE_MAX_SIZE];
 
 enum {
        RPC_F_R_PATH,
@@ -801,7 +805,7 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid,
        struct blob_attr *cur;
 
        uint8_t arglen;
-       char *executable, **args, **tmp;
+       char *executable, **args, **tmp, *p;
 
        struct rpc_file_exec_context *c;
 
@@ -816,7 +820,29 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid,
                return UBUS_STATUS_UNKNOWN_ERROR;
 
        if (!rpc_file_access(sid, executable, "exec"))
-               return UBUS_STATUS_PERMISSION_DENIED;
+       {
+               if (arg == NULL || strlen(executable) >= sizeof(cmdstr))
+                       return UBUS_STATUS_PERMISSION_DENIED;
+
+               arglen = 0;
+               p = cmdstr + sprintf(cmdstr, "%s", executable);
+
+               blobmsg_for_each_attr(cur, arg, rem)
+               {
+                       if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
+                               continue;
+
+                       if (arglen == 255 ||
+                           p + blobmsg_data_len(cur) >= cmdstr + sizeof(cmdstr))
+                               break;
+
+                       p += sprintf(p, " %s", blobmsg_get_string(cur));
+                       arglen++;
+               }
+
+               if (!rpc_file_access(sid, cmdstr, "exec"))
+                       return UBUS_STATUS_PERMISSION_DENIED;
+       }
 
        c = malloc(sizeof(*c));