[Fedora 09/19] binfmt_elf: Elf executable signature verification

Vivek Goyal vgoyal at redhat.com
Wed Sep 4 21:24:41 UTC 2013


Do elf executable signature verification (if one is present). If signature
is present, it should be valid. Validly signed executables are locked in
memory and a flag cred->proc_signed gets set to signify this process
executable contents are signed.

If file is unsigned, it can execute but it does not have the cred->proc_signed
set.

Signed-off-by: Vivek Goyal <vgoyal at redhat.com>
---
 fs/Kconfig.binfmt    | 13 +++++++++++++
 fs/binfmt_elf.c      | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/cred.h |  2 ++
 kernel/cred.c        |  2 ++
 4 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 370b24c..08ce598 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -23,6 +23,19 @@ config BINFMT_ELF
 	  ld.so (check the file <file:Documentation/Changes> for location and
 	  latest version).
 
+config BINFMT_ELF_SIG
+	bool "ELF binary signature verification"
+	depends on BINFMT_ELF
+	select INTEGRITY
+	select INTEGRITY_SIGNATURE
+	select INTEGRITY_ASYMMETRIC_KEYS
+	select IMA
+	select IMA_APPRAISE
+	select SYSTEM_TRUSTED_KEYRING
+	default n
+	---help---
+	  Check ELF binary signature verfication.
+
 config COMPAT_BINFMT_ELF
 	bool
 	depends on COMPAT && BINFMT_ELF
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 100edcc..55aea2b 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -34,6 +34,8 @@
 #include <linux/utsname.h>
 #include <linux/coredump.h>
 #include <linux/sched.h>
+#include <linux/ima.h>
+#include <keys/system_keyring.h>
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
@@ -584,6 +586,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	int executable_stack = EXSTACK_DEFAULT;
 	unsigned long def_flags = 0;
 	struct pt_regs *regs = current_pt_regs();
+	char *signature = NULL;
+#ifdef CONFIG_BINFMT_ELF_SIG
+	unsigned int siglen = 0;
+	bool mlock_mappings = false;
+#endif
 	struct {
 		struct elfhdr elf_ex;
 		struct elfhdr interp_elf_ex;
@@ -725,6 +732,36 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	/* OK, This is the point of no return */
 	current->mm->def_flags = def_flags;
 
+#ifdef CONFIG_BINFMT_ELF_SIG
+	/* If executable is digitally signed. Lock down in memory */
+	/* Get file signature, if any */
+	retval = ima_file_signature_alloc(bprm->file, &signature);
+
+	/*
+	 * If there is an error getting signature, bail out. Having
+	 * no signature is fine though.
+	 */
+	if (retval < 0 && retval != -ENODATA && retval != -EOPNOTSUPP)
+		goto out_free_dentry;
+
+	if (signature != NULL) {
+		siglen = retval;
+		retval = ima_signature_type(signature);
+		if (retval == EVM_IMA_XATTR_DIGSIG &&
+		    ima_memlock_file(signature, siglen)) {
+			retval = ima_appraise_file_digsig(system_trusted_keyring,
+					bprm->file, signature, siglen);
+			if (retval) {
+				send_sig(SIGKILL, current, 0);
+				goto out_free_dentry;
+			}
+
+			mlock_mappings = true;
+			current->mm->def_flags |= VM_LOCKED;
+			set_bit(MMF_VM_LOCKED, &current->mm->flags);
+		}
+	}
+#endif
 	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
 	   may depend on the personality.  */
 	SET_PERSONALITY(loc->elf_ex);
@@ -895,6 +932,19 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		goto out_free_dentry;
 	}
 
+#ifdef CONFIG_BINFMT_ELF_SIG
+	if (mlock_mappings) {
+		retval = ima_appraise_file_digsig(system_trusted_keyring,
+					bprm->file, signature, siglen);
+		if (retval) {
+			send_sig(SIGKILL, current, 0);
+			goto out_free_dentry;
+		}
+		/* Signature verification successful */
+		bprm->cred->proc_signed = true;
+	}
+#endif
+
 	if (elf_interpreter) {
 		unsigned long interp_map_addr = 0;
 
@@ -988,11 +1038,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	 */
 	ELF_PLAT_INIT(regs, reloc_func_desc);
 #endif
-
 	start_thread(regs, elf_entry, bprm->p);
 	retval = 0;
 out:
 	kfree(loc);
+	kfree(signature);
 out_ret:
 	return retval;
 
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 04421e8..1f5f418 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -136,6 +136,8 @@ struct cred {
 	struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
 	struct group_info *group_info;	/* supplementary groups for euid/fsgid */
 	struct rcu_head	rcu;		/* RCU deletion hook */
+	bool	proc_signed;		/* Executable signature have been
+					 * verified post load */
 };
 
 extern void __put_cred(struct cred *);
diff --git a/kernel/cred.c b/kernel/cred.c
index e0573a4..589f1fa 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -299,6 +299,8 @@ struct cred *prepare_exec_creds(void)
 	new->process_keyring = NULL;
 #endif
 
+	/* proc_signed status will be evaluated again from executable file */
+	new->proc_signed = false;
 	return new;
 }
 
-- 
1.8.3.1



More information about the kernel mailing list