Merge master.kernel.org:/home/rmk/linux-2.6-serial
[pandora-kernel.git] / security / selinux / hooks.c
index 524915d..2e8b4df 100644 (file)
@@ -18,7 +18,6 @@
  *      as published by the Free Software Foundation.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -69,6 +68,7 @@
 #include <linux/sysctl.h>
 #include <linux/audit.h>
 #include <linux/string.h>
+#include <linux/selinux.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -246,6 +246,7 @@ static int superblock_alloc_security(struct super_block *sb)
        sbsec->sb = sb;
        sbsec->sid = SECINITSID_UNLABELED;
        sbsec->def_sid = SECINITSID_FILE;
+       sbsec->mntpoint_sid = SECINITSID_UNLABELED;
        sb->s_security = sbsec;
 
        return 0;
@@ -319,19 +320,53 @@ enum {
        Opt_context = 1,
        Opt_fscontext = 2,
        Opt_defcontext = 4,
+       Opt_rootcontext = 8,
 };
 
 static match_table_t tokens = {
        {Opt_context, "context=%s"},
        {Opt_fscontext, "fscontext=%s"},
        {Opt_defcontext, "defcontext=%s"},
+       {Opt_rootcontext, "rootcontext=%s"},
 };
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
 
+static int may_context_mount_sb_relabel(u32 sid,
+                       struct superblock_security_struct *sbsec,
+                       struct task_security_struct *tsec)
+{
+       int rc;
+
+       rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELFROM, NULL);
+       if (rc)
+               return rc;
+
+       rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELTO, NULL);
+       return rc;
+}
+
+static int may_context_mount_inode_relabel(u32 sid,
+                       struct superblock_security_struct *sbsec,
+                       struct task_security_struct *tsec)
+{
+       int rc;
+       rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELFROM, NULL);
+       if (rc)
+               return rc;
+
+       rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__ASSOCIATE, NULL);
+       return rc;
+}
+
 static int try_context_mount(struct super_block *sb, void *data)
 {
        char *context = NULL, *defcontext = NULL;
+       char *fscontext = NULL, *rootcontext = NULL;
        const char *name;
        u32 sid;
        int alloc = 0, rc = 0, seen = 0;
@@ -374,7 +409,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 
                        switch (token) {
                        case Opt_context:
-                               if (seen) {
+                               if (seen & (Opt_context|Opt_defcontext)) {
                                        rc = -EINVAL;
                                        printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
                                        goto out_free;
@@ -390,13 +425,13 @@ static int try_context_mount(struct super_block *sb, void *data)
                                break;
 
                        case Opt_fscontext:
-                               if (seen & (Opt_context|Opt_fscontext)) {
+                               if (seen & Opt_fscontext) {
                                        rc = -EINVAL;
                                        printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
                                        goto out_free;
                                }
-                               context = match_strdup(&args[0]);
-                               if (!context) {
+                               fscontext = match_strdup(&args[0]);
+                               if (!fscontext) {
                                        rc = -ENOMEM;
                                        goto out_free;
                                }
@@ -405,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data)
                                seen |= Opt_fscontext;
                                break;
 
+                       case Opt_rootcontext:
+                               if (seen & Opt_rootcontext) {
+                                       rc = -EINVAL;
+                                       printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+                                       goto out_free;
+                               }
+                               rootcontext = match_strdup(&args[0]);
+                               if (!rootcontext) {
+                                       rc = -ENOMEM;
+                                       goto out_free;
+                               }
+                               if (!alloc)
+                                       alloc = 1;
+                               seen |= Opt_rootcontext;
+                               break;
+
                        case Opt_defcontext:
                                if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
                                        rc = -EINVAL;
@@ -441,6 +492,28 @@ static int try_context_mount(struct super_block *sb, void *data)
        if (!seen)
                goto out;
 
+       /* sets the context of the superblock for the fs being mounted. */
+       if (fscontext) {
+               rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
+               if (rc) {
+                       printk(KERN_WARNING "SELinux: security_context_to_sid"
+                              "(%s) failed for (dev %s, type %s) errno=%d\n",
+                              fscontext, sb->s_id, name, rc);
+                       goto out_free;
+               }
+
+               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
+               if (rc)
+                       goto out_free;
+
+               sbsec->sid = sid;
+       }
+
+       /*
+        * Switch to using mount point labeling behavior.
+        * sets the label used on all file below the mountpoint, and will set
+        * the superblock context if not already set.
+        */
        if (context) {
                rc = security_context_to_sid(context, strlen(context), &sid);
                if (rc) {
@@ -450,20 +523,34 @@ static int try_context_mount(struct super_block *sb, void *data)
                        goto out_free;
                }
 
-               rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELFROM, NULL);
+               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
                if (rc)
                        goto out_free;
 
-               rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELTO, NULL);
-               if (rc)
+               if (!fscontext)
+                       sbsec->sid = sid;
+               sbsec->mntpoint_sid = sid;
+
+               sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+       }
+
+       if (rootcontext) {
+               struct inode *inode = sb->s_root->d_inode;
+               struct inode_security_struct *isec = inode->i_security;
+               rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid);
+               if (rc) {
+                       printk(KERN_WARNING "SELinux: security_context_to_sid"
+                              "(%s) failed for (dev %s, type %s) errno=%d\n",
+                              rootcontext, sb->s_id, name, rc);
                        goto out_free;
+               }
 
-               sbsec->sid = sid;
+               rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
+               if (rc)
+                       goto out_free;
 
-               if (seen & Opt_context)
-                       sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+               isec->sid = sid;
+               isec->initialized = 1;
        }
 
        if (defcontext) {
@@ -478,13 +565,7 @@ static int try_context_mount(struct super_block *sb, void *data)
                if (sid == sbsec->def_sid)
                        goto out_free;
 
-               rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELFROM, NULL);
-               if (rc)
-                       goto out_free;
-
-               rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__ASSOCIATE, NULL);
+               rc = may_context_mount_inode_relabel(sid, sbsec, tsec);
                if (rc)
                        goto out_free;
 
@@ -495,6 +576,8 @@ out_free:
        if (alloc) {
                kfree(context);
                kfree(defcontext);
+               kfree(fscontext);
+               kfree(rootcontext);
        }
 out:
        return rc;
@@ -876,8 +959,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        goto out;
                isec->sid = sid;
                break;
+       case SECURITY_FS_USE_MNTPOINT:
+               isec->sid = sbsec->mntpoint_sid;
+               break;
        default:
-               /* Default to the fs SID. */
+               /* Default to the fs superblock SID. */
                isec->sid = sbsec->sid;
 
                if (sbsec->proc) {
@@ -1099,6 +1185,17 @@ static int may_create(struct inode *dir,
                            FILESYSTEM__ASSOCIATE, &ad);
 }
 
+/* Check whether a task can create a key. */
+static int may_create_key(u32 ksid,
+                         struct task_struct *ctx)
+{
+       struct task_security_struct *tsec;
+
+       tsec = ctx->security;
+
+       return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+}
+
 #define MAY_LINK   0
 #define MAY_UNLINK 1
 #define MAY_RMDIR  2
@@ -1521,8 +1618,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
        /* Default to the current task SID. */
        bsec->sid = tsec->sid;
 
-       /* Reset create SID on execve. */
+       /* Reset fs, key, and sock SIDs on execve. */
        tsec->create_sid = 0;
+       tsec->keycreate_sid = 0;
+       tsec->sockcreate_sid = 0;
 
        if (tsec->exec_sid) {
                newsid = tsec->exec_sid;
@@ -1830,7 +1929,8 @@ static inline int selinux_option(char *option, int len)
 {
        return (match_prefix("context=", sizeof("context=")-1, option, len) ||
                match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) ||
-               match_prefix("defcontext=", sizeof("defcontext=")-1, option, len));
+               match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) ||
+               match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len));
 }
 
 static inline void take_option(char **to, char *from, int *first, int len)
@@ -1903,13 +2003,13 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)
        return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
 }
 
-static int selinux_sb_statfs(struct super_block *sb)
+static int selinux_sb_statfs(struct dentry *dentry)
 {
        struct avc_audit_data ad;
 
        AVC_AUDIT_DATA_INIT(&ad,FS);
-       ad.u.fs.dentry = sb->s_root;
-       return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad);
+       ad.u.fs.dentry = dentry->d_sb->s_root;
+       return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
 }
 
 static int selinux_mount(char * dev_name,
@@ -2574,9 +2674,11 @@ static int selinux_task_alloc_security(struct task_struct *tsk)
        tsec2->osid = tsec1->osid;
        tsec2->sid = tsec1->sid;
 
-       /* Retain the exec and create SIDs across fork */
+       /* Retain the exec, fs, key, and sock SIDs across fork */
        tsec2->exec_sid = tsec1->exec_sid;
        tsec2->create_sid = tsec1->create_sid;
+       tsec2->keycreate_sid = tsec1->keycreate_sid;
+       tsec2->sockcreate_sid = tsec1->sockcreate_sid;
 
        /* Retain ptracer SID across fork, if any.
           This will be reset by the ptrace hook upon any
@@ -2628,6 +2730,11 @@ static int selinux_task_getsid(struct task_struct *p)
        return task_has_perm(current, p, PROCESS__GETSESSION);
 }
 
+static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
+{
+       selinux_get_task_sid(p, secid);
+}
+
 static int selinux_task_setgroups(struct group_info *group_info)
 {
        /* See the comment for setuid above. */
@@ -2645,6 +2752,16 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
        return task_has_perm(current,p, PROCESS__SETSCHED);
 }
 
+static int selinux_task_setioprio(struct task_struct *p, int ioprio)
+{
+       return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_getioprio(struct task_struct *p)
+{
+       return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
 static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
 {
        struct rlimit *old_rlim = current->signal->rlim + resource;
@@ -2674,12 +2791,19 @@ static int selinux_task_getscheduler(struct task_struct *p)
        return task_has_perm(current, p, PROCESS__GETSCHED);
 }
 
-static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
+static int selinux_task_movememory(struct task_struct *p)
+{
+       return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
+                               int sig, u32 secid)
 {
        u32 perm;
        int rc;
+       struct task_security_struct *tsec;
 
-       rc = secondary_ops->task_kill(p, info, sig);
+       rc = secondary_ops->task_kill(p, info, sig, secid);
        if (rc)
                return rc;
 
@@ -2690,8 +2814,12 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int si
                perm = PROCESS__SIGNULL; /* null signal; existence test */
        else
                perm = signal_to_av(sig);
-
-       return task_has_perm(current, p, perm);
+       tsec = p->security;
+       if (secid)
+               rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+       else
+               rc = task_has_perm(current, p, perm);
+       return rc;
 }
 
 static int selinux_task_prctl(int option,
@@ -2916,12 +3044,14 @@ static int selinux_socket_create(int family, int type,
 {
        int err = 0;
        struct task_security_struct *tsec;
+       u32 newsid;
 
        if (kern)
                goto out;
 
        tsec = current->security;
-       err = avc_has_perm(tsec->sid, tsec->sid,
+       newsid = tsec->sockcreate_sid ? : tsec->sid;
+       err = avc_has_perm(tsec->sid, newsid,
                           socket_type_to_security_class(family, type,
                           protocol), SOCKET__CREATE, NULL);
 
@@ -2934,12 +3064,14 @@ static void selinux_socket_post_create(struct socket *sock, int family,
 {
        struct inode_security_struct *isec;
        struct task_security_struct *tsec;
+       u32 newsid;
 
        isec = SOCK_INODE(sock)->i_security;
 
        tsec = current->security;
+       newsid = tsec->sockcreate_sid ? : tsec->sid;
        isec->sclass = socket_type_to_security_class(family, type, protocol);
-       isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
+       isec->sid = kern ? SECINITSID_KERNEL : newsid;
        isec->initialized = 1;
 
        return;
@@ -3391,7 +3523,13 @@ out:
 static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen)
 {
        int err = 0;
-       u32 peer_sid = selinux_socket_getpeer_dgram(skb);
+       u32 peer_sid;
+
+       if (skb->sk->sk_family == PF_UNIX)
+               selinux_get_inode_sid(SOCK_INODE(skb->sk->sk_socket),
+                                     &peer_sid);
+       else
+               peer_sid = selinux_socket_getpeer_dgram(skb);
 
        if (peer_sid == SECSID_NULL)
                return -EINVAL;
@@ -3403,8 +3541,6 @@ static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata,
        return 0;
 }
 
-
-
 static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
 {
        return sk_alloc_security(sk, family, priority);
@@ -3612,32 +3748,32 @@ static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
 
 static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-       struct task_security_struct *tsec;
-       struct av_decision avd;
        int err;
 
        err = secondary_ops->netlink_send(sk, skb);
        if (err)
                return err;
 
-       tsec = current->security;
-
-       avd.allowed = 0;
-       avc_has_perm_noaudit(tsec->sid, tsec->sid,
-                               SECCLASS_CAPABILITY, ~0, &avd);
-       cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
-
        if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
                err = selinux_nlmsg_perm(sk, skb);
 
        return err;
 }
 
-static int selinux_netlink_recv(struct sk_buff *skb)
+static int selinux_netlink_recv(struct sk_buff *skb, int capability)
 {
-       if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
-               return -EPERM;
-       return 0;
+       int err;
+       struct avc_audit_data ad;
+
+       err = secondary_ops->netlink_recv(skb, capability);
+       if (err)
+               return err;
+
+       AVC_AUDIT_DATA_INIT(&ad, CAP);
+       ad.u.cap = capability;
+
+       return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
+                           SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad);
 }
 
 static int ipc_alloc_security(struct task_struct *task,
@@ -4140,6 +4276,10 @@ static int selinux_getprocattr(struct task_struct *p,
                sid = tsec->exec_sid;
        else if (!strcmp(name, "fscreate"))
                sid = tsec->create_sid;
+       else if (!strcmp(name, "keycreate"))
+               sid = tsec->keycreate_sid;
+       else if (!strcmp(name, "sockcreate"))
+               sid = tsec->sockcreate_sid;
        else
                return -EINVAL;
 
@@ -4172,6 +4312,10 @@ static int selinux_setprocattr(struct task_struct *p,
                error = task_has_perm(current, p, PROCESS__SETEXEC);
        else if (!strcmp(name, "fscreate"))
                error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+       else if (!strcmp(name, "keycreate"))
+               error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+       else if (!strcmp(name, "sockcreate"))
+               error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
        else if (!strcmp(name, "current"))
                error = task_has_perm(current, p, PROCESS__SETCURRENT);
        else
@@ -4201,6 +4345,13 @@ static int selinux_setprocattr(struct task_struct *p,
                tsec->exec_sid = sid;
        else if (!strcmp(name, "fscreate"))
                tsec->create_sid = sid;
+       else if (!strcmp(name, "keycreate")) {
+               error = may_create_key(sid, p);
+               if (error)
+                       return error;
+               tsec->keycreate_sid = sid;
+       } else if (!strcmp(name, "sockcreate"))
+               tsec->sockcreate_sid = sid;
        else if (!strcmp(name, "current")) {
                struct av_decision avd;
 
@@ -4254,7 +4405,8 @@ static int selinux_setprocattr(struct task_struct *p,
 
 #ifdef CONFIG_KEYS
 
-static int selinux_key_alloc(struct key *k, struct task_struct *tsk)
+static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+                            unsigned long flags)
 {
        struct task_security_struct *tsec = tsk->security;
        struct key_security_struct *ksec;
@@ -4264,7 +4416,10 @@ static int selinux_key_alloc(struct key *k, struct task_struct *tsk)
                return -ENOMEM;
 
        ksec->obj = k;
-       ksec->sid = tsec->sid;
+       if (tsec->keycreate_sid)
+               ksec->sid = tsec->keycreate_sid;
+       else
+               ksec->sid = tsec->sid;
        k->security = ksec;
 
        return 0;
@@ -4381,11 +4536,15 @@ static struct security_operations selinux_ops = {
        .task_setpgid =                 selinux_task_setpgid,
        .task_getpgid =                 selinux_task_getpgid,
        .task_getsid =                  selinux_task_getsid,
+       .task_getsecid =                selinux_task_getsecid,
        .task_setgroups =               selinux_task_setgroups,
        .task_setnice =                 selinux_task_setnice,
+       .task_setioprio =               selinux_task_setioprio,
+       .task_getioprio =               selinux_task_getioprio,
        .task_setrlimit =               selinux_task_setrlimit,
        .task_setscheduler =            selinux_task_setscheduler,
        .task_getscheduler =            selinux_task_getscheduler,
+       .task_movememory =              selinux_task_movememory,
        .task_kill =                    selinux_task_kill,
        .task_wait =                    selinux_task_wait,
        .task_prctl =                   selinux_task_prctl,
@@ -4501,8 +4660,10 @@ static __init int selinux_init(void)
 
 #ifdef CONFIG_KEYS
        /* Add security information to initial keyrings */
-       security_key_alloc(&root_user_keyring, current);
-       security_key_alloc(&root_session_keyring, current);
+       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;