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/

Reply via email to