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 <JMax@xxxxxxxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html