]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - security/selinux/hooks.c
r8169: perform a PHY reset before any other operation at boot time
[linux-2.6-omap-h63xx.git] / security / selinux / hooks.c
index 1dc935f7b919ea4f9a0beabd9a45b249fb827559..28ee187ed2249423e17ad4542e9017b458ffe1ba 100644 (file)
@@ -12,6 +12,8 @@
  *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *                          <dgoeddel@trustedcs.com>
+ *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
+ *                     Paul Moore, <paul.moore@hp.com>
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
@@ -49,7 +51,6 @@
 #include <net/ip.h>            /* for sysctl_local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 #include <asm/ioctls.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/audit.h>
 #include <linux/string.h>
 #include <linux/selinux.h>
+#include <linux/mutex.h>
 
 #include "avc.h"
 #include "objsec.h"
 #include "netif.h"
 #include "xfrm.h"
+#include "selinux_netlabel.h"
 
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -182,7 +185,7 @@ static int inode_alloc_security(struct inode *inode)
                return -ENOMEM;
 
        memset(isec, 0, sizeof(*isec));
-       init_MUTEX(&isec->sem);
+       mutex_init(&isec->lock);
        INIT_LIST_HEAD(&isec->list);
        isec->inode = inode;
        isec->sid = SECINITSID_UNLABELED;
@@ -239,7 +242,7 @@ static int superblock_alloc_security(struct super_block *sb)
        if (!sbsec)
                return -ENOMEM;
 
-       init_MUTEX(&sbsec->sem);
+       mutex_init(&sbsec->lock);
        INIT_LIST_HEAD(&sbsec->list);
        INIT_LIST_HEAD(&sbsec->isec_head);
        spin_lock_init(&sbsec->isec_lock);
@@ -278,6 +281,8 @@ static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
        ssec->sid = SECINITSID_UNLABELED;
        sk->sk_security = ssec;
 
+       selinux_netlbl_sk_security_init(ssec, family);
+
        return 0;
 }
 
@@ -393,7 +398,7 @@ static int try_context_mount(struct super_block *sb, void *data)
                /* Standard string-based options. */
                char *p, *options = data;
 
-               while ((p = strsep(&options, ",")) != NULL) {
+               while ((p = strsep(&options, "|")) != NULL) {
                        int token;
                        substring_t args[MAX_OPT_ARGS];
 
@@ -589,7 +594,7 @@ static int superblock_doinit(struct super_block *sb, void *data)
        struct inode *inode = root->d_inode;
        int rc = 0;
 
-       down(&sbsec->sem);
+       mutex_lock(&sbsec->lock);
        if (sbsec->initialized)
                goto out;
 
@@ -684,7 +689,7 @@ next_inode:
        }
        spin_unlock(&sbsec->isec_lock);
 out:
-       up(&sbsec->sem);
+       mutex_unlock(&sbsec->lock);
        return rc;
 }
 
@@ -838,15 +843,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        char *context = NULL;
        unsigned len = 0;
        int rc = 0;
-       int hold_sem = 0;
 
        if (isec->initialized)
                goto out;
 
-       down(&isec->sem);
-       hold_sem = 1;
+       mutex_lock(&isec->lock);
        if (isec->initialized)
-               goto out;
+               goto out_unlock;
 
        sbsec = inode->i_sb->s_security;
        if (!sbsec->initialized) {
@@ -857,7 +860,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                if (list_empty(&isec->list))
                        list_add(&isec->list, &sbsec->isec_head);
                spin_unlock(&sbsec->isec_lock);
-               goto out;
+               goto out_unlock;
        }
 
        switch (sbsec->behavior) {
@@ -880,7 +883,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        printk(KERN_WARNING "%s:  no dentry for dev=%s "
                               "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id,
                               inode->i_ino);
-                       goto out;
+                       goto out_unlock;
                }
 
                len = INITCONTEXTLEN;
@@ -888,7 +891,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                if (!context) {
                        rc = -ENOMEM;
                        dput(dentry);
-                       goto out;
+                       goto out_unlock;
                }
                rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
                                           context, len);
