[PATCH] sns: check related executable memory of binaries [3/4]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Johannes Schlumberger <[email protected]>

Checks on mmap and mprotect (i.e. libraries) wether they are signed and adjusts
the processe's signed flag accordingly.

If a process looses its signed state it gets, in our current design, killed, for
it is no longer trustworthy. A process also looses its signed flag if it mprotects
any memory as executable.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
 include/linux/mm.h  |    3 ++
 include/linux/sns.h |   17 +++++++++++
 kernel/fork.c       |    7 ++++
 mm/mmap.c           |   79 +++++++++++++++++++++++++++++++++++++++++++++++----
 mm/mprotect.c       |   30 +++++++++++++++++++
 security/Kconfig    |   36 +++++++++++++++++++++++
 security/sns.c      |   10 ++++++
 7 files changed, 176 insertions(+), 6 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index e4183c6..903bc45 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -112,6 +112,9 @@ struct vm_area_struct {
 #ifdef CONFIG_NUMA
 	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
 #endif
+#ifdef CONFIG_SNS_SIGNED
+	int sns_valid_sig;
+#endif
 };
 
 extern struct kmem_cache *vm_area_cachep;
diff --git a/include/linux/sns.h b/include/linux/sns.h
index ad15e4b..eefb6e7 100644
--- a/include/linux/sns.h
+++ b/include/linux/sns.h
@@ -1,3 +1,20 @@
 #ifdef CONFIG_SNS_SIGNED
 int sns_signature_valid(struct file *);
+int sns_signature_becomes_invalid(void);
+
+/*
+ * The following is unfortunately necessary, there does not seem to be a
+ * common define to find out wether some ominous DSO which somebody
+ * likes to mmap or mprotect is in fact trustworthy kernel code.
+ */
+#ifdef CONFIG_X86
+#define sns_is_gate_vdso(addr, len) (addr==0xffffe000 && len == PAGE_SIZE)
+#else
+#ifdef CONFIG_IA64
+#define sns_is_gate_vdso(addr, len) (addr==0xffffe000UL && len == PAGE_SIZE)
+#else
+#define sns_is_gate_vdso(addr, len) 0
+#endif
+#endif
+
 #endif
