2007-06-21 16:05:37

by Johannes Schlumberger

[permalink] [raw]
Subject: [PATCH] signed binaries support [0/4]

Hi,

We (two students of CS) built a system for signing binaries and verifying them
before executing. Our main focus was to implement a way to inhibit execution
of suid-binaries, which are not trustworthy (i.e. not signed). Of course this
can also be used to grant other access rights, capabilities, etc.

The signature (e.g. HMAC-SHA1 with a shared secret) is stored in extended
filesystem attributes (userland-signing-tool provided) [1]. Depending on the
outcome of our check (performed during exec) a newly introduced flag in
the task_struct is set. To be able to also check libraries, we introduced a
similar flag in the vm_area struct. Depending on the state of the flag, the
suid/sgid bit on the file is honored or ignored. If a process behaves badly
(e.g mapping executable memory writable or loading an untrusted library) it
is handled appropriately (killed in our current implementation).

In the current state our code is of course very expermimental and should be
handled with care.

We mainly seek comments, suggestions and wisdom before we tackle the more
difficult tasks, like proper signatures (public-key-systems, etc.).

regards,
Johannes

[1] http://git.informatik.uni-erlangen.de/?p=ssuid-userland&a=snapshot;h=HEAD

--
Johannes Schlumberger Department of Computer Science IV
Martensstrasse 1 D-91058 Erlangen Germany University of Erlangen-Nuremberg
http://wwwcip.informatik.uni-erlangen.de/~spjsschl


2007-06-21 16:10:28

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] export xattr_resolve_name_sns [1/4]

From: Johannes Schlumberger <[email protected]>

Makes it possible to get extended attributes for a given inode. We need this
for cases where we no longer have the corresponding direntry.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
fs/xattr.c | 18 ++++++++++++++++++
include/linux/xattr.h | 1 +
2 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 4523aca..467417f 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -538,6 +538,24 @@ xattr_resolve_name(struct xattr_handler **handlers, const char **name)
return handler;
}

+struct xattr_handler *
+xattr_resolve_name_sns(struct xattr_handler **handlers, const char **name)
+{
+ struct xattr_handler *handler;
+
+ if (!*name)
+ return NULL;
+
+ for_each_xattr_handler(handlers, handler) {
+ const char *n = strcmp_prefix(*name, handler->prefix);
+ if (n) {
+ *name = n;
+ break;
+ }
+ }
+ return handler;
+}
+
/*
* Find the handler for the prefix and dispatch its get() operation.
*/
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index def131a..5653508 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -46,6 +46,7 @@ struct xattr_handler {
size_t size, int flags);
};

+struct xattr_handler * xattr_resolve_name_sns(struct xattr_handler **, const char **);
ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
int vfs_setxattr(struct dentry *, char *, void *, size_t, int);
--
1.5.2.1

2007-06-21 16:10:41

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] sns: add syscall to check signed state of a process [4/4]

Makes it possible for a userspace process to ask for the trustworthiness of
another process.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
arch/i386/kernel/syscall_table.S | 1 +
include/asm-i386/unistd.h | 3 ++-
security/sns.c | 15 +++++++++++++++
3 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S
index bf6adce..e8ba35a 100644
--- a/arch/i386/kernel/syscall_table.S
+++ b/arch/i386/kernel/syscall_table.S
@@ -323,3 +323,4 @@ ENTRY(sys_call_table)
.long sys_signalfd
.long sys_timerfd
.long sys_eventfd
+ .long sys_sns_is_trusted /* 320 */
diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h
index e84ace1..3f8df3e 100644
--- a/include/asm-i386/unistd.h
+++ b/include/asm-i386/unistd.h
@@ -329,10 +329,11 @@
#define __NR_signalfd 321
#define __NR_timerfd 322
#define __NR_eventfd 323
+#define __NR_sns_is_trusted 324

#ifdef __KERNEL__

-#define NR_syscalls 324
+#define NR_syscalls 325

#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
diff --git a/security/sns.c b/security/sns.c
index 3192a90..1978f7c 100644
--- a/security/sns.c
+++ b/security/sns.c
@@ -112,3 +112,18 @@ int sns_signature_valid(struct file *file)
crypto_free_hash(tfm);
return ret;
}
+
+asmlinkage int sys_sns_is_trusted(pid_t p)
+{
+ struct task_struct *t;
+ rcu_read_lock();
+ t = find_task_by_pid(p);
+ if (IS_ERR(t)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ p = t->sns_valid_sig; /*locking needed*/
+ rcu_read_unlock();
+ return p;
+}
+EXPORT_SYMBOL_GPL(sys_sns_is_trusted);
--
1.5.2.1

2007-06-21 16:10:55

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] Check files' signatures before doing suid/sgid [2/4]

Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
on fork() and checked during exec before giving the new process suid/sgid
privileges.

sns.c contains our helper functions to verify the signatures.
sns_secret_key.dat contains the 'secret key' which is used for HMAC.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
fs/exec.c | 19 +++++++-
include/linux/Kbuild | 2 +
include/linux/sched.h | 3 +
include/linux/sns.h | 3 +
kernel/fork.c | 6 +++
security/Kconfig | 28 ++++++++++++
security/Makefile | 1 +
security/sns.c | 104 +++++++++++++++++++++++++++++++++++++++++++
security/sns_secret_key.dat | 5 ++
9 files changed, 169 insertions(+), 2 deletions(-)
create mode 100644 include/linux/sns.h
create mode 100644 security/sns.c
create mode 100644 security/sns_secret_key.dat

diff --git a/fs/exec.c b/fs/exec.c
index f20561f..5dfa406 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -51,6 +51,9 @@
#include <linux/cn_proc.h>
#include <linux/audit.h>
#include <linux/signalfd.h>
+#ifdef CONFIG_SNS_SIGNED
+#include <linux/sns.h>
+#endif

#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -928,13 +931,21 @@ int prepare_binprm(struct linux_binprm *bprm)
mode = inode->i_mode;
if (bprm->file->f_op == NULL)
return -EACCES;
+#ifdef CONFIG_SNS_SIGNED
+ if (mode & S_ISUID)
+ current->sns_valid_sig = sns_signature_valid(bprm->file);
+#endif

bprm->e_uid = current->euid;
bprm->e_gid = current->egid;

if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
/* Set-uid? */
- if (mode & S_ISUID) {
+#ifdef CONFIG_SNS_SIGNED_SETUID
+ if (mode & S_ISUID && current->sns_valid_sig) {
+#else
+ if (mode & S_ISUID) {
+#endif /*SNS_SIGNED_SETUID*/
current->personality &= ~PER_CLEAR_ON_SETID;
bprm->e_uid = inode->i_uid;
}
@@ -945,7 +956,11 @@ int prepare_binprm(struct linux_binprm *bprm)
* is a candidate for mandatory locking, not a setgid
* executable.
*/
- if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+#ifdef CONFIG_SNS_SIGNED_SETGID
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && current->sns_valid_sig) {
+#else
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+#endif /*SNS_SIGNED_SETGID*/
current->personality &= ~PER_CLEAR_ON_SETID;
bprm->e_gid = inode->i_gid;
}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index f317c27..16df5f0 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -159,6 +159,7 @@ header-y += videotext.h
header-y += vt.h
header-y += wireless.h
header-y += x25.h
+header-y += sns.h

unifdef-y += acct.h
unifdef-y += adb.h
@@ -347,5 +348,6 @@ unifdef-y += watchdog.h
unifdef-y += wireless.h
unifdef-y += xattr.h
unifdef-y += xfrm.h
+unifdef-y += sns.h

objhdr-y += version.h
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 693f0e6..36c58d6 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1076,6 +1076,9 @@ struct task_struct {
#ifdef CONFIG_FAULT_INJECTION
int make_it_fail;
#endif
+#ifdef CONFIG_SNS_SIGNED
+ int sns_valid_sig;
+#endif
};

static inline pid_t process_group(struct task_struct *tsk)
diff --git a/include/linux/sns.h b/include/linux/sns.h
new file mode 100644
index 0000000..ad15e4b
--- /dev/null
+++ b/include/linux/sns.h
@@ -0,0 +1,3 @@
+#ifdef CONFIG_SNS_SIGNED
+int sns_signature_valid(struct file *);
+#endif
diff --git a/kernel/fork.c b/kernel/fork.c
index 73ad5cd..c12cf61 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -156,6 +156,9 @@ void __init fork_init(unsigned long mempages)
init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
init_task.signal->rlim[RLIMIT_SIGPENDING] =
init_task.signal->rlim[RLIMIT_NPROC];
+#ifdef CONFIG_SNS_SIGNED
+ init_task.sns_valid_sig = 0;
+#endif
}

static struct task_struct *dup_task_struct(struct task_struct *orig)
@@ -182,6 +185,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
#ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
#endif
+#ifdef CONFIG_SNS_SIGNED
+ tsk->sns_valid_sig = orig->sns_valid_sig;
+#endif

/* One for us, one for whoever does the "release_task()" (usually parent) */
atomic_set(&tsk->usage,2);
diff --git a/security/Kconfig b/security/Kconfig
index 460e5c9..bfaace7 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -4,6 +4,34 @@

menu "Security options"

+config SNS_SIGNED
+ bool "Enable sns-signed binaries (EXPERIMENTAL)"
+ depends on (EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4DEV_FS_XATTR || REISERFS_FS_XATTR || JFFS2_FS_XATTR || CIFS_XATTR) && (CRYPTO_SHA1 || CRYPTO_HMAC || CRYPTO_MD5) && MMU && EXPERIMENTAL
+ help
+ This option turns on sns-signatures of binaries. Requires extended
+ attributes and cryptographic hashes/HMAC support. HMAC is preferred.
+
+ This will leave your system unusable without proper preparation of
+ your sbit-files.
+
+ If you don't know exactly what you are doing, answer N.
+
+config SNS_SIGNED_SETUID
+ bool "Enables sns-signed binaries mandatory for suid-bits"
+ depends on SNS_SIGNED
+ help
+ Mandates signed binaries for suidbits.
+
+ If you don't know exactly what you are doing, answer N.
+
+config SNS_SIGNED_SETGID
+ bool "Enables sns-signed binaries mandatory for sgid-bits"
+ depends on SNS_SIGNED
+ help
+ Mandates signed binaries for sgidbits.
+
+ If you don't know exactly what you are doing, answer N.
+
config KEYS
bool "Enable access key retention support"
help
diff --git a/security/Makefile b/security/Makefile
index ef87df2..677b978 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
+obj-$(CONFIG_SNS_SIGNED) += sns.o
diff --git a/security/sns.c b/security/sns.c
new file mode 100644
index 0000000..4403e5a
--- /dev/null
+++ b/security/sns.c
@@ -0,0 +1,104 @@
+#include <linux/crypto.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#include <linux/xattr.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/sns.h>
+
+#include "sns_secret_key.dat"
+
+#define SNS_MAX_DIGEST_SIZE 64
+
+struct sns_attr {
+ char algname[CRYPTO_MAX_ALG_NAME+1];
+ char hash_value[SNS_MAX_DIGEST_SIZE];
+};
+
+
+static int sns_sig_reader(read_descriptor_t *desc, struct page *page, unsigned long offset, unsigned long nr)
+{
+ struct scatterlist s;
+ struct hash_desc *hash_desc = (struct hash_desc *) desc->arg.data;
+ unsigned int read;
+
+ s.page = page;
+ s.offset = offset;
+ s.length = nr;
+ read = nr - offset;
+ crypto_hash_update(hash_desc, &s, read);
+ desc->written += read;
+ desc->count -= read;
+ return read;
+}
+
+/*
+ * check file signature for setuid
+ */
+
+int sns_signature_valid(struct file *file)
+{
+ unsigned long i;
+ struct inode *inode = file->f_mapping->host;
+ struct crypto_hash *tfm;
+ struct hash_desc hash_desc;
+ struct sns_attr attrdata;
+ char hash_result[SNS_MAX_DIGEST_SIZE];
+ struct xattr_handler *handler;
+ const char *namespaces[2] = { "trusted.", NULL };
+ int ret = 0;
+ loff_t pos = 0;
+ read_descriptor_t read_desc;
+
+ handler = xattr_resolve_name_sns(inode->i_sb->s_xattr, namespaces);
+ if (unlikely(!handler)) {
+ printk(KERN_DEBUG "sns_signature_valid: xattr_resolve_name failed\n");
+ return 0;
+ }
+ memset(&attrdata, 0, sizeof(struct sns_attr));
+ i = handler->get(inode, "sns", &attrdata, sizeof(struct sns_attr));
+ if (i != sizeof(struct sns_attr)) {
+ printk(KERN_DEBUG "sns_signature_valid: invalid xattr found\n");
+ return 0;
+ }
+ attrdata.algname[CRYPTO_MAX_ALG_NAME] = '\0';
+ read_desc.count = i_size_read(inode);
+ if (unlikely(!read_desc.count)) {
+ printk(KERN_DEBUG "sns_signature_valid: inode of file has invalid size\n");
+ return 0;
+ }
+ tfm = crypto_alloc_hash(attrdata.algname, 0, CRYPTO_ALG_ASYNC);
+ if (unlikely(IS_ERR(tfm))) {
+ printk("sns_signature_valid: %s unavailable\n", attrdata.algname);
+ return 0;
+ /*FIXME: failure mode should be defined at build-time */
+ }
+ memset(hash_result, 0, SNS_MAX_DIGEST_SIZE); /*Needed?*/
+ hash_desc.tfm = tfm;
+ hash_desc.flags = 0;
+ read_desc.arg.data = &hash_desc;
+ read_desc.written = 0;
+ if (crypto_hash_setkey(tfm, sns_secret_key, SNS_SECRET_KEY_SIZE)) {
+ printk("sns_signature_valid: hash function did not accept setkey\n");
+ return 0;
+ }
+ crypto_hash_init(&hash_desc);
+ do_generic_file_read(file, &pos, &read_desc, sns_sig_reader);
+ crypto_hash_final(&hash_desc, hash_result);
+ BUG_ON(read_desc.written != i_size_read(inode));
+#ifdef SNS_SIGNED_DEBUG
+ printk("sns_signature_valid: attrdata.algname = %s\n", attrdata.algname);
+ printk("sns_signature_valid: attrib: ");
+ for (i = 0; i < SNS_MAX_DIGEST_SIZE; i++)
+ printk("%02x ", (unsigned char) attrdata.hash_value[i]);
+ printk("\n");
+ printk("sns_signature_valid: result: ");
+ for (i = 0; i < SNS_MAX_DIGEST_SIZE; i++)
+ printk("%02x ", (unsigned char) hash_result[i]);
+ printk("\n");
+#endif
+ ret = !memcmp(attrdata.hash_value, hash_result, SNS_MAX_DIGEST_SIZE);
+ crypto_free_hash(tfm);
+ return ret;
+}
diff --git a/security/sns_secret_key.dat b/security/sns_secret_key.dat
new file mode 100644
index 0000000..aec09da
--- /dev/null
+++ b/security/sns_secret_key.dat
@@ -0,0 +1,5 @@
+#define SNS_SECRET_KEY_SIZE 8
+static char sns_secret_key[SNS_SECRET_KEY_SIZE] =
+ {
+ 'd', 'e', 'a', 'd', 'b', 'e', 'e', 'f'
+ };
--
1.5.2.1

2007-06-21 16:11:21

by Alexander Wuerstlein

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

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

2007-06-21 16:17:53

by Adrian Bunk

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

On Thu, Jun 21, 2007 at 05:55:16PM +0200, Johannes Schlumberger wrote:

> Hi,

Hi Johannes,

> We (two students of CS) built a system for signing binaries and verifying them
> before executing. Our main focus was to implement a way to inhibit execution
> of suid-binaries, which are not trustworthy (i.e. not signed).
>...

doesn't anyone who is able to install a not trustworthy suid-binary
already have the priviliges to do anything he wants to without requiring
an suid bit?

> regards,
> Johannes
>...

cu
Adrian

--

"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed

2007-06-21 16:29:36

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

On 070621 18:19, Adrian Bunk <[email protected]> wrote:
> On Thu, Jun 21, 2007 at 05:55:16PM +0200, Johannes Schlumberger wrote:
>
> > Hi,
>
> Hi Johannes,
>
> > We (two students of CS) built a system for signing binaries and verifying them
> > before executing. Our main focus was to implement a way to inhibit execution
> > of suid-binaries, which are not trustworthy (i.e. not signed).
> >...
>
> doesn't anyone who is able to install a not trustworthy suid-binary
> already have the priviliges to do anything he wants to without requiring
> an suid bit?

Yes, quite correct in most cases. But if you have taken control of a computer
on of the more common ways to keep the control for some time is the
installation of a suid-binary (e.g. as part of a rootkit).

One could also imagine a scenario where an attacker controls some filesystems
(on external storage perhaps) where he can of course manipulate the suid bit,
but he does not have direct control over the attacked system unless he can
execute that file.



Ciao,

Alexander Wuerstlein.

2007-06-21 16:34:37

by Akinobu Mita

[permalink] [raw]
Subject: Re: [PATCH] sns: add syscall to check signed state of a process [4/4]

2007/6/22, Alexander Wuerstlein <[email protected]>:

> +asmlinkage int sys_sns_is_trusted(pid_t p)
> +{
> + struct task_struct *t;
> + rcu_read_lock();
> + t = find_task_by_pid(p);
> + if (IS_ERR(t)) {

Shouldn't it be:
if (!t) {
...
?

find_task_by_pid() returns NULL on failure.

> + rcu_read_unlock();
> + return -EINVAL;
> + }
> + p = t->sns_valid_sig; /*locking needed*/
> + rcu_read_unlock();
> + return p;
> +}

2007-06-21 16:49:32

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] sns: add syscall to check signed state of a process [4/4]

On 070621 18:34, Akinobu Mita <[email protected]> wrote:
> 2007/6/22, Alexander Wuerstlein <[email protected]>:
>
>> +asmlinkage int sys_sns_is_trusted(pid_t p)
>> +{
>> + struct task_struct *t;
>> + rcu_read_lock();
>> + t = find_task_by_pid(p);
>> + if (IS_ERR(t)) {
>
> Shouldn't it be:
> if (!t) {
> ...
> ?
>
> find_task_by_pid() returns NULL on failure.

You seem to be right, the rest of the kernel just does 'if (!t)'. We just used
IS_ERR() as the 'check for evil pointers' function.




Ciao,

Alexander Wuerstlein.

2007-06-21 17:21:27

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On Thu, 2007-06-21 at 18:02 +0200, Alexander Wuerstlein wrote:
> Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
> on fork() and checked during exec before giving the new process suid/sgid
> privileges.
>



do you also check the signature of glibc and every other shared library
that the app uses (or dlopens)? if not.. the entire exercise is rather
pointless...

2007-06-21 17:23:32

by Adrian Bunk

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

On Thu, Jun 21, 2007 at 06:29:17PM +0200, Alexander Wuerstlein wrote:
> On 070621 18:19, Adrian Bunk <[email protected]> wrote:
> > On Thu, Jun 21, 2007 at 05:55:16PM +0200, Johannes Schlumberger wrote:
> >
> > > Hi,
> >
> > Hi Johannes,
> >
> > > We (two students of CS) built a system for signing binaries and verifying them
> > > before executing. Our main focus was to implement a way to inhibit execution
> > > of suid-binaries, which are not trustworthy (i.e. not signed).
> > >...
> >
> > doesn't anyone who is able to install a not trustworthy suid-binary
> > already have the priviliges to do anything he wants to without requiring
> > an suid bit?
>
> Yes, quite correct in most cases. But if you have taken control of a computer
> on of the more common ways to keep the control for some time is the
> installation of a suid-binary (e.g. as part of a rootkit).

There are so many ways for manipulating a computer that controlling
setuid binaries hardly brings a real security gain.

> One could also imagine a scenario where an attacker controls some filesystems
> (on external storage perhaps) where he can of course manipulate the suid bit,
> but he does not have direct control over the attacked system unless he can
> execute that file.

And unless the filesystem is mounted without nosuid...

> Ciao,
>
> Alexander Wuerstlein.

cu
Adrian

--

"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed

2007-06-21 17:26:25

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 070621 19:21, Arjan van de Ven <[email protected]> wrote:
> On Thu, 2007-06-21 at 18:02 +0200, Alexander Wuerstlein wrote:
> > Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
> > on fork() and checked during exec before giving the new process suid/sgid
> > privileges.
> >
>
>
>
> do you also check the signature of glibc and every other shared library
> that the app uses (or dlopens)? if not.. the entire exercise is rather
> pointless...

We do check that, that is patch [3/4].

Of course we can only check mmap-ed files, if there is no file like with JIT
compilers we are out of luck.


Ciao,

Alexander Wuerstlein.

2007-06-21 17:33:36

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On Thu, 2007-06-21 at 19:25 +0200, Alexander Wuerstlein wrote:
> On 070621 19:21, Arjan van de Ven <[email protected]> wrote:
> > On Thu, 2007-06-21 at 18:02 +0200, Alexander Wuerstlein wrote:
> > > Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
> > > on fork() and checked during exec before giving the new process suid/sgid
> > > privileges.
> > >
> >
> >
> >
> > do you also check the signature of glibc and every other shared library
> > that the app uses (or dlopens)? if not.. the entire exercise is rather
> > pointless...
>
> We do check that, that is patch [3/4].
>
> Of course we can only check mmap-ed files, if there is no file like with JIT
> compilers we are out of luck.

or if the process uses read() not mmap().

or .. or ...


so if perl is signed and it's the perl script that is setuid, and then
it includes other perl libs... that's read() not mmap().

--
if you want to mail me at work (you don't), use arjan (at) linux.intel.com
Test the interaction between Linux and your BIOS via http://www.linuxfirmwarekit.org

2007-06-21 17:35:01

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

On 070621 19:26, Adrian Bunk <[email protected]> wrote:
> On Thu, Jun 21, 2007 at 06:29:17PM +0200, Alexander Wuerstlein wrote:
> > On 070621 18:19, Adrian Bunk <[email protected]> wrote:
> > > On Thu, Jun 21, 2007 at 05:55:16PM +0200, Johannes Schlumberger wrote:
> > >
> > > > Hi,
> > >
> > > Hi Johannes,
> > >
> > > > We (two students of CS) built a system for signing binaries and verifying them
> > > > before executing. Our main focus was to implement a way to inhibit execution
> > > > of suid-binaries, which are not trustworthy (i.e. not signed).
> > > >...
> > >
> > > doesn't anyone who is able to install a not trustworthy suid-binary
> > > already have the priviliges to do anything he wants to without requiring
> > > an suid bit?
> >
> > Yes, quite correct in most cases. But if you have taken control of a computer
> > on of the more common ways to keep the control for some time is the
> > installation of a suid-binary (e.g. as part of a rootkit).
>
> There are so many ways for manipulating a computer that controlling
> setuid binaries hardly brings a real security gain.

Even if it does not really improve security too much it can be helpful as a
part of a larger system. For example around here we use a 'sbit-checker' that
basically does a 'find' and 'chmod', which we would be able to replace by this
patch.

Also our patch is not solely about suid-binaries, we just implemented
suid-checking because it seemed a simple and obvious thing to do. Our real aim
was to implement binary signatures, which can be used in numerous security
related checks around the kernel. Btw. if you have any good ideas where one
could use them, please tell us :)



Ciao,

Alexander Wuerstlein.

2007-06-21 17:46:21

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 070621 19:33, Arjan van de Ven <[email protected]> wrote:
> On Thu, 2007-06-21 at 19:25 +0200, Alexander Wuerstlein wrote:
> > On 070621 19:21, Arjan van de Ven <[email protected]> wrote:
> > > On Thu, 2007-06-21 at 18:02 +0200, Alexander Wuerstlein wrote:
> > > > Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
> > > > on fork() and checked during exec before giving the new process suid/sgid
> > > > privileges.
> > > >
> > >
> > >
> > >
> > > do you also check the signature of glibc and every other shared library
> > > that the app uses (or dlopens)? if not.. the entire exercise is rather
> > > pointless...
> >
> > We do check that, that is patch [3/4].
> >
> > Of course we can only check mmap-ed files, if there is no file like with JIT
> > compilers we are out of luck.
>
> or if the process uses read() not mmap().

If a process uses read() it needs some executable and writable memory. We do
check for this in mprotect(). There is a problem with the i386-architecture,
because it allows execution of any readable page (except with newer
processors). But beyond that ugliness of i386, it should not be possible to
execute anything without us noticing it (hopefully).

Scripting languages are of course problematic. In the suid-case you could just
call anyone insane who wants to use a suid-shellscript. But in other cases one
might want signed binaries for, we do have a problem. With java or shell one
would need an interpreter/vm which is signed and reasonably trustworthy itself
and checks the signature of the shellscript or classfile it executes. The
(probably not all too complicated) writing of such an interpreter is left as an
exercise to the reader ;)



Ciao,

Alexander Wuerstlein.

2007-06-21 18:05:47

by Adrian Bunk

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

On Thu, Jun 21, 2007 at 07:34:45PM +0200, Alexander Wuerstlein wrote:
> On 070621 19:26, Adrian Bunk <[email protected]> wrote:
> > On Thu, Jun 21, 2007 at 06:29:17PM +0200, Alexander Wuerstlein wrote:
> > > On 070621 18:19, Adrian Bunk <[email protected]> wrote:
> > > > On Thu, Jun 21, 2007 at 05:55:16PM +0200, Johannes Schlumberger wrote:
> > > >
> > > > > Hi,
> > > >
> > > > Hi Johannes,
> > > >
> > > > > We (two students of CS) built a system for signing binaries and verifying them
> > > > > before executing. Our main focus was to implement a way to inhibit execution
> > > > > of suid-binaries, which are not trustworthy (i.e. not signed).
> > > > >...
> > > >
> > > > doesn't anyone who is able to install a not trustworthy suid-binary
> > > > already have the priviliges to do anything he wants to without requiring
> > > > an suid bit?
> > >
> > > Yes, quite correct in most cases. But if you have taken control of a computer
> > > on of the more common ways to keep the control for some time is the
> > > installation of a suid-binary (e.g. as part of a rootkit).
> >
> > There are so many ways for manipulating a computer that controlling
> > setuid binaries hardly brings a real security gain.
>
> Even if it does not really improve security too much it can be helpful as a
> part of a larger system. For example around here we use a 'sbit-checker' that
> basically does a 'find' and 'chmod', which we would be able to replace by this
> patch.

Something that sounds as if it would increase security but doesn't
really increase security is actually bad since it gives users a false
impression of security.

> Also our patch is not solely about suid-binaries, we just implemented
> suid-checking because it seemed a simple and obvious thing to do. Our real aim
> was to implement binary signatures, which can be used in numerous security
> related checks around the kernel. Btw. if you have any good ideas where one
> could use them, please tell us :)

Linux systems usually ship and heavily use interpreters like bash, perl
or python.

Does writing an ELF loader in perl circumvent everything you want to do?

> Ciao,
> Alexander Wuerstlein.

cu
Adrian

--

"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed

2007-06-21 18:21:50

by Johannes Schlumberger

[permalink] [raw]
Subject: Re: [PATCH] signed binaries support [0/4]

Hi,

> > Even if it does not really improve security too much it can be helpful as a
> > part of a larger system. For example around here we use a 'sbit-checker' that
> > basically does a 'find' and 'chmod', which we would be able to replace by this
> > patch.
>
> Something that sounds as if it would increase security but doesn't
> really increase security is actually bad since it gives users a false
> impression of security.

We are aware of the limitations, this approach has. It is not designed to make
every bit of executed code trusted, because that what just be the same as
before. Everything would be given the same amount of trust.
What we do, is establish a caste of binaries and processes more trusted than the
rest, which could be used for special tasks.

> > Also our patch is not solely about suid-binaries, we just implemented
> > suid-checking because it seemed a simple and obvious thing to do. Our real aim
> > was to implement binary signatures, which can be used in numerous security
> > related checks around the kernel. Btw. if you have any good ideas where one
> > could use them, please tell us :)

> Does writing an ELF loader in perl circumvent everything you want to do?

No, it does not. We do check any executable pages.
regards,
Johannes

--
Johannes Schlumberger Department of Computer Science IV
Martensstrasse 1 D-91058 Erlangen Germany University of Erlangen-Nuremberg
http://wwwcip.informatik.uni-erlangen.de/~spjsschl

2007-06-21 18:53:43

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On Thu, 2007-06-21 at 19:46 +0200, Alexander Wuerstlein wrote:
> On 070621 19:33, Arjan van de Ven <[email protected]> wrote:
> > On Thu, 2007-06-21 at 19:25 +0200, Alexander Wuerstlein wrote:
> > > On 070621 19:21, Arjan van de Ven <[email protected]> wrote:
> > > > On Thu, 2007-06-21 at 18:02 +0200, Alexander Wuerstlein wrote:
> > > > > Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
> > > > > on fork() and checked during exec before giving the new process suid/sgid
> > > > > privileges.
> > > > >
> > > >
> > > >
> > > >
> > > > do you also check the signature of glibc and every other shared library
> > > > that the app uses (or dlopens)? if not.. the entire exercise is rather
> > > > pointless...
> > >
> > > We do check that, that is patch [3/4].
> > >
> > > Of course we can only check mmap-ed files, if there is no file like with JIT
> > > compilers we are out of luck.
> >
> > or if the process uses read() not mmap().
>
> If a process uses read() it needs some executable and writable memory. We do
> check for this in mprotect(). There is a problem with the i386-architecture,
> because it allows execution of any readable page (except with newer
> processors). But beyond that ugliness of i386, it should not be possible to
> execute anything without us noticing it (hopefully).

welcome to mprotect() where the app can just change the permissions

--
if you want to mail me at work (you don't), use arjan (at) linux.intel.com
Test the interaction between Linux and your BIOS via http://www.linuxfirmwarekit.org

2007-06-21 21:41:14

by Johannes Schlumberger

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

Hi,

> > If a process uses read() it needs some executable and writable memory. We do
> > check for this in mprotect(). There is a problem with the i386-architecture,
> > because it allows execution of any readable page (except with newer
> > processors). But beyond that ugliness of i386, it should not be possible to
> > execute anything without us noticing it (hopefully).
>
> welcome to mprotect() where the app can just change the permissions

We have mprotect covered. If a process tries to mprotect() some pages
executable, he had writable before, it is no longer trusted in our current
implementation.

We are beginning to feel like poeple do not look at our patches, because we
screwed up the msg-id, so our Patches are not visible as one clean thread. Sorry
for that.

regards,
Johannes

--
Johannes Schlumberger Department of Computer Science IV
Martensstrasse 1 D-91058 Erlangen Germany University of Erlangen-Nuremberg
http://wwwcip.informatik.uni-erlangen.de/~spjsschl

2007-06-22 18:25:40

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] export xattr_resolve_name_sns [1/4]

From: Johannes Schlumberger <[email protected]>

Makes it possible to get extended attributes for a given inode. We need this
for cases where we no longer have the corresponding direntry.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
fs/xattr.c | 18 ++++++++++++++++++
include/linux/xattr.h | 1 +
2 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 4523aca..467417f 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -538,6 +538,24 @@ xattr_resolve_name(struct xattr_handler **handlers, const char **name)
return handler;
}

+struct xattr_handler *
+xattr_resolve_name_sns(struct xattr_handler **handlers, const char **name)
+{
+ struct xattr_handler *handler;
+
+ if (!*name)
+ return NULL;
+
+ for_each_xattr_handler(handlers, handler) {
+ const char *n = strcmp_prefix(*name, handler->prefix);
+ if (n) {
+ *name = n;
+ break;
+ }
+ }
+ return handler;
+}
+
/*
* Find the handler for the prefix and dispatch its get() operation.
*/
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index def131a..5653508 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -46,6 +46,7 @@ struct xattr_handler {
size_t size, int flags);
};

+struct xattr_handler * xattr_resolve_name_sns(struct xattr_handler **, const char **);
ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
int vfs_setxattr(struct dentry *, char *, void *, size_t, int);
--
1.5.2.1

2007-06-22 18:25:53

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] Check files' signatures before doing suid/sgid [2/4]

Modified task_struct to hold a 'signed flag' which is set on exec(), inherited
on fork() and checked during exec before giving the new process suid/sgid
privileges.

sns.c contains our helper functions to verify the signatures.
sns_secret_key.dat contains the 'secret key' which is used for HMAC.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
fs/exec.c | 19 +++++++-
include/linux/Kbuild | 2 +
include/linux/sched.h | 3 +
include/linux/sns.h | 3 +
kernel/fork.c | 6 +++
security/Kconfig | 28 ++++++++++++
security/Makefile | 1 +
security/sns.c | 104 +++++++++++++++++++++++++++++++++++++++++++
security/sns_secret_key.dat | 5 ++
9 files changed, 169 insertions(+), 2 deletions(-)
create mode 100644 include/linux/sns.h
create mode 100644 security/sns.c
create mode 100644 security/sns_secret_key.dat

diff --git a/fs/exec.c b/fs/exec.c
index f20561f..5dfa406 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -51,6 +51,9 @@
#include <linux/cn_proc.h>
#include <linux/audit.h>
#include <linux/signalfd.h>
+#ifdef CONFIG_SNS_SIGNED
+#include <linux/sns.h>
+#endif

#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -928,13 +931,21 @@ int prepare_binprm(struct linux_binprm *bprm)
mode = inode->i_mode;
if (bprm->file->f_op == NULL)
return -EACCES;
+#ifdef CONFIG_SNS_SIGNED
+ if (mode & S_ISUID)
+ current->sns_valid_sig = sns_signature_valid(bprm->file);
+#endif

bprm->e_uid = current->euid;
bprm->e_gid = current->egid;

if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
/* Set-uid? */
- if (mode & S_ISUID) {
+#ifdef CONFIG_SNS_SIGNED_SETUID
+ if (mode & S_ISUID && current->sns_valid_sig) {
+#else
+ if (mode & S_ISUID) {
+#endif /*SNS_SIGNED_SETUID*/
current->personality &= ~PER_CLEAR_ON_SETID;
bprm->e_uid = inode->i_uid;
}
@@ -945,7 +956,11 @@ int prepare_binprm(struct linux_binprm *bprm)
* is a candidate for mandatory locking, not a setgid
* executable.
*/
- if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+#ifdef CONFIG_SNS_SIGNED_SETGID
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && current->sns_valid_sig) {
+#else
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+#endif /*SNS_SIGNED_SETGID*/
current->personality &= ~PER_CLEAR_ON_SETID;
bprm->e_gid = inode->i_gid;
}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index f317c27..16df5f0 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -159,6 +159,7 @@ header-y += videotext.h
header-y += vt.h
header-y += wireless.h
header-y += x25.h
+header-y += sns.h

unifdef-y += acct.h
unifdef-y += adb.h
@@ -347,5 +348,6 @@ unifdef-y += watchdog.h
unifdef-y += wireless.h
unifdef-y += xattr.h
unifdef-y += xfrm.h
+unifdef-y += sns.h

objhdr-y += version.h
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 693f0e6..36c58d6 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1076,6 +1076,9 @@ struct task_struct {
#ifdef CONFIG_FAULT_INJECTION
int make_it_fail;
#endif
+#ifdef CONFIG_SNS_SIGNED
+ int sns_valid_sig;
+#endif
};

static inline pid_t process_group(struct task_struct *tsk)
diff --git a/include/linux/sns.h b/include/linux/sns.h
new file mode 100644
index 0000000..ad15e4b
--- /dev/null
+++ b/include/linux/sns.h
@@ -0,0 +1,3 @@
+#ifdef CONFIG_SNS_SIGNED
+int sns_signature_valid(struct file *);
+#endif
diff --git a/kernel/fork.c b/kernel/fork.c
index 73ad5cd..c12cf61 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -156,6 +156,9 @@ void __init fork_init(unsigned long mempages)
init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
init_task.signal->rlim[RLIMIT_SIGPENDING] =
init_task.signal->rlim[RLIMIT_NPROC];
+#ifdef CONFIG_SNS_SIGNED
+ init_task.sns_valid_sig = 0;
+#endif
}

static struct task_struct *dup_task_struct(struct task_struct *orig)
@@ -182,6 +185,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
#ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
#endif
+#ifdef CONFIG_SNS_SIGNED
+ tsk->sns_valid_sig = orig->sns_valid_sig;
+#endif

/* One for us, one for whoever does the "release_task()" (usually parent) */
atomic_set(&tsk->usage,2);
diff --git a/security/Kconfig b/security/Kconfig
index 460e5c9..bfaace7 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -4,6 +4,34 @@

menu "Security options"

+config SNS_SIGNED
+ bool "Enable sns-signed binaries (EXPERIMENTAL)"
+ depends on (EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4DEV_FS_XATTR || REISERFS_FS_XATTR || JFFS2_FS_XATTR || CIFS_XATTR) && (CRYPTO_SHA1 || CRYPTO_HMAC || CRYPTO_MD5) && MMU && EXPERIMENTAL
+ help
+ This option turns on sns-signatures of binaries. Requires extended
+ attributes and cryptographic hashes/HMAC support. HMAC is preferred.
+
+ This will leave your system unusable without proper preparation of
+ your sbit-files.
+
+ If you don't know exactly what you are doing, answer N.
+
+config SNS_SIGNED_SETUID
+ bool "Enables sns-signed binaries mandatory for suid-bits"
+ depends on SNS_SIGNED
+ help
+ Mandates signed binaries for suidbits.
+
+ If you don't know exactly what you are doing, answer N.
+
+config SNS_SIGNED_SETGID
+ bool "Enables sns-signed binaries mandatory for sgid-bits"
+ depends on SNS_SIGNED
+ help
+ Mandates signed binaries for sgidbits.
+
+ If you don't know exactly what you are doing, answer N.
+
config KEYS
bool "Enable access key retention support"
help
diff --git a/security/Makefile b/security/Makefile
index ef87df2..677b978 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
+obj-$(CONFIG_SNS_SIGNED) += sns.o
diff --git a/security/sns.c b/security/sns.c
new file mode 100644
index 0000000..4403e5a
--- /dev/null
+++ b/security/sns.c
@@ -0,0 +1,104 @@
+#include <linux/crypto.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#include <linux/xattr.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/sns.h>
+
+#include "sns_secret_key.dat"
+
+#define SNS_MAX_DIGEST_SIZE 64
+
+struct sns_attr {
+ char algname[CRYPTO_MAX_ALG_NAME+1];
+ char hash_value[SNS_MAX_DIGEST_SIZE];
+};
+
+
+static int sns_sig_reader(read_descriptor_t *desc, struct page *page, unsigned long offset, unsigned long nr)
+{
+ struct scatterlist s;
+ struct hash_desc *hash_desc = (struct hash_desc *) desc->arg.data;
+ unsigned int read;
+
+ s.page = page;
+ s.offset = offset;
+ s.length = nr;
+ read = nr - offset;
+ crypto_hash_update(hash_desc, &s, read);
+ desc->written += read;
+ desc->count -= read;
+ return read;
+}
+
+/*
+ * check file signature for setuid
+ */
+
+int sns_signature_valid(struct file *file)
+{
+ unsigned long i;
+ struct inode *inode = file->f_mapping->host;
+ struct crypto_hash *tfm;
+ struct hash_desc hash_desc;
+ struct sns_attr attrdata;
+ char hash_result[SNS_MAX_DIGEST_SIZE];
+ struct xattr_handler *handler;
+ const char *namespaces[2] = { "trusted.", NULL };
+ int ret = 0;
+ loff_t pos = 0;
+ read_descriptor_t read_desc;
+
+ handler = xattr_resolve_name_sns(inode->i_sb->s_xattr, namespaces);
+ if (unlikely(!handler)) {
+ printk(KERN_DEBUG "sns_signature_valid: xattr_resolve_name failed\n");
+ return 0;
+ }
+ memset(&attrdata, 0, sizeof(struct sns_attr));
+ i = handler->get(inode, "sns", &attrdata, sizeof(struct sns_attr));
+ if (i != sizeof(struct sns_attr)) {
+ printk(KERN_DEBUG "sns_signature_valid: invalid xattr found\n");
+ return 0;
+ }
+ attrdata.algname[CRYPTO_MAX_ALG_NAME] = '\0';
+ read_desc.count = i_size_read(inode);
+ if (unlikely(!read_desc.count)) {
+ printk(KERN_DEBUG "sns_signature_valid: inode of file has invalid size\n");
+ return 0;
+ }
+ tfm = crypto_alloc_hash(attrdata.algname, 0, CRYPTO_ALG_ASYNC);
+ if (unlikely(IS_ERR(tfm))) {
+ printk("sns_signature_valid: %s unavailable\n", attrdata.algname);
+ return 0;
+ /*FIXME: failure mode should be defined at build-time */
+ }
+ memset(hash_result, 0, SNS_MAX_DIGEST_SIZE); /*Needed?*/
+ hash_desc.tfm = tfm;
+ hash_desc.flags = 0;
+ read_desc.arg.data = &hash_desc;
+ read_desc.written = 0;
+ if (crypto_hash_setkey(tfm, sns_secret_key, SNS_SECRET_KEY_SIZE)) {
+ printk("sns_signature_valid: hash function did not accept setkey\n");
+ return 0;
+ }
+ crypto_hash_init(&hash_desc);
+ do_generic_file_read(file, &pos, &read_desc, sns_sig_reader);
+ crypto_hash_final(&hash_desc, hash_result);
+ BUG_ON(read_desc.written != i_size_read(inode));
+#ifdef SNS_SIGNED_DEBUG
+ printk("sns_signature_valid: attrdata.algname = %s\n", attrdata.algname);
+ printk("sns_signature_valid: attrib: ");
+ for (i = 0; i < SNS_MAX_DIGEST_SIZE; i++)
+ printk("%02x ", (unsigned char) attrdata.hash_value[i]);
+ printk("\n");
+ printk("sns_signature_valid: result: ");
+ for (i = 0; i < SNS_MAX_DIGEST_SIZE; i++)
+ printk("%02x ", (unsigned char) hash_result[i]);
+ printk("\n");
+#endif
+ ret = !memcmp(attrdata.hash_value, hash_result, SNS_MAX_DIGEST_SIZE);
+ crypto_free_hash(tfm);
+ return ret;
+}
diff --git a/security/sns_secret_key.dat b/security/sns_secret_key.dat
new file mode 100644
index 0000000..aec09da
--- /dev/null
+++ b/security/sns_secret_key.dat
@@ -0,0 +1,5 @@
+#define SNS_SECRET_KEY_SIZE 8
+static char sns_secret_key[SNS_SECRET_KEY_SIZE] =
+ {
+ 'd', 'e', 'a', 'd', 'b', 'e', 'e', 'f'
+ };
--
1.5.2.1

2007-06-22 18:26:10

by Alexander Wuerstlein

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

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

2007-06-22 18:26:31

by Alexander Wuerstlein

[permalink] [raw]
Subject: [PATCH] sns: add syscall to check signed state of a process [4/4]

Makes it possible for a userspace process to ask for the trustworthiness of
another process.

Signed-off-by: Johannes Schlumberger <[email protected]>
---
arch/i386/kernel/syscall_table.S | 1 +
include/asm-i386/unistd.h | 3 ++-
security/sns.c | 15 +++++++++++++++
3 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S
index bf6adce..e8ba35a 100644
--- a/arch/i386/kernel/syscall_table.S
+++ b/arch/i386/kernel/syscall_table.S
@@ -323,3 +323,4 @@ ENTRY(sys_call_table)
.long sys_signalfd
.long sys_timerfd
.long sys_eventfd
+ .long sys_sns_is_trusted /* 320 */
diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h
index e84ace1..3f8df3e 100644
--- a/include/asm-i386/unistd.h
+++ b/include/asm-i386/unistd.h
@@ -329,10 +329,11 @@
#define __NR_signalfd 321
#define __NR_timerfd 322
#define __NR_eventfd 323
+#define __NR_sns_is_trusted 324

#ifdef __KERNEL__

-#define NR_syscalls 324
+#define NR_syscalls 325

#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
diff --git a/security/sns.c b/security/sns.c
index 3192a90..1978f7c 100644
--- a/security/sns.c
+++ b/security/sns.c
@@ -112,3 +112,18 @@ int sns_signature_valid(struct file *file)
crypto_free_hash(tfm);
return ret;
}
+
+asmlinkage int sys_sns_is_trusted(pid_t p)
+{
+ struct task_struct *t;
+ rcu_read_lock();
+ t = find_task_by_pid(p);
+ if (IS_ERR(t)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ p = t->sns_valid_sig; /*locking needed*/
+ rcu_read_unlock();
+ return p;
+}
+EXPORT_SYMBOL_GPL(sys_sns_is_trusted);
--
1.5.2.1

2007-06-22 19:37:14

by Satyam Sharma

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

Hi Alexander, Johannes,

[ Added linux-crypto to Cc: ]

Wow, this is _one_ *intrusive* patchset indeed :-)

But first: Have you checked the digsig project? It's been doing
(for some time) what your current patchset proposes -- and
it uses public key cryptosystems for the key management,
which is decidedly better than using secret-keyed hashes
(HMAC, XCBC). Also, digsig aims to protect executable
binaries in general, and not just suid / sgid ones.

Second: Can we have some discussion on the security model /
threat model / trust model / cryptographic key management
scheme of your signing mechanism? [I had read through the
[0/4] mail you had sent yesterday, but found no relevant
discussion on these aspects there.]

>From the patchset, it appears you use a *common* secret key
for _all_ signed binaries, and it is set at kernel build-time itself:

On 6/22/07, Alexander Wuerstlein <[email protected]> wrote:
> sns_secret_key.dat contains the 'secret key' which is used for HMAC.

Where:

> --- /dev/null
> +++ b/security/sns_secret_key.dat
> +#define SNS_SECRET_KEY_SIZE 8
> +static char sns_secret_key[SNS_SECRET_KEY_SIZE] =
> + {
> + 'd', 'e', 'a', 'd', 'b', 'e', 'e', 'f'
> + };

[ Ok, I won't nitpick as to why does this file look like a header,
is #include-d in the C source as a header, but still has a .dat
extension :-) ]

Anyway, this is *totally* insecure and broken. Do you realize anybody
who lays hands on the kernel image can now _trivially_ extract the
should-have-been-a-secret key from it and use it to sign his own
binaries?

Satyam

2007-06-23 17:54:48

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]


On Jun 22 2007 20:25, Alexander Wuerstlein wrote:
>+#ifdef CONFIG_SNS_SIGNED
>+#include <linux/sns.h>
>+#endif
>
> #include <asm/uaccess.h>
> #include <asm/mmu_context.h>
>@@ -928,13 +931,21 @@ int prepare_binprm(struct linux_binprm *bprm)
> mode = inode->i_mode;
> if (bprm->file->f_op == NULL)
> return -EACCES;
>+#ifdef CONFIG_SNS_SIGNED
>+ if (mode & S_ISUID)
>+ current->sns_valid_sig = sns_signature_valid(bprm->file);
>+#endif
>
> bprm->e_uid = current->euid;
> bprm->e_gid = current->egid;
>
> if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
> /* Set-uid? */
>- if (mode & S_ISUID) {
>+#ifdef CONFIG_SNS_SIGNED_SETUID
>+ if (mode & S_ISUID && current->sns_valid_sig) {
>+#else
>+ if (mode & S_ISUID) {
>+#endif /*SNS_SIGNED_SETUID*/
> current->personality &= ~PER_CLEAR_ON_SETID;
> bprm->e_uid = inode->i_uid;
> }
>@@ -945,7 +956,11 @@ int prepare_binprm(struct linux_binprm *bprm)
> * is a candidate for mandatory locking, not a setgid
> * executable.
> */
>- if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
>+#ifdef CONFIG_SNS_SIGNED_SETGID
>+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) && current->sns_valid_sig) {
>+#else
>+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
>+#endif /*SNS_SIGNED_SETGID*/
> current->personality &= ~PER_CLEAR_ON_SETID;
> bprm->e_gid = inode->i_gid;
> }

...
That's a lot of unwanted ifdefs!



Jan
--

2007-06-23 18:01:37

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]


On Jun 21 2007 19:46, Alexander Wuerstlein wrote:
>
>If a process uses read() it needs some executable and writable memory. We do
>check for this in mprotect(). There is a problem with the i386-architecture,
>because it allows execution of any readable page (except with newer
>processors). But beyond that ugliness of i386, it should not be possible to
>execute anything without us noticing it (hopefully).

r and x together is not a problem IMHO.


Jan
--

2007-06-23 18:03:24

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] sns: add syscall to check signed state of a process [4/4]


On Jun 21 2007 18:49, Alexander Wuerstlein wrote:
>
>You seem to be right, the rest of the kernel just does 'if (!t)'. We just used
>IS_ERR() as the 'check for evil pointers' function.

But NULL is not covered by IS_ERR.


Jan
--

2007-06-24 22:58:21

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 070622 21:40, Satyam Sharma <[email protected]> wrote:
> Hi Alexander, Johannes,
>
> But first: Have you checked the digsig project? It's been doing
> (for some time) what your current patchset proposes -- and
> it uses public key cryptosystems for the key management,
> which is decidedly better than using secret-keyed hashes
> (HMAC, XCBC). Also, digsig aims to protect executable
> binaries in general, and not just suid / sgid ones.

We have not heard about digsig before, thanks for pointing it out. After a
short look over the source (correct me if I'm wrong): The most important
difference between our project and digsig is that digsig relies on storing
signatures inside the ELF file structure. Therefore a handmade binary-loader or
just COFF binaries could be used to circumvent digsig. We decided against
altering the file itself for that and some other reasons.

The limitation to suid/sgid was only due to a limited amount of time we had for
implementing our patch. For the future we are planning further uses like
setting capabilities only for signed binaries.

> Second: Can we have some discussion on the security model /
> threat model / trust model / cryptographic key management
> scheme of your signing mechanism? [I had read through the
> [0/4] mail you had sent yesterday, but found no relevant
> discussion on these aspects there.]

Our scenario was as follows: Usually system administrators rely on cronjobs
checking their binaries for unwanted suid-bits. Because of the obvious problems
with this (time between cronjobs, performance) we wrote our patch to replace it.

An admin would verify the to-be-installed binaries (e.g. by reading the source,
checking the distribution's package signatures), sign them in a central
location. He then distributes those signatures along with the installation
packages onto his computers. There should only be one key in use at a site the
public part of which is compiled into the kernel. Any kind of chain-of-trust
should be handled in userspace by signing or not signing with the site-wide
key depending on the earlier signatures in the chain.

So far for the initial idea. Perhaps it would be useful to have more than one
key or some more complex scheme for obtaining the keys and checking their
validity. But as all of this would need to be part of the kernel we decided to
rather keep it as simple as possible, anything complex is better and more
flexibly done in userspace.

> From the patchset, it appears you use a *common* secret key
> for _all_ signed binaries, and it is set at kernel build-time itself:
> [...]
> Anyway, this is *totally* insecure and broken. Do you realize anybody
> who lays hands on the kernel image can now _trivially_ extract the
> should-have-been-a-secret key from it and use it to sign his own
> binaries?

We do realize that this is really really ugly, broken and nasty and nobody
would or should ever want to use it for anything but playing around as it is
atm. ;)

We only used HMAC because it was already available inside the kernel, for
implementing real asymetric cryptography there was simply no time. Of course
our next objective is to implement that.


Ciao,

Alexander Wuerstlein.


Attachments:
(No filename) (3.13 kB)
(No filename) (185.00 B)
Download all attachments

2007-06-25 10:54:47

by Johannes Schlumberger

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]


Hi,

> >If a process uses read() it needs some executable and writable memory. We do
> >check for this in mprotect(). There is a problem with the i386-architecture,
> >because it allows execution of any readable page (except with newer
> >processors). But beyond that ugliness of i386, it should not be possible to
> >execute anything without us noticing it (hopefully).
>
> r and x together is not a problem IMHO.

It is, if you would want to have rw but can only get rwx.
regards,
Johannes

--
Johannes Schlumberger Department of Computer Science IV
Martensstrasse 1 D-91058 Erlangen Germany University of Erlangen-Nuremberg
http://wwwcip.informatik.uni-erlangen.de/~spjsschl

2007-06-25 23:53:17