@@ -898,7 +901,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                                   NULL, 0);
                        if (rc < 0) {
                                dput(dentry);
-                               goto out;
+                               goto out_unlock;
                        }
                        kfree(context);
                        len = rc;
@@ -906,7 +909,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        if (!context) {
                                rc = -ENOMEM;
                                dput(dentry);
-                               goto out;
+                               goto out_unlock;
                        }
                        rc = inode->i_op->getxattr(dentry,
                                                   XATTR_NAME_SELINUX,
@@ -919,7 +922,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                       "%d for dev=%s ino=%ld\n", __FUNCTION__,
                                       -rc, inode->i_sb->s_id, inode->i_ino);
                                kfree(context);
-                               goto out;
+                               goto out_unlock;
                        }
                        /* Map ENODATA to the default file SID */
                        sid = sbsec->def_sid;
@@ -955,7 +958,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                             isec->sclass,
                                             &sid);
                if (rc)
-                       goto out;
+                       goto out_unlock;
                isec->sid = sid;
                break;
        case SECURITY_FS_USE_MNTPOINT:
@@ -973,7 +976,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                                          isec->sclass,
                                                          &sid);
                                if (rc)
-                                       goto out;
+                                       goto out_unlock;
                                isec->sid = sid;
                        }
                }
@@ -982,12 +985,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
 
        isec->initialized = 1;
 
+out_unlock:
+       mutex_unlock(&isec->lock);
 out:
        if (isec->sclass == SECCLASS_FILE)
                isec->sclass = inode_mode_to_security_class(inode->i_mode);
-
-       if (hold_sem)
-               up(&isec->sem);
        return rc;
 }
 
@@ -1359,25 +1361,6 @@ static inline u32 file_to_av(struct file *file)
        return av;
 }
 
-/* Set an inode's SID to a specified value. */
-static int inode_security_set_sid(struct inode *inode, u32 sid)
-{
-       struct inode_security_struct *isec = inode->i_security;
-       struct superblock_security_struct *sbsec = inode->i_sb->s_security;
-
-       if (!sbsec->initialized) {
-               /* Defer initialization to selinux_complete_init. */
-               return 0;
-       }
-
-       down(&isec->sem);
-       isec->sclass = inode_mode_to_security_class(inode->i_mode);
-       isec->sid = sid;
-       isec->initialized = 1;
-       up(&isec->sem);
-       return 0;
-}
-
 /* Hook functions begin here. */
 
 static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
@@ -1706,10 +1689,12 @@ static inline void flush_unauthorized_files(struct files_struct * files)
 {
        struct avc_audit_data ad;
        struct file *file, *devnull = NULL;
-       struct tty_struct *tty = current->signal->tty;
+       struct tty_struct *tty;
        struct fdtable *fdt;
        long j = -1;
 
+       mutex_lock(&tty_mutex);
+       tty = current->signal->tty;
        if (tty) {
                file_list_lock();
                file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list);
@@ -1729,6 +1714,7 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                }
                file_list_unlock();
        }
+       mutex_unlock(&tty_mutex);
 
        /* Revalidate access to inherited open files. */
 
@@ -1768,7 +1754,8 @@ static inline void flush_unauthorized_files(struct files_struct * files)
                                                get_file(devnull);
                                        } else {
                                                devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
-                                               if (!devnull) {
+                                               if (IS_ERR(devnull)) {
+                                                       devnull = NULL;
                                                        put_unused_fd(fd);
                                                        fput(file);
                                                        continue;
@@ -1937,18 +1924,40 @@ static inline void take_option(char **to, char *from, int *first, int len)
        if (!*first) {
                **to = ',';
                *to += 1;
-       }
-       else
+       } else
                *first = 0;
        memcpy(*to, from, len);
        *to += len;
 }
 
+static inline void take_selinux_option(char **to, char *from, int *first, 
+                                      int len)
+{
+       int current_size = 0;
+
+       if (!*first) {
+               **to = '|';
+               *to += 1;
+       }
+       else
+               *first = 0;
+
+       while (current_size < len) {
+               if (*from != '"') {
+                       **to = *from;
+                       *to += 1;
+               }
+               from += 1;
+               current_size += 1;
+       }
+}
+
 static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy)
 {
        int fnosec, fsec, rc = 0;
        char *in_save, *in_curr, *in_end;
        char *sec_curr, *nosec_save, *nosec;
+       int open_quote = 0;
 
        in_curr = orig;
        sec_curr = copy;
@@ -1970,11 +1979,14 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void
        in_save = in_end = orig;
 
        do {
-               if (*in_end == ',' || *in_end == '\0') {
+               if (*in_end == '"')
+                       open_quote = !open_quote;
+               if ((*in_end == ',' && open_quote == 0) ||
+                               *in_end == '\0') {
                        int len = in_end - in_curr;
 
                        if (selinux_option(in_curr, len))
-                               take_option(&sec_curr, in_curr, &fsec, len);
+                               take_selinux_option(&sec_curr, in_curr, &fsec, len);
                        else
                                take_option(&nosec, in_curr, &fnosec, len);
 
@@ -2086,7 +2098,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                }
        }
 
-       inode_security_set_sid(inode, newsid);
+       /* Possibly defer initialization to selinux_complete_init. */
+       if (sbsec->initialized) {
+               struct inode_security_struct *isec = inode->i_security;
+               isec->sclass = inode_mode_to_security_class(inode->i_mode);
+               isec->sid = newsid;
+               isec->initialized = 1;
+       }
 
        if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
                return -EOPNOTSUPP;
@@ -2395,6 +2413,7 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
 
 static int selinux_file_permission(struct file *file, int mask)
 {
+       int rc;
        struct inode *inode = file->f_dentry->d_inode;
 
        if (!mask) {
@@ -2406,8 +2425,12 @@ static int selinux_file_permission(struct file *file, int mask)
        if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
                mask |= MAY_APPEND;
 
-       return file_has_perm(current, file,
-                            file_mask_to_av(inode->i_mode, mask));
+       rc = file_has_perm(current, file,
+                          file_mask_to_av(inode->i_mode, mask));
+       if (rc)
+               return rc;
+
+       return selinux_netlbl_inode_permission(inode, mask);
 }
 
 static int selinux_file_alloc_security(struct file *file)
@@ -3058,9 +3081,10 @@ out:
        return err;
 }
 
-static void selinux_socket_post_create(struct socket *sock, int family,
-                                      int type, int protocol, int kern)
+static int selinux_socket_post_create(struct socket *sock, int family,
+                                     int type, int protocol, int kern)
 {
+       int err = 0;
        struct inode_security_struct *isec;
        struct task_security_struct *tsec;
        struct sk_security_struct *sksec;
@@ -3077,9 +3101,12 @@ static void selinux_socket_post_create(struct socket *sock, int family,
        if (sock->sk) {
                sksec = sock->sk->sk_security;
                sksec->sid = isec->sid;
+               err = selinux_netlbl_socket_post_create(sock,
+                                                       family,
+                                                       isec->sid);
        }
 
-       return;
+       return err;
 }
 
 /* Range of port numbers used to automatically bind.
@@ -3260,7 +3287,13 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
 static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                  int size)
 {
-       return socket_has_perm(current, sock, SOCKET__WRITE);
+       int rc;
+
+       rc = socket_has_perm(current, sock, SOCKET__WRITE);
+       if (rc)
+               return rc;
+
+       return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
 }
 
 static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
@@ -3281,7 +3314,13 @@ static int selinux_socket_getpeername(struct socket *sock)
 
 static int selinux_socket_setsockopt(struct socket *sock,int level,int optname)
 {
-       return socket_has_perm(current, sock, SOCKET__SETOPT);
+       int err;
+
+       err = socket_has_perm(current, sock, SOCKET__SETOPT);
+       if (err)
+               return err;
+
+       return selinux_netlbl_socket_setsockopt(sock, level, optname);
 }
 
 static int selinux_socket_getsockopt(struct socket *sock, int level,
@@ -3468,6 +3507,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                goto out;
 
+       err = selinux_netlbl_sock_rcv_skb(sksec, skb, &ad);
+       if (err)
+               goto out;
+
        err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
 out:   
        return err;
@@ -3491,8 +3534,9 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
                peer_sid = ssec->peer_sid;
        }
        else if (isec->sclass == SECCLASS_TCP_SOCKET) {
-               peer_sid = selinux_socket_getpeer_stream(sock->sk);
-
+               peer_sid = selinux_netlbl_socket_getpeersec_stream(sock);
+               if (peer_sid == SECSID_NULL)
+                       peer_sid = selinux_socket_getpeer_stream(sock->sk);
                if (peer_sid == SECSID_NULL) {
                        err = -ENOPROTOOPT;
                        goto out;
@@ -3532,8 +3576,11 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
 
        if (sock && (sock->sk->sk_family == PF_UNIX))
                selinux_get_inode_sid(SOCK_INODE(sock), &peer_secid);
-       else if (skb)
-               peer_secid = selinux_socket_getpeer_dgram(skb);
+       else if (skb) {
+               peer_secid = selinux_netlbl_socket_getpeersec_dgram(skb);
+               if (peer_secid == SECSID_NULL)
+                       peer_secid = selinux_socket_getpeer_dgram(skb);
+       }
 
        if (peer_secid == SECSID_NULL)
                err = -EINVAL;
@@ -3559,6 +3606,8 @@ static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
 
        newssec->sid = ssec->sid;
        newssec->peer_sid = ssec->peer_sid;
+
+       selinux_netlbl_sk_clone_security(ssec, newssec);
 }
 
 static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
@@ -3572,25 +3621,40 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
        }
 }
 
-void selinux_sock_graft(struct sock* sk, struct socket *parent)
+static void selinux_sock_graft(struct sock* sk, struct socket *parent)
 {
        struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
        struct sk_security_struct *sksec = sk->sk_security;
 
-       isec->sid = sksec->sid;
+       if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
+           sk->sk_family == PF_UNIX)
+               isec->sid = sksec->sid;
+
+       selinux_netlbl_sock_graft(sk, parent);
 }
 
-int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
-                                          struct request_sock *req)
+static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+                                    struct request_sock *req)
 {
        struct sk_security_struct *sksec = sk->sk_security;
        int err;
-       u32 newsid = 0;
+       u32 newsid;
        u32 peersid;
 
+       newsid = selinux_netlbl_inet_conn_request(skb, sksec->sid);
+       if (newsid != SECSID_NULL) {
+               req->secid = newsid;
+               return 0;
+       }
+
        err = selinux_xfrm_decode_session(skb, &peersid, 0);
        BUG_ON(err);
 
+       if (peersid == SECSID_NULL) {
+               req->secid = sksec->sid;
+               return 0;
+       }
+
        err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
        if (err)
                return err;
@@ -3599,7 +3663,8 @@ int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
-void selinux_inet_csk_clone(struct sock *newsk, const struct request_sock *req)
+static void selinux_inet_csk_clone(struct sock *newsk,
+                                  const struct request_sock *req)
 {
        struct sk_security_struct *newsksec = newsk->sk_security;
 
@@ -3608,9 +3673,12 @@ void selinux_inet_csk_clone(struct sock *newsk, const struct request_sock *req)
           new socket in sync, but we don't have the isec available yet.
           So we will wait until sock_graft to do it, by which
           time it will have been created and available. */
+
+       selinux_netlbl_sk_security_init(newsksec, req->rsk_ops->family);
 }
 
-void selinux_req_classify_flow(const struct request_sock *req, struct flowi *fl)
+static void selinux_req_classify_flow(const struct request_sock *req,
+                                     struct flowi *fl)
 {
        fl->secid = req->secid;
 }