diff --git a/kernel/fork.c b/kernel/fork.c
index c12cf61..b1afa57 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -260,6 +260,9 @@ static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
 		file = tmp->vm_file;
 		if (file) {
 			struct inode *inode = file->f_path.dentry->d_inode;
+#ifdef CONFIG_SNS_SIGNED
+			tmp->sns_valid_sig = mpnt->sns_valid_sig;
+#endif
 			get_file(file);
 			if (tmp->vm_flags & VM_DENYWRITE)
 				atomic_dec(&inode->i_writecount);
@@ -271,6 +274,10 @@ static inline int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
 			vma_prio_tree_add(tmp, mpnt);
 			flush_dcache_mmap_unlock(file->f_mapping);
 			spin_unlock(&file->f_mapping->i_mmap_lock);
+#ifdef CONFIG_SNS_SIGNED
+		} else {
+			tmp->sns_valid_sig = 0;
+#endif
 		}
 
 		/*
diff --git a/mm/mmap.c b/mm/mmap.c
index 68b9ad2..1f4bdf0 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -25,6 +25,9 @@
 #include <linux/mount.h>
 #include <linux/mempolicy.h>
 #include <linux/rmap.h>
+#ifdef CONFIG_SNS_SIGNED
+#include <linux/sns.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
@@ -1101,10 +1104,33 @@ munmap_back:
 		error = file->f_op->mmap(file, vma);
 		if (error)
 			goto unmap_and_free_vma;
-	} else if (vm_flags & VM_SHARED) {
-		error = shmem_zero_setup(vma);
-		if (error)
-			goto free_vma;
+#ifdef CONFIG_SNS_SIGNED
+		if (current->sns_valid_sig && (vm_flags & VM_EXEC)) {
+			if (vm_flags & VM_WRITE){
+				vma->sns_valid_sig = 0;
+				current->sns_valid_sig = 0;
+				sns_signature_becomes_invalid();
+			} else {
+				vma->sns_valid_sig = sns_signature_valid(file);
+				current->sns_valid_sig = vma->sns_valid_sig;
+				if(!current->sns_valid_sig)
+					sns_signature_becomes_invalid();
+			}
+		}
+#endif
+	} else {
+#ifdef CONFIG_SNS_SIGNED
+		/* JIT could have written some evil code here, which we are unable to verify */
+		if (prot & PROT_EXEC && current->sns_valid_sig) {
+			if ((vma->sns_valid_sig = (current->sns_valid_sig = (sns_is_gate_vdso(addr, len)))))
+				sns_signature_becomes_invalid();
+		}
+#endif
+		if (vm_flags & VM_SHARED) {
+			error = shmem_zero_setup(vma);
+			if (error)
+				goto free_vma;
+		}
 	}
 
 	/* We set VM_ACCOUNT in a shared mapping's vm_flags, to inform
@@ -1946,8 +1972,49 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
 	vma->vm_end = addr + len;
 	vma->vm_pgoff = pgoff;
 	vma->vm_flags = flags;
-	vma->vm_page_prot = protection_map[flags &
-				(VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
+
+#ifdef CONFIG_SNS_SIGNED
+	/*
+	 * A signed process could put executable code into an area
+	 * it got from brk(). We can either disable the exec-bit on
+	 * that area, which might break some programs, or we can
+	 * allow the exec bit but remove the signed status, thereby
+	 * unsigning any program that does some malloc()...
+	 *
+	 * Choose your poison.
+	 */
+
+	if (current->sns_valid_sig){
+#ifdef CONFIG_SNS_BRK_ALLOW_EXEC
+		vma->vm_page_prot = protection_map[flags &
+			(VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
+
+
+#ifdef CONFIG_SNS_BRK_UNSIGN
+		current->sns_valid_sig = 0;
+		sns_signature_becomes_invalid();
+		vma->sns_valid_sig = 0;
+#endif /* CONFIG_SNS_BRK_UNSIGN */
+
+
+
+#else /* not CONFIG_SNS_BRK_ALLOW_EXEC */
+		vma->vm_page_prot = protection_map[flags &
+			(VM_READ|VM_WRITE|VM_SHARED)];
+#endif /* CONFIG_SNS_BRK_ALLOW_EXEC */
+	} else {
+		vma->vm_page_prot = protection_map[flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
+	}
+
+
+#else /* not CONFIG_SNS_SIGNED */
+	vma->vm_page_prot = protection_map[flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)];
+
+
+
+
+#endif /* CONFIG_SNS_SIGNED */
+
 	vma_link(mm, vma, prev, rb_link, rb_parent);
 out:
 	mm->total_vm += len >> PAGE_SHIFT;
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 3b8f3c0..db17887 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -26,6 +26,10 @@
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 
+#ifdef CONFIG_SNS_SIGNED
+#include <linux/sns.h>
+#endif
+
 static void change_pte_range(struct mm_struct *mm, pmd_t *pmd,
 		unsigned long addr, unsigned long end, pgprot_t newprot,
 		int dirty_accountable)
@@ -289,6 +293,32 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 		if (error)
 			goto out;
 
+#ifdef CONFIG_SNS_SIGNED
+		if (! sns_is_gate_vdso(start, len)) {
+			if ((newflags & VM_EXEC) && current->sns_valid_sig){
+				if ((newflags & VM_WRITE) == 0) {
+					if (current->sns_valid_sig && vma->vm_file) {
+						vma->sns_valid_sig = sns_signature_valid(vma->vm_file);
+						current->sns_valid_sig = vma->sns_valid_sig;
+						if (!current->sns_valid_sig)
+							sns_signature_becomes_invalid();
+					} else {
+						vma->sns_valid_sig = 0;
+						current->sns_valid_sig = 0;
+						sns_signature_becomes_invalid();
+					}
+				} else {
+					vma->sns_valid_sig = 0;
+					current->sns_valid_sig = 0;
+					sns_signature_becomes_invalid();
+				}
+			}
+		} else {
+			/* always trust kernelspace */
+			vma->sns_valid_sig = 1;
+		}
+#endif
+
 		tmp = vma->vm_end;
 		if (tmp > end)
 			tmp = end;
diff --git a/security/Kconfig b/security/Kconfig
index bfaace7..9776e29 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -32,6 +32,42 @@ config SNS_SIGNED_SETGID
 
 	  If you don't know exactly what you are doing, answer N.
 
+config SNS_BRK_ALLOW_EXEC
+	bool "Allows the executable bit to be set on brk()-ed memory"
+	depends on SNS_SIGNED
+	help
+	  By default the memory a process gets from brk() is executable.
+	  This is undesirable in our situation because it would allow
+	  an evil binary to load unsigned code.
+
+	  We can either disable the exec-bit on that area (set this option to
+	  off), which might break some programs, or we can allow the exec bit
+	  but remove the signed status (this option on, next option on),
+	  thereby unsigning any program that does some malloc()...
+
+          Choose your poison.
+
+	  On old x86 (without CPU-flag NX to be exact) this option is useless
+	  because every readable page is also executable. This might apply to
+	  some other broken architectures as well. YMMV.
+
+	  The default behaviour everybody expects is probably broken
+	  either way. If you do not want any problems or you are
+	  unsure, enable this option (SNS_BREAK_ALLOW_EXEC) and
+	  disable the next one (SNS_BRK_UNSIGN).
+
+	  I repeat: Always say Y, unless you are very brave...
+
+config SNS_BRK_UNSIGN
+	bool "Unsign process after doing brk()"
+	depends on SNS_BRK_ALLOW_EXEC
+	help
+	  Remove signed bit from any process that does brk().
+
+	  See previous option (SNS_BRK_ALLOW_EXEC) for help.
+
+	  If unsure say N.
+
 config KEYS
 	bool "Enable access key retention support"
 	help
diff --git a/security/sns.c b/security/sns.c
index 4403e5a..3192a90 100644
--- a/security/sns.c
+++ b/security/sns.c
@@ -33,6 +33,16 @@ static int sns_sig_reader(read_descriptor_t *desc, struct page *page, unsigned l
 	return read;
 }
 
+int sns_signature_becomes_invalid(void)
+{
+	current->sns_valid_sig = 0;
+	/*no checking necessary because of SEND_SIG_FORCED*/
+	force_sig_info(SIGKILL, SEND_SIG_FORCED, current);
+	return 0; /*Leave the actual killing to the scheduler*/
+	/*TODO check if the process has any way left to execute any code*/
+}
+
+
 /*
  * check file signature for setuid
  */
-- 
1.5.2.1

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux