This patch allows binfmt_misc to select the interpeter for arbitrary
binaries by comparing a specified registered keyword with the value
of a specified binary's extended attribute (user.binfmt.interp),
and then launching the program with the registered interpreter.

This is useful when wanting to launch a collection of binaries under
the same interpreter, even when they do not necessarily share a common
extension or magic bits, or when their magic conflics with the operation
of binfmt_elf. Some examples of its use would be to launch some executables
of various different architectures in a directory, or for running some
native binaries under a sandbox (like firejail) automatically during their
launch.

Signed-off-by: Josh Max <j...@mail.greenriver.edu>
---
 fs/binfmt_misc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 6103a63..86d93c7 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -24,6 +24,7 @@
 #include <linux/mount.h>
 #include <linux/syscalls.h>
 #include <linux/fs.h>
+#include <linux/xattr.h>
 #include <linux/uaccess.h>
 
 #include "internal.h"
@@ -41,12 +42,17 @@ enum {
 static LIST_HEAD(entries);
 static int enabled = 1;
 
-enum {Enabled, Magic};
+enum {Enabled, Magic, Keyword};
 #define MISC_FMT_PRESERVE_ARGV0 (1 << 31)
 #define MISC_FMT_OPEN_BINARY (1 << 30)
 #define MISC_FMT_CREDENTIALS (1 << 29)
 #define MISC_FMT_OPEN_FILE (1 << 28)
 
+#define XATTR_BINFMT_PREFIX XATTR_USER_PREFIX "binfmt."
+#define XATTR_BINFMT_INTERPRETER_SUFFIX "interp"
+#define XATTR_NAME_BINFMT (XATTR_BINFMT_PREFIX XATTR_BINFMT_INTERPRETER_SUFFIX)
+#define XATTR_VALUE_MAX_LENGTH 128
+
 typedef struct {
        struct list_head list;
        unsigned long flags;            /* type, status, etc. */
@@ -61,6 +67,7 @@ typedef struct {
 } Node;
 
 static DEFINE_RWLOCK(entries_lock);
+static int get_xattr_interp_keyword(struct file *file, char *buf, size_t 
count);
 static struct file_system_type bm_fs_type;
 static struct vfsmount *bm_mnt;
 static int entry_count;
@@ -87,6 +94,8 @@ static int entry_count;
  */
 static Node *check_file(struct linux_binprm *bprm)
 {
+       char k[XATTR_VALUE_MAX_LENGTH];
+       int k_len = get_xattr_interp_keyword(bprm->file, k, sizeof(k)-1);
        char *p = strrchr(bprm->interp, '.');
        struct list_head *l;
 
@@ -100,6 +109,16 @@ static Node *check_file(struct linux_binprm *bprm)
                if (!test_bit(Enabled, &e->flags))
                        continue;
 
+               /* Do matching based on xattrs keyword */
+               if (test_bit(Keyword, &e->flags)) {
+                       if (k_len <= 0)
+                               continue;
+                       k[k_len] = 0;
+                       if (!strcmp(e->magic, k))
+                               return e;
+                       continue;
+               }
+
                /* Do matching based on extension if applicable. */
                if (!test_bit(Magic, &e->flags)) {
                        if (p && !strcmp(e->magic, p + 1))
@@ -309,6 +328,20 @@ static char *check_special_flags(char *sfs, Node *e)
 }
 
 /*
+ * Check to see if the filesystem supports xattrs
+ * and grab the value so it can be checked against
+ * the list of keywords in binfmt_misc for a match
+ */
+static int get_xattr_interp_keyword(struct file *file, char *buf, size_t count)
+{
+
+       if (unlikely(!file->f_inode->i_op->getxattr))
+               return -ENOENT;
+       return file->f_inode->i_op->getxattr(file->f_path.dentry, file->f_inode,
+               XATTR_NAME_BINFMT, buf, count);
+}
+
+/*
  * This registers a new binary format, it recognises the syntax
  * ':name:type:offset:magic:mask:interpreter:flags'
  * where the ':' is the IFS, that can be chosen with the first char
@@ -366,6 +399,10 @@ static Node *create_entry(const char __user *buffer, 
size_t count)
                pr_debug("register: type: E (extension)\n");
                e->flags = 1 << Enabled;
                break;
+       case 'K':
+               pr_debug("register: type: K (xattrs keyword)\n");
+               e->flags = (1 << Enabled) | (1 << Keyword);
+               break;
        case 'M':
                pr_debug("register: type: M (magic)\n");
                e->flags = (1 << Enabled) | (1 << Magic);
@@ -453,7 +490,7 @@ static Node *create_entry(const char __user *buffer, size_t 
count)
                        }
                }
        } else {
-               /* Handle the 'E' (extension) format. */
+               /* Handle the 'E' (extension) and 'K' (keyword) format. */
 
                /* Skip the 'offset' field. */
                p = strchr(p, del);
@@ -469,7 +506,10 @@ static Node *create_entry(const char __user *buffer, 
size_t count)
                *p++ = '\0';
                if (!e->magic[0] || strchr(e->magic, '/'))
                        goto einval;
-               pr_debug("register: extension: {%s}\n", e->magic);
+               if (test_bit(Keyword, &e->flags))
+                       pr_debug("register: keyword: {%s}\n", e->magic);
+               else
+                       pr_debug("register: extension: {%s}\n", e->magic);
 
                /* Skip the 'mask' field. */
                p = strchr(p, del);
@@ -563,7 +603,10 @@ static void entry_status(Node *e, char *page)
        *dp++ = '\n';
 
        if (!test_bit(Magic, &e->flags)) {
-               sprintf(dp, "extension .%s\n", e->magic);
+               if (test_bit(Keyword, &e->flags))
+                       sprintf(dp, "keyword %s\n", e->magic);
+               else
+                       sprintf(dp, "extension .%s\n", e->magic);
        } else {
                dp += sprintf(dp, "offset %i\nmagic ", e->offset);
                dp = bin2hex(dp, e->magic, e->size);
-- 
2.8.1

Reply via email to