]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - security/selinux/hooks.c
LSM/SELinux: show LSM mount options in /proc/mounts
[net-next-2.6.git] / security / selinux / hooks.c
index 308e2cf17d75ef363907243359fcf1c64a9d0800..33dee83fdd2f43ce5b59580b5e9b3e80d3bb5d9d 100644 (file)
@@ -9,7 +9,8 @@
  *           James Morris <jmorris@redhat.com>
  *
  *  Copyright (C) 2001,2002 Networks Associates Technology, Inc.
- *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *  Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *                                        Eric Paris <eparis@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *                         <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
 #include <linux/spinlock.h>
 #include <linux/syscalls.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
-#include <linux/ext2_fs.h>
 #include <linux/proc_fs.h>
-#include <linux/kd.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
@@ -52,7 +52,7 @@
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
 #include <net/net_namespace.h>
 #include <net/netlabel.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <asm/ioctls.h>
 #include <asm/atomic.h>
 #include <linux/bitops.h>
@@ -103,7 +103,9 @@ int selinux_enforcing;
 
 static int __init enforcing_setup(char *str)
 {
-       selinux_enforcing = simple_strtol(str, NULL, 0);
+       unsigned long enforcing;
+       if (!strict_strtoul(str, 0, &enforcing))
+               selinux_enforcing = enforcing ? 1 : 0;
        return 1;
 }
 __setup("enforcing=", enforcing_setup);
@@ -114,7 +116,9 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
 
 static int __init selinux_enabled_setup(char *str)
 {
-       selinux_enabled = simple_strtol(str, NULL, 0);
+       unsigned long enabled;
+       if (!strict_strtoul(str, 0, &enabled))
+               selinux_enabled = enabled ? 1 : 0;
        return 1;
 }
 __setup("selinux=", selinux_enabled_setup);
@@ -553,13 +557,15 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        struct task_security_struct *tsec = current->security;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
-       struct inode *inode = sbsec->sb->s_root->d_inode;
-       struct inode_security_struct *root_isec = inode->i_security;
+       struct dentry *root = sb->s_root;
+       struct inode *root_inode = root->d_inode;
+       struct inode_security_struct *root_isec = root_inode->i_security;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
        char **mount_options = opts->mnt_opts;
        int *flags = opts->mnt_opts_flags;
        int num_opts = opts->num_mnt_opts;
+       bool can_xattr = false;
 
        mutex_lock(&sbsec->lock);
 
@@ -593,7 +599,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         */
        if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
            && (num_opts == 0))
-               goto out;
+               goto out;
 
        /*
         * parse the mount options, check if they are valid sids.
@@ -663,14 +669,24 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                goto out;
        }
 
-       if (strcmp(sb->s_type->name, "proc") == 0)
+       if (strcmp(name, "proc") == 0)
                sbsec->proc = 1;
 
+       /*
+        * test if the fs supports xattrs, fs_use might make use of this if the
+        * fs has no definition in policy.
+        */
+       if (root_inode->i_op->getxattr) {
+               rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+               if (rc >= 0 || rc == -ENODATA)
+                       can_xattr = true;
+       }
+
        /* Determine the labeling behavior to use for this filesystem type. */
-       rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+       rc = security_fs_use(name, &sbsec->behavior, &sbsec->sid, can_xattr);
        if (rc) {
                printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
-                      __func__, sb->s_type->name, rc);
+                      __func__, name, rc);
                goto out;
        }
 
@@ -955,6 +971,57 @@ out_err:
        return rc;
 }
 