by Satyam Sharma

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 6/25/07, Alexander Wuerstlein
<[email protected]> wrote:
> On 070622 21:40, Satyam Sharma <[email protected]> wrote:
> > [...]
> > But first: Have you checked the digsig project? It's been doing
> > (for some time) what your current patchset proposes -- and
> > it uses public key cryptosystems for the key management,
> > which is decidedly better than using secret-keyed hashes
> > (HMAC, XCBC). Also, digsig aims to protect executable
> > binaries in general, and not just suid / sgid ones.
>
> We have not heard about digsig before, thanks for pointing it out. After a
> short look over the source (correct me if I'm wrong): The most important
> difference between our project and digsig is that digsig relies on storing
> signatures inside the ELF file structure. Therefore a handmade binary-loader or
> just COFF binaries could be used to circumvent digsig.

Yes, that's correct.

> We decided against
> altering the file itself for that and some other reasons.
> The limitation to suid/sgid was only due to a limited amount of time we had for
> implementing our patch. For the future we are planning further uses like
> setting capabilities only for signed binaries.

Ok, effectively what you have there is a signature on an entire file stored in
one of its extended attributes, so I suspect you could think of few other
applications for something like this too.

> > Second: Can we have some discussion on the security model /
> > threat model / trust model / cryptographic key management
> > scheme of your signing mechanism? [I had read through the
> > [0/4] mail you had sent yesterday, but found no relevant
> > discussion on these aspects there.]
> [...]
> An admin would verify the to-be-installed binaries (e.g. by reading the source,
> checking the distribution's package signatures), sign them in a central
> location. He then distributes those signatures along with the installation
> packages onto his computers. There should only be one key in use at a site the
> public part of which is compiled into the kernel. Any kind of chain-of-trust
> should be handled in userspace by signing or not signing with the site-wide
> key depending on the earlier signatures in the chain.

Ok, so:

1. Admin is trusted. [ This need not mean the same as: "superuser
_account_ is trusted", but let's stay in the real world in for now. ]
2. Signing happens at some central, assumed-to-be-secure location (and say
the private key never leaves that central secure location). And let's say the
admin *repackages* the packages, this time such that the signed files get the
signature-carrying-extended-attributes with them, so the installation
automatically copies them correctly. => nothing wrong with this assumption.
3. Kernel verifies signatures at runtime. => kernel is trusted.
4. Public key needs to be *compiled into* the kernel ... so this is not getting
into mainline, but fair enough as something site administrators would patch in
and build.
5. Chain-of-trust handled in userspace. => userspace is trusted.

Let me know if I got the trust model / key management wrong.

> So far for the initial idea. Perhaps it would be useful to have more than one
> key or some more complex scheme for obtaining the keys and checking their
> validity. But as all of this would need to be part of the kernel we decided to
> rather keep it as simple as possible, anything complex is better and more
> flexibly done in userspace.

Well, if you're trusting (privileged) userspace already, I'm suddenly
not so sure
as to what new is this patchset bringing to the table in the first
place ... could
you also describe the attack vectors / threats that you had in mind that get
blocked with the proposed scheme?

> > From the patchset, it appears you use a *common* secret key
> > for _all_ signed binaries, and it is set at kernel build-time itself:
> > [...]
> > Anyway, this is *totally* insecure and broken. Do you realize anybody
> > who lays hands on the kernel image can now _trivially_ extract the
> > should-have-been-a-secret key from it and use it to sign his own
> > binaries?
>
> We do realize that this is really really ugly, broken and nasty and nobody
> would or should ever want to use it for anything but playing around as it is
> atm. ;)
>
> We only used HMAC because it was already available inside the kernel, for
> implementing real asymetric cryptography there was simply no time. Of course
> our next objective is to implement that.

Have a look at modsign (signed kernel modules) project too (just the key
management part, specifically the asymmetric crypto and DSA implementation
that they've already ported to the kernel). You could also go through the lkml
archives for whenever that was proposed for inclusion in mainline ...

Satyam

2007-06-26 00:27:17

by Alexander Wuerstlein

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 070626 01:56, Satyam Sharma <[email protected]> wrote:
> On 6/25/07, Alexander Wuerstlein
> <[email protected]> wrote:
>> On 070622 21:40, Satyam Sharma <[email protected]> wrote:
>> > [...]
>> We decided against
>> altering the file itself for that and some other reasons.
>> The limitation to suid/sgid was only due to a limited amount of time we
>> had for
>> implementing our patch. For the future we are planning further uses like
>> setting capabilities only for signed binaries.
>
> Ok, effectively what you have there is a signature on an entire file stored
> in one of its extended attributes, so I suspect you could think of few other
> applications for something like this too.

Yes, for example one could sign Java's classfiles and employ a special trusted
Java VM which checks the signatures before execution. Also, this is a more
general case of signing kernel modules (as you mentioned below). There are
really numerous applications one could imagine, we just don't really know which
ones are practical. We definitely appreciate further ideas on this.

Also the signature-in-ELF can be used complementary to our approach: for
example NFS is currently unable to handle real extended attributes (nfs does
only posix acls). So for binaries delivered over NFS our approach wouldn't
work.

> Ok, so:
>
> 1. Admin is trusted. [ This need not mean the same as: "superuser
> _account_ is trusted", but let's stay in the real world in for now. ]
> 2. Signing happens at some central, assumed-to-be-secure location (and say
> the private key never leaves that central secure location). And let's say the
> admin *repackages* the packages, this time such that the signed files get the
> signature-carrying-extended-attributes with them, so the installation
> automatically copies them correctly. => nothing wrong with this assumption.
> 3. Kernel verifies signatures at runtime. => kernel is trusted.
> 4. Public key needs to be *compiled into* the kernel ... so this is not
> getting into mainline, but fair enough as something site administrators would
> patch in and build.

Correct up to here.

> 5. Chain-of-trust handled in userspace. => userspace is trusted.

Nope. I unluckily wrote 'userspace' where I should have said something else:
Chain-of-trust is handled in what I would label 'Adminspace' (Where we do the
signing as in points 1 and 2). There is a very small number of signatures (in
our example one) known to the kernel and only those are trusted, and those are
applied to the binaries by the administrator in your point 2. The kernel does
and should never rely on userspace to tell it which signatures are trustworthy.
Only the administrator may do so by means of the signatures directly compiled
into the kernel.

So in short: Chain-of-trust is handled by the administrator in his secure
central location.

>> So far for the initial idea. Perhaps it would be useful to have more than
>> one
>> key or some more complex scheme for obtaining the keys and checking their
>> validity. But as all of this would need to be part of the kernel we
>> decided to
>> rather keep it as simple as possible, anything complex is better and more
>> flexibly done in userspace.
>
> Well, if you're trusting (privileged) userspace already, I'm suddenly not so
> sure as to what new is this patchset bringing to the table in the first place
> ...

We do not trust any userspace application, see above.

> could you also describe the attack vectors / threats that you had in mind
> that get blocked with the proposed scheme?

We focus on attacks where an attacker may alter some executable file, for
example by altering a mounted nfs-share, manipulating disk-content by simply
pulling a disk, mounting it and writing to it, etc.

This relies on the kernel beeing trustworthy of course, so one would need to
take special measures to protect the kernel-image from beeing similarly
altered. One (somewhat not-so-secure method) would be supplying kernel images
by PXE and forbidding local booting, another measure would be using a TPM
and an appropriate bootloader to check the kernel for unwanted modifications.

> Have a look at modsign (signed kernel modules) project too (just the key
> management part, specifically the asymmetric crypto and DSA implementation
> that they've already ported to the kernel). You could also go through the
> lkml archives for whenever that was proposed for inclusion in mainline ...

We already thought about that. Using some existing code is definitely preferable
to inventing DSA again :)



Ciao,

Alexander Wuerstlein.


Attachments:
(No filename) (4.49 kB)
(No filename) (185.00 B)
Download all attachments

2007-06-26 02:13:44

by Satyam Sharma

[permalink] [raw]
Subject: Re: [PATCH] Check files' signatures before doing suid/sgid [2/4]

On 6/26/07, Alexander Wuerstlein
<[email protected]> wrote:
> [...]
> Nope. I unluckily wrote 'userspace' where I should have said something else:
> Chain-of-trust is handled in what I would label 'Adminspace' (Where we do the
> signing as in points 1 and 2). There is a very small number of signatures (in
> our example one) known to the kernel and only those are trusted, and those are
> applied to the binaries by the administrator in your point 2. The kernel does
> and should never rely on userspace to tell it which signatures are trustworthy.
> Only the administrator may do so by means of the signatures directly compiled
> into the kernel.
>
> So in short: Chain-of-trust is handled by the administrator in his secure
> central location.

Ok, so the "trust chain" you're talking about is simply the decision of the
admin to compile-in the (verified and trusted) public keys of known trusted
entities into the kernel at build time. That is not really scalable, but I guess
you might just as well impose such a restriction for sake of simplicity.

[ I initially thought a scenario where a given binary is signed by an
entity whose
corresponding public key is _not_ present in the kernel, but who does possess
a signature -- over its name, id and public key -- by another entity whose
corresponding public key _is_ built into the kernel). Then at the time of
verification there's really no other alternative to *build* the entire
chain at the
_point of verification_ (in-kernel) itself ... but this obviously
introduces huge and
ugly complexities that you'd have a hard time bringing into the kernel :-) That
"signature over name, id and public key" could be a _certificate_ (if you care
about following standards), and building their chains in-kernel ... well. But if
you really want to differentiate between kernel and userspace from security
perspective, and want to give such functionality, I don't see any easy
way out. ]

> >> So far for the initial idea. Perhaps it would be useful to have more than
> >> one
> >> key or some more complex scheme for obtaining the keys and checking their
> >> validity. But as all of this would need to be part of the kernel we
> >> decided to
> >> rather keep it as simple as possible, anything complex is better and more
> >> flexibly done in userspace.
> >
> > Well, if you're trusting (privileged) userspace already, I'm suddenly not so
> > sure as to what new is this patchset bringing to the table in the first place
> > ...
>
> We do not trust any userspace application, see above.
>
> > could you also describe the attack vectors / threats that you had in mind
> > that get blocked with the proposed scheme?
>
> We focus on attacks where an attacker may alter some executable file, for
> example by altering a mounted nfs-share, manipulating disk-content by simply
> pulling a disk, mounting it and writing to it, etc.
>
> This relies on the kernel beeing trustworthy of course, so one would need to
> take special measures to protect the kernel-image from beeing similarly
> altered. One (somewhat not-so-secure method) would be supplying kernel images
> by PXE and forbidding local booting, another measure would be using a TPM
> and an appropriate bootloader to check the kernel for unwanted modifications.

Kernel-userspace differentiation from security perspective is always tricky
(so this is why I pointed you to the discussions whenever such stuff, such
as asymmetric crypto and modsign etc are proposed to be merged). It's
definitely not impossible to compromise a _running_ kernel from privileged
userspace, if it really wanted to do so ...

Satyam