All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matthew Garrett <mjg59@google.com>
To: linux-integrity@vger.kernel.org
Cc: zohar@linux.vnet.ibm.com, james.l.morris@oracle.com,
	keescook@chromium.org, oleg@redhat.com,
	Matthew Garrett <mjg59@google.com>
Subject: [PATCH V3 2/2] Add a bprm_interpreted security hook and call into IMA from it
Date: Wed, 11 Oct 2017 12:12:18 -0700	[thread overview]
Message-ID: <20171011191218.5274-2-mjg59@google.com> (raw)
In-Reply-To: <20171011191218.5274-1-mjg59@google.com>

IMA has support for validating files during execution using the
bprm_check functionality. However, this is called before new credentials
have been applied to the task. The previous patch in this series added
an additional IMA check in the bprm_committed_creds hook - however, if a
file is handled by binfmt_misc or binfmt_script (or, in an extremely
unlikely corner case, binfmt_em86), only the interpreter will be
appraised since bprm_committed_creds is never called for the initial
executable.

This patch adds an additional bprm_interpreted hook and calls it from
the end of the relevant binfmt implementations. It is effectively
identical to the bprm_committed_creds hook, except that bprm->file
points to the initial file rather than to the interpreter.

Signed-off-by: Matthew Garrett <mjg59@google.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Oleg Nesterov <oleg@redhat.com>
---
 fs/binfmt_em86.c          | 31 ++++++++++++++++++++++---------
 fs/binfmt_misc.c          | 11 +++++++----
 fs/binfmt_script.c        | 45 +++++++++++++++++++++++++++++++--------------
 include/linux/lsm_hooks.h |  7 +++++++
 include/linux/security.h  |  1 +
 security/security.c       | 11 +++++++++++
 6 files changed, 79 insertions(+), 27 deletions(-)

diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
index dd2d3f0cd55d..e954ec123d27 100644
--- a/fs/binfmt_em86.c
+++ b/fs/binfmt_em86.c
@@ -26,7 +26,7 @@ static int load_em86(struct linux_binprm *bprm)
 {
 	const char *i_name, *i_arg;
 	char *interp;
-	struct file * file;
+	struct file *file, *old;
 	int retval;
 	struct elfhdr	elf_ex;
 
@@ -47,8 +47,7 @@ static int load_em86(struct linux_binprm *bprm)
 	if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
 		return -ENOENT;
 
-	allow_write_access(bprm->file);
-	fput(bprm->file);
+	old = bprm->file;
 	bprm->file = NULL;
 
 	/* Unlike in the script case, we don't have to do any hairy
@@ -72,11 +71,13 @@ static int load_em86(struct linux_binprm *bprm)
 	bprm->argc++;
 	if (i_arg) {
 		retval = copy_strings_kernel(1, &i_arg, bprm);
-		if (retval < 0) return retval; 
+		if (retval < 0)
+			goto ret;
 		bprm->argc++;
 	}
 	retval = copy_strings_kernel(1, &i_name, bprm);
-	if (retval < 0)	return retval;
+	if (retval < 0)
+		goto ret;
 	bprm->argc++;
 
 	/*
@@ -85,16 +86,28 @@ static int load_em86(struct linux_binprm *bprm)
 	 * space, and we don't need to copy it.
 	 */
 	file = open_exec(interp);
-	if (IS_ERR(file))
-		return PTR_ERR(file);
+	if (IS_ERR(file)) {
+		retval = PTR_ERR(file);
+		goto ret;
+	}
 
 	bprm->file = file;
 
 	retval = prepare_binprm(bprm);
 	if (retval < 0)
-		return retval;
+		goto ret;
 
-	return search_binary_handler(bprm);
+	retval = search_binary_handler(bprm);
+	if (retval < 0)
+		goto ret;
+
+	bprm->file = old;
+	retval = security_bprm_interpreted(bprm);
+	bprm->file = file;
+ret:
+	allow_write_access(old);
+	fput(old);
+	return retval;
 }
 
 static struct linux_binfmt em86_format = {
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 2a46762def31..81e6e02348f9 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -130,7 +130,7 @@ static Node *check_file(struct linux_binprm *bprm)
 static int load_misc_binary(struct linux_binprm *bprm)
 {
 	Node *fmt;
-	struct file *interp_file = NULL;
+	struct file *interp_file = NULL, *old = bprm->file;
 	int retval;
 	int fd_binary = -1;
 
@@ -175,7 +175,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
 		   regardless of the interpreter's permissions */
 		would_dump(bprm, bprm->file);
 
-		allow_write_access(bprm->file);
 		bprm->file = NULL;
 
 		/* mark the bprm that fd should be passed to interp */
@@ -183,8 +182,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
 		bprm->interp_data = fd_binary;
 
 	} else {
-		allow_write_access(bprm->file);
-		fput(bprm->file);
 		bprm->file = NULL;
 	}
 	/* make argv[1] be the path to the binary */
@@ -236,7 +233,13 @@ static int load_misc_binary(struct linux_binprm *bprm)
 	if (retval < 0)
 		goto error;
 
+	bprm->file = old;
+	retval = security_bprm_interpreted(bprm);
+	bprm->file = interp_file;
 ret:
+	allow_write_access(old);
+	if (fd_binary < 0)
+		fput(old);
 	dput(fmt->dentry);
 	return retval;
 error:
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 7cde3f46ad26..f2bd2aa15702 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -13,12 +13,13 @@
 #include <linux/file.h>
 #include <linux/err.h>
 #include <linux/fs.h>
+#include <linux/security.h>
 
 static int load_script(struct linux_binprm *bprm)
 {
 	const char *i_arg, *i_name;
 	char *cp;
-	struct file *file;
+	struct file *file, *old;
 	int retval;
 
 	if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
@@ -38,8 +39,7 @@ static int load_script(struct linux_binprm *bprm)
 	 * Sorta complicated, but hopefully it will work.  -TYT
 	 */
 
-	allow_write_access(bprm->file);
-	fput(bprm->file);
+	old = bprm->file;
 	bprm->file = NULL;
 
 	bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
@@ -54,8 +54,11 @@ static int load_script(struct linux_binprm *bprm)
 			break;
 	}
 	for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
-	if (*cp == '\0')
-		return -ENOEXEC; /* No interpreter name found */
+	if (*cp == '\0') {
+		retval = -ENOEXEC; /* No interpreter name found */
+		goto ret;
+	}
+
 	i_name = cp;
 	i_arg = NULL;
 	for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
@@ -76,37 +79,51 @@ static int load_script(struct linux_binprm *bprm)
 	 */
 	retval = remove_arg_zero(bprm);
 	if (retval)
-		return retval;
+		goto ret;
 	retval = copy_strings_kernel(1, &bprm->interp, bprm);
 	if (retval < 0)
-		return retval;
+		goto ret;
 	bprm->argc++;
 	if (i_arg) {
 		retval = copy_strings_kernel(1, &i_arg, bprm);
 		if (retval < 0)
-			return retval;
+			goto ret;
 		bprm->argc++;
 	}
 	retval = copy_strings_kernel(1, &i_name, bprm);
 	if (retval)
-		return retval;
+		goto ret;
 	bprm->argc++;
 	retval = bprm_change_interp(i_name, bprm);
 	if (retval < 0)
-		return retval;
+		goto ret;
 
 	/*
 	 * OK, now restart the process with the interpreter's dentry.
 	 */
 	file = open_exec(i_name);
-	if (IS_ERR(file))
-		return PTR_ERR(file);
+	if (IS_ERR(file)) {
+		retval = PTR_ERR(file);
+		goto ret;
+	}
 
 	bprm->file = file;
 	retval = prepare_binprm(bprm);
 	if (retval < 0)
-		return retval;
-	return search_binary_handler(bprm);
+		goto ret;
+	retval = search_binary_handler(bprm);
+	if (retval)
+		goto ret;
+	/*
+	 * Allow for validation of the script as well as the interpreter
+	 */
+	bprm->file = old;
+	retval = security_bprm_interpreted(bprm);
+	bprm->file = file;
+ret:
+	allow_write_access(old);
+	fput(old);
+	return retval;
 }
 
 static struct linux_binfmt script_format = {
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index c9258124e417..fd23b4098098 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -75,6 +75,11 @@
  *	linux_binprm structure.  This hook is a good place to perform state
  *	changes on the process such as clearing out non-inheritable signal
  *	state.  This is called immediately after commit_creds().
+ * @bprm_interpreted:
+ *	Validate the credentials of an executable that was interpreted via
+ *	binfmt_misc or binfmt_script. This occurs after the new credentials
+ *	have been committed to @current->cred, but @bprm->file points to the
+ *	original file rather than the interpreter that was used to execute it.
  *
  * Security hooks for filesystem operations.
  *
@@ -1383,6 +1388,7 @@ union security_list_options {
 	int (*bprm_check_security)(struct linux_binprm *bprm);
 	void (*bprm_committing_creds)(struct linux_binprm *bprm);
 	void (*bprm_committed_creds)(struct linux_binprm *bprm);
+	int (*bprm_interpreted)(struct linux_binprm *bprm);
 
 	int (*sb_alloc_security)(struct super_block *sb);
 	void (*sb_free_security)(struct super_block *sb);
@@ -1703,6 +1709,7 @@ struct security_hook_heads {
 	struct list_head bprm_check_security;
 	struct list_head bprm_committing_creds;
 	struct list_head bprm_committed_creds;
+	struct list_head bprm_interpreted;
 	struct list_head sb_alloc_security;
 	struct list_head sb_free_security;
 	struct list_head sb_copy_data;
diff --git a/include/linux/security.h b/include/linux/security.h
index ad970a4f19f6..e48c38379c64 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -233,6 +233,7 @@ int security_bprm_set_creds(struct linux_binprm *bprm);
 int security_bprm_check(struct linux_binprm *bprm);
 void security_bprm_committing_creds(struct linux_binprm *bprm);
 int security_bprm_committed_creds(struct linux_binprm *bprm);
+int security_bprm_interpreted(struct linux_binprm *bprm);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
diff --git a/security/security.c b/security/security.c
index c2c5017e3477..6b9cb108ff61 100644
--- a/security/security.c
+++ b/security/security.c
@@ -352,6 +352,17 @@ int security_bprm_committed_creds(struct linux_binprm *bprm)
 	return ima_creds_check(bprm);
 }
 
+int security_bprm_interpreted(struct linux_binprm *bprm)
+{
+	int ret;
+
+	ret = call_int_hook(bprm_interpreted, 0, bprm);
+	if (ret)
+		return ret;
+
+	return ima_creds_check(bprm);
+}
+
 int security_sb_alloc(struct super_block *sb)
 {
 	return call_int_hook(sb_alloc_security, 0, sb);
-- 
2.15.0.rc0.271.g36b669edcc-goog

  reply	other threads:[~2017-10-11 19:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-11 19:12 [PATCH V3 1/2] IMA: Add support for IMA validation after credentials are committed Matthew Garrett
2017-10-11 19:12 ` Matthew Garrett [this message]
2017-10-15 14:08   ` [PATCH V3 2/2] Add a bprm_interpreted security hook and call into IMA from it Mimi Zohar
2017-10-16 18:13     ` Matthew Garrett

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171011191218.5274-2-mjg59@google.com \
    --to=mjg59@google.com \
    --cc=james.l.morris@oracle.com \
    --cc=keescook@chromium.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=oleg@redhat.com \
    --cc=zohar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.