+void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts)
+{
+       int i;
+       char *prefix;
+
+       for (i = 0; i < opts->num_mnt_opts; i++) {
+               char *has_comma = strchr(opts->mnt_opts[i], ',');
+
+               switch (opts->mnt_opts_flags[i]) {
+               case CONTEXT_MNT:
+                       prefix = CONTEXT_STR;
+                       break;
+               case FSCONTEXT_MNT:
+                       prefix = FSCONTEXT_STR;
+                       break;
+               case ROOTCONTEXT_MNT:
+                       prefix = ROOTCONTEXT_STR;
+                       break;
+               case DEFCONTEXT_MNT:
+                       prefix = DEFCONTEXT_STR;
+                       break;
+               default:
+                       BUG();
+               };
+               /* we need a comma before each option */
+               seq_putc(m, ',');
+               seq_puts(m, prefix);
+               if (has_comma)
+                       seq_putc(m, '\"');
+               seq_puts(m, opts->mnt_opts[i]);
+               if (has_comma)
+                       seq_putc(m, '\"');
+       }
+}
+
+static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+       struct security_mnt_opts opts;
+       int rc;
+
+       rc = selinux_get_mnt_opts(sb, &opts);
+       if (rc)
+               return rc;
+
+       selinux_write_opts(m, &opts);
+
+       security_free_mnt_opts(&opts);
+
+       return rc;
+}
+
 static inline u16 inode_mode_to_security_class(umode_t mode)
 {
        switch (mode & S_IFMT) {
@@ -1681,14 +1748,23 @@ static inline u32 file_to_av(struct file *file)
 
 /* Hook functions begin here. */
 
-static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
+static int selinux_ptrace(struct task_struct *parent,
+                         struct task_struct *child,
+                         unsigned int mode)
 {
        int rc;
 
-       rc = secondary_ops->ptrace(parent, child);
+       rc = secondary_ops->ptrace(parent, child, mode);
        if (rc)
                return rc;
 
+       if (mode == PTRACE_MODE_READ) {
+               struct task_security_struct *tsec = parent->security;
+               struct task_security_struct *csec = child->security;
+               return avc_has_perm(tsec->sid, csec->sid,
+                                   SECCLASS_FILE, FILE__READ, NULL);
+       }
+
        return task_has_perm(parent, child, PROCESS__PTRACE);
 }
 
@@ -2494,7 +2570,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        }
 
        if (value && len) {
-               rc = security_sid_to_context(newsid, &context, &clen);
+               rc = security_sid_to_context_force(newsid, &context, &clen);
                if (rc) {
                        kfree(namep);
                        return rc;
@@ -2619,7 +2695,7 @@ static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
        return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_setotherxattr(struct dentry *dentry, char *name)
+static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
 {
        if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof XATTR_SECURITY_PREFIX - 1)) {
@@ -2638,7 +2714,8 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, char *name)
        return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
 }
 
-static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
+static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
+                                 const void *value, size_t size, int flags)
 {
        struct task_security_struct *tsec = current->security;
        struct inode *inode = dentry->d_inode;
@@ -2667,6 +2744,11 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
                return rc;
 
        rc = security_context_to_sid(value, size, &newsid);
+       if (rc == -EINVAL) {
+               if (!capable(CAP_MAC_ADMIN))
+                       return rc;
+               rc = security_context_to_sid_force(value, size, &newsid);
+       }
        if (rc)
                return rc;
 
@@ -2687,8 +2769,9 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
                            &ad);
 }
 
-static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
-                                       void *value, size_t size, int flags)
+static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
+                                       const void *value, size_t size,
+                                       int flags)
 {
        struct inode *inode = dentry->d_inode;
        struct inode_security_struct *isec = inode->i_security;
@@ -2700,10 +2783,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
                return;
        }
 
-       rc = security_context_to_sid(value, size, &newsid);
+       rc = security_context_to_sid_force(value, size, &newsid);
        if (rc) {
-               printk(KERN_WARNING "%s:  unable to obtain SID for context "
-                      "%s, rc=%d\n", __func__, (char *)value, -rc);
+               printk(KERN_ERR "SELinux:  unable to map context to SID"
+                      "for (%s, %lu), rc=%d\n",
+                      inode->i_sb->s_id, inode->i_ino, -rc);
                return;
        }
 
@@ -2711,7 +2795,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, char *name,
        return;
 }
 
-static int selinux_inode_getxattr(struct dentry *dentry, char *name)
+static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
 {
        return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
@@ -2721,7 +2805,7 @@ static int selinux_inode_listxattr(struct dentry *dentry)
        return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
 }
 
