public inbox for linux-integrity@vger.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox