
From: "Zach, Yoav" <yoav.zach@intel.com>

(Still needs work)

I prepared a patch that solves a couple of problems that interpreters have
when invoked via binfmt_misc.  currently - 

1) such interpreters cannot open non-readable binaries

2) the processes will have their credentials and security attributes
   calculated according to interpreter permissions and not those of the
   original binary

the proposed patch solves these problems by -

1) opening the binary on behalf of the interpreter and passing its fd
   instead of the path as argv[1] to the interpreter

2) calling prepare_binprm with the file struct of the binary and not the
   one of the interpreter

The new functionality is enabled by adding a special flag to the registration
string.  If this flag is not added then old behavior is not changed.

A preliminary version of this patch was sent to the list on 9/1/2003 with the
title "[PATCH]: non-readable binaries - binfmt_misc 2.6.0-test4".  This new
version fixes the concerns that were raised by the patch, except of calling
unshare_files() before allocating a new fd.  this is because this feature did
not enter 2.6 yet.  


---

 fs/binfmt_misc.c        |   95 ++++++++++++++++++++++++++++++++++++++++--------
 fs/exec.c               |    4 +-
 include/linux/binfmts.h |    4 ++
 3 files changed, 88 insertions(+), 15 deletions(-)

diff -puN fs/binfmt_misc.c~non-readable-binaries fs/binfmt_misc.c
--- 25/fs/binfmt_misc.c~non-readable-binaries	2004-01-18 16:14:22.000000000 -0800
+++ 25-akpm/fs/binfmt_misc.c	2004-01-18 16:14:22.000000000 -0800
@@ -38,6 +38,7 @@ static int enabled = 1;
 
 enum {Enabled, Magic};
 #define MISC_FMT_PRESERVE_ARGV0 (1<<31)
