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

-
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