-static int selinux_inode_removexattr(struct dentry *dentry, char *name)
+static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
 {
        if (strcmp(name, XATTR_NAME_SELINUX))
                return selinux_inode_setotherxattr(dentry, name);
@@ -2732,9 +2816,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, char *name)
 }
 
 /*
- * Copy the in-core inode security context value to the user.  If the
- * getxattr() prior to this succeeded, check to see if we need to
- * canonicalize the value to be finally returned to the user.
+ * Copy the inode security context value to the user.
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
@@ -2743,12 +2825,33 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
        u32 size;
        int error;
        char *context = NULL;
+       struct task_security_struct *tsec = current->security;
        struct inode_security_struct *isec = inode->i_security;
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
 
-       error = security_sid_to_context(isec->sid, &context, &size);
+       /*
+        * If the caller has CAP_MAC_ADMIN, then get the raw context
+        * value even if it is not defined by current policy; otherwise,
+        * use the in-core value under current policy.
+        * Use the non-auditing forms of the permission checks since
+        * getxattr may be called by unprivileged processes commonly
+        * and lack of permission just means that we fall back to the
+        * in-core context value, not a denial.
+        */
+       error = secondary_ops->capable(current, CAP_MAC_ADMIN);
+       if (!error)
+               error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+                                            SECCLASS_CAPABILITY2,
+                                            CAPABILITY2__MAC_ADMIN,
+                                            0,
+                                            NULL);
+       if (!error)
+               error = security_sid_to_context_force(isec->sid, &context,
+                                                     &size);
+       else
+               error = security_sid_to_context(isec->sid, &context, &size);
        if (error)
                return error;
        error = size;
@@ -2862,46 +2965,16 @@ static void selinux_file_free_security(struct file *file)
 static int selinux_file_ioctl(struct file *file, unsigned int cmd,
                              unsigned long arg)
 {
-       int error = 0;
-
-       switch (cmd) {
-       case FIONREAD:
-       /* fall through */
-       case FIBMAP:
-       /* fall through */
-       case FIGETBSZ:
-       /* fall through */
-       case EXT2_IOC_GETFLAGS:
-       /* fall through */
-       case EXT2_IOC_GETVERSION:
-               error = file_has_perm(current, file, FILE__GETATTR);
-               break;
-
-       case EXT2_IOC_SETFLAGS:
-       /* fall through */
-       case EXT2_IOC_SETVERSION:
-               error = file_has_perm(current, file, FILE__SETATTR);
-               break;
-
-       /* sys_ioctl() checks */
-       case FIONBIO:
-       /* fall through */
-       case FIOASYNC:
-               error = file_has_perm(current, file, 0);
-               break;
+       u32 av = 0;
 
-       case KDSKBENT:
-       case KDSKBSENT:
-               error = task_has_capability(current, CAP_SYS_TTY_CONFIG);
-               break;
+       if (_IOC_DIR(cmd) & _IOC_WRITE)
+               av |= FILE__WRITE;
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               av |= FILE__READ;
+       if (!av)
+               av = FILE__IOCTL;
 
-       /* default case assumes that the command will go
-        * to the file's ioctl() function.
-        */
-       default:
-               error = file_has_perm(current, file, FILE__IOCTL);
-       }
-       return error;
+       return file_has_perm(current, file, av);
 }
 
 static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
@@ -3284,9 +3357,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
        if (rc)
                return rc;
 
-       if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
-               return 0;
-
        if (!sig)
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
@@ -3303,12 +3373,13 @@ static int selinux_task_prctl(int option,
                              unsigned long arg2,
                              unsigned long arg3,
                              unsigned long arg4,
-                             unsigned long arg5)
+                             unsigned long arg5,
+                             long *rc_p)
 {
        /* The current prctl operations do not appear to require
           any SELinux controls since they merely observe or modify
           the state of the current process. */
-       return 0;
+       return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
 }
 
 static int selinux_task_wait(struct task_struct *p)
@@ -3662,7 +3733,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                struct sockaddr_in6 *addr6 = NULL;
                unsigned short snum;
                struct sock *sk = sock->sk;
-               u32 sid, node_perm, addrlen;
+               u32 sid, node_perm;
 
                tsec = current->security;
                isec = SOCK_INODE(sock)->i_security;
@@ -3670,12 +3741,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                if (family == PF_INET) {
                        addr4 = (struct sockaddr_in *)address;
                        snum = ntohs(addr4->sin_port);
-                       addrlen = sizeof(addr4->sin_addr.s_addr);
                        addrp = (char *)&addr4->sin_addr.s_addr;
                } else {
                        addr6 = (struct sockaddr_in6 *)address;
                        snum = ntohs(addr6->sin6_port);
-                       addrlen = sizeof(addr6->sin6_addr.s6_addr);
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                }
 
@@ -5152,6 +5221,12 @@ static int selinux_setprocattr(struct task_struct *p,
                        size--;
                }
                error = security_context_to_sid(value, size, &sid);
+               if (error == -EINVAL && !strcmp(name, "fscreate")) {
+                       if (!capable(CAP_MAC_ADMIN))
+                               return error;
+                       error = security_context_to_sid_force(value, size,
+                                                             &sid);
+               }
                if (error)
                        return error;
        }
@@ -5185,12 +5260,12 @@ static int selinux_setprocattr(struct task_struct *p,
                        struct task_struct *g, *t;
                        struct mm_struct *mm = p->mm;
                        read_lock(&tasklist_lock);
-                       do_each_thread(g, t)
+                       do_each_thread(g, t) {
                                if (t->mm == mm && t != p) {
                                        read_unlock(&tasklist_lock);
                                        return -EPERM;
                                }
-                       while_each_thread(g, t);
+                       while_each_thread(g, t);
                        read_unlock(&tasklist_lock);
                }
 
@@ -5235,7 +5310,7 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
        return security_sid_to_context(secid, secdata, seclen);
 }
 
-static int selinux_secctx_to_secid(char *secdata, u32 seclen, u32 *secid)
+static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
 {
        return security_context_to_sid(secdata, seclen, secid);
 }
@@ -5297,6 +5372,20 @@ static int selinux_key_permission(key_ref_t key_ref,
                            SECCLASS_KEY, perm, NULL);
 }
 
+static int selinux_key_getsecurity(struct key *key, char **_buffer)
+{
+       struct key_security_struct *ksec = key->security;
+       char *context = NULL;
+       unsigned len;
+       int rc;
+
+       rc = security_sid_to_context(ksec->sid, &context, &len);
+       if (!rc)
+               rc = len;
+       *_buffer = context;
+       return rc;
+}
+
 #endif
 
 static struct security_operations selinux_ops = {
@@ -5328,6 +5417,7 @@ static struct security_operations selinux_ops = {
        .sb_free_security =             selinux_sb_free_security,
        .sb_copy_data =                 selinux_sb_copy_data,
        .sb_kern_mount =                selinux_sb_kern_mount,
+       .sb_show_options =              selinux_sb_show_options,
        .sb_statfs =                    selinux_sb_statfs,
        .sb_mount =                     selinux_mount,
        .sb_umount =                    selinux_umount,
@@ -5363,7 +5453,7 @@ static struct security_operations selinux_ops = {
        .inode_listsecurity =           selinux_inode_listsecurity,
        .inode_need_killpriv =          selinux_inode_need_killpriv,
        .inode_killpriv =               selinux_inode_killpriv,
-       .inode_getsecid =               selinux_inode_getsecid,
+       .inode_getsecid =               selinux_inode_getsecid,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,
@@ -5404,7 +5494,7 @@ static struct security_operations selinux_ops = {
        .task_to_inode =                selinux_task_to_inode,
 
        .ipc_permission =               selinux_ipc_permission,
-       .ipc_getsecid =                 selinux_ipc_getsecid,
+       .ipc_getsecid =                 selinux_ipc_getsecid,
 
        .msg_msg_alloc_security =       selinux_msg_msg_alloc_security,
        .msg_msg_free_security =        selinux_msg_msg_free_security,
@@ -5485,6 +5575,7 @@ static struct security_operations selinux_ops = {
        .key_alloc =                    selinux_key_alloc,
        .key_free =                     selinux_key_free,
        .key_permission =               selinux_key_permission,
+       .key_getsecurity =              selinux_key_getsecurity,
 #endif
 
 #ifdef CONFIG_AUDIT
@@ -5533,14 +5624,6 @@ static __init int selinux_init(void)
        else
                printk(KERN_DEBUG "SELinux:  Starting in permissive mode\n");
 
-#ifdef CONFIG_KEYS
-       /* Add security information to initial keyrings */
-       selinux_key_alloc(&root_user_keyring, current,
-                         KEY_ALLOC_NOT_IN_QUOTA);
-       selinux_key_alloc(&root_session_keyring, current,
-                         KEY_ALLOC_NOT_IN_QUOTA);
-#endif
-
        return 0;
 }