+#define MISC_FMT_OPEN_BINARY (1<<30)
 
 typedef struct {
 	struct list_head list;
@@ -101,10 +102,13 @@ static Node *check_file(struct linux_bin
 static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 {
 	Node *fmt;
-	struct file * file;
+	struct file *interp_file;
 	char iname[BINPRM_BUF_SIZE];
 	char *iname_addr = iname;
 	int retval;
+	int fd;
+	char fd_str[32];
+	char *fdsp = fd_str;
 
 	retval = -ENOEXEC;
 	if (!enabled)
@@ -119,15 +123,36 @@ static int load_misc_binary(struct linux
 	if (!fmt)
 		goto _ret;
 
-	allow_write_access(bprm->file);
-	fput(bprm->file);
-	bprm->file = NULL;
+ 	if (fmt->flags & MISC_FMT_OPEN_BINARY) {
+		/* if the binary should be opened on behalf of the
+		 * interpreter than keep it open and assign descriptor
+		 * to it */
+ 		fd = get_unused_fd();
+ 		if (fd < 0) {
+ 			retval = fd;
+ 			goto _ret;
+ 		} else {
+ 			fd_install(fd, bprm->file);
+ 			snprintf(fd_str, sizeof(fd_str), "%d", fd);
+ 		}
+ 	} else {
+ 		allow_write_access(bprm->file);
+ 		fput(bprm->file);
+ 		bprm->file = NULL;
+ 	}
 
 	/* Build args for interpreter */
 	if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
 		remove_arg_zero(bprm);
 	}
-	retval = copy_strings_kernel(1, &bprm->interp, bprm);
+
+ 	if (fmt->flags & MISC_FMT_OPEN_BINARY) {
+		/* make argv[1] be the file descriptor of the binary */
+ 		retval = copy_strings_kernel(1, &fdsp, bprm);
+ 	} else {
+		/* make argv[1] be the path to the binary */
+ 		retval = copy_strings_kernel(1, &bprm->interp, bprm);
+ 	}
 	if (retval < 0) goto _ret; 
 	bprm->argc++;
 	retval = copy_strings_kernel(1, &iname_addr, bprm);
@@ -135,15 +160,39 @@ static int load_misc_binary(struct linux
 	bprm->argc++;
 	bprm->interp = iname;	/* for binfmt_script */
 
-	file = open_exec(iname);
-	retval = PTR_ERR(file);
-	if (IS_ERR(file))
+	interp_file = open_exec(iname);
+	retval = PTR_ERR(interp_file);
+	if (IS_ERR(interp_file))
 		goto _ret;
-	bprm->file = file;
 
-	retval = prepare_binprm(bprm);
+	if (fmt->flags & MISC_FMT_OPEN_BINARY) {
+		/* if the binary is not readable than enforce mm->dumpable=0
+		   regardless of the interpreter's permissions */
+		if (permission(bprm->file->f_dentry->d_inode,MAY_READ,NULL)) {
+			bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+		}
+		/* call prepare_binprm before switching to interpreter's file
+		 * so that all security calculation will be done according to
+		 * binary and not interpreter */
+		retval = prepare_binprm(bprm);
+		if (retval < 0) {
+			goto _ret;
+		}
+		/* switch to interpreter and read it's header */
+		bprm->file = interp_file;
+		memset(bprm->buf,0,BINPRM_BUF_SIZE);
+		retval = kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+	} else {
+		bprm->file = interp_file;
+		retval = prepare_binprm(bprm);
+	}
+
+
 	if (retval >= 0)
 		retval = search_binary_handler(bprm, regs);
+
+	if (retval < 0)
+		bprm->interp_flags &= ~BINPRM_FLAGS_ENFORCE_NONDUMP;
 _ret:
 	return retval;
 }
@@ -190,6 +239,26 @@ static int unquote(char *from)
 	return p - from;
 }
 
+static inline char * check_special_flags (char * sfs, Node * e)
+{
+	char * p = sfs;
+	char SPECIAL_FLAGS[] = "OP";
+
+	/* special flags */
+	while (strchr (SPECIAL_FLAGS, *p)) {
+		switch (*p) {
+			case 'P':
+				p++;
+				e->flags |= MISC_FMT_PRESERVE_ARGV0;
+				break;
+			case 'O':
+				p++;
+				e->flags |= MISC_FMT_OPEN_BINARY;
+				break;
+		}
+	}
+	return p;
+}
 /*
  * This registers a new binary format, it recognises the syntax
  * ':name:type:offset:magic:mask:interpreter:'
@@ -292,10 +361,8 @@ static Node *create_entry(const char *bu
 	if (!e->interpreter[0])
 		goto Einval;
 
-	if (*p == 'P') {
-		p++;
-		e->flags |= MISC_FMT_PRESERVE_ARGV0;
-	}
+
+	p = check_special_flags (p, e);
 
 	if (*p == '\n')
 		p++;
diff -puN fs/exec.c~non-readable-binaries fs/exec.c
--- 25/fs/exec.c~non-readable-binaries	2004-01-18 16:14:22.000000000 -0800
+++ 25-akpm/fs/exec.c	2004-01-18 16:14:22.000000000 -0800
@@ -832,7 +832,8 @@ int flush_old_exec(struct linux_binprm *
 	flush_thread();
 
 	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || 
-	    permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL))
+	    permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) ||
+	    (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP))
 		current->mm->dumpable = 0;
 
 	/* An exec changes our domain. We are no longer part of the thread
@@ -1105,6 +1106,7 @@ int do_execve(char * filename,
 	bprm.file = file;
 	bprm.filename = filename;
 	bprm.interp = filename;
+	bprm.interp_flags = 0;
 	bprm.sh_bang = 0;
 	bprm.loader = 0;
 	bprm.exec = 0;
diff -puN include/linux/binfmts.h~non-readable-binaries include/linux/binfmts.h
--- 25/include/linux/binfmts.h~non-readable-binaries	2004-01-18 16:14:22.000000000 -0800
+++ 25-akpm/include/linux/binfmts.h	2004-01-18 16:14:22.000000000 -0800
@@ -35,9 +35,13 @@ struct linux_binprm{
 	char * interp;		/* Name of the binary really executed. Most
 				   of the time same as filename, but could be
 				   different for binfmt_{misc,script} */
+	unsigned long interp_flags;
 	unsigned long loader, exec;
 };
 
+#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
+#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
+
 /*
  * This structure defines the functions that are used to load the binary formats that
  * linux accepts.

_
