security/ cleanups
[firefly-linux-kernel-4.4.55.git] / security / selinux / hooks.c
index 0fac6829c63a7815016f0489c83db5419b990c0a..24e1b1885de7c5b379b7c19c9cf7b760e1664c41 100644 (file)
  *                          <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
  *                     Paul Moore, <paul.moore@hp.com>
+ *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ *                     Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
  *      as published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
@@ -47,7 +48,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
 #include <net/icmp.h>
-#include <net/ip.h>            /* for sysctl_local_port_range[] */
+#include <net/ip.h>            /* for local_port_range[] */
 #include <net/tcp.h>           /* struct or_callable used in sock_rcv_skb */
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
@@ -84,6 +85,7 @@
 extern unsigned int policydb_loaded_version;
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
 extern int selinux_compat_net;
+extern struct security_operations *security_ops;
 
 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 int selinux_enforcing = 0;
@@ -316,6 +318,7 @@ static inline int inode_doinit(struct inode *inode)
 }
 
 enum {
+       Opt_error = -1,
        Opt_context = 1,
        Opt_fscontext = 2,
        Opt_defcontext = 4,
@@ -327,6 +330,7 @@ static match_table_t tokens = {
        {Opt_fscontext, "fscontext=%s"},
        {Opt_defcontext, "defcontext=%s"},
        {Opt_rootcontext, "rootcontext=%s"},
+       {Opt_error, NULL},
 };
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
@@ -1584,7 +1588,7 @@ static int selinux_syslog(int type)
  * Do not audit the selinux permission check, as this is applied to all
  * processes that allocate mappings.
  */
-static int selinux_vm_enough_memory(long pages)
+static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 {
        int rc, cap_sys_admin = 0;
        struct task_security_struct *tsec = current->security;
@@ -1600,7 +1604,7 @@ static int selinux_vm_enough_memory(long pages)
        if (rc == 0)
                cap_sys_admin = 1;
 
-       return __vm_enough_memory(pages, cap_sys_admin);
+       return __vm_enough_memory(mm, pages, cap_sys_admin);
 }
 
 /* binprm security operations */
@@ -1907,6 +1911,9 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
                spin_unlock_irq(&current->sighand->siglock);
        }
 
+       /* Always clear parent death signal on SID transitions. */
+       current->pdeath_signal = 0;
+
        /* Check whether the new SID can inherit resource limits
           from the old SID.  If not, reset all soft limits to
           the lower of the current task's hard limit and the init
@@ -2290,6 +2297,25 @@ 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)
+{
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+                    sizeof XATTR_SECURITY_PREFIX - 1)) {
+               if (!strcmp(name, XATTR_NAME_CAPS)) {
+                       if (!capable(CAP_SETFCAP))
+                               return -EPERM;
+               } else if (!capable(CAP_SYS_ADMIN)) {
+                       /* A different attribute in the security namespace.
+                          Restrict to administrator. */
+                       return -EPERM;
+               }
+       }
+
+       /* Not an attribute we recognize, so just check the
+          ordinary setattr permission. */
+       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)
 {
        struct task_security_struct *tsec = current->security;
@@ -2300,19 +2326,8 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value
        u32 newsid;
        int rc = 0;
 
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
        if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
@@ -2386,31 +2401,14 @@ static int selinux_inode_listxattr (struct dentry *dentry)
 
 static int selinux_inode_removexattr (struct dentry *dentry, char *name)
 {
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. Might want a separate
-                  permission for removexattr. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        /* No one is allowed to remove a SELinux security label.
           You can change the label, but all data must be labeled. */
        return -EACCES;
 }
 
-static const char *selinux_inode_xattr_getsuffix(void)
-{
-      return XATTR_SELINUX_SUFFIX;
-}
-
 /*
  * 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
@@ -2457,9 +2455,19 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
        return len;
 }
 
+static int selinux_inode_need_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_need_killpriv(dentry);
+}
+
+static int selinux_inode_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_killpriv(dentry);
+}
+
 /* file security operations */
 
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_revalidate_file_permission(struct file *file, int mask)
 {
        int rc;
        struct inode *inode = file->f_path.dentry->d_inode;
@@ -2481,6 +2489,25 @@ static int selinux_file_permission(struct file *file, int mask)
        return selinux_netlbl_inode_permission(inode, mask);
 }
 
+static int selinux_file_permission(struct file *file, int mask)
+{
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct task_security_struct *tsec = current->security;
+       struct file_security_struct *fsec = file->f_security;
+       struct inode_security_struct *isec = inode->i_security;
+
+       if (!mask) {
+               /* No permission to check.  Existence test. */
+               return 0;
+       }
+
+       if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+           && fsec->pseqno == avc_policy_seqno())
+               return selinux_netlbl_inode_permission(inode, mask);
+
+       return selinux_revalidate_file_permission(file, mask);
+}
+
 static int selinux_file_alloc_security(struct file *file)
 {
        return file_alloc_security(file);
@@ -2720,6 +2747,34 @@ static int selinux_file_receive(struct file *file)
        return file_has_perm(current, file, file_to_av(file));
 }
 
+static int selinux_dentry_open(struct file *file)
+{
+       struct file_security_struct *fsec;
+       struct inode *inode;
+       struct inode_security_struct *isec;
+       inode = file->f_path.dentry->d_inode;
+       fsec = file->f_security;
+       isec = inode->i_security;
+       /*
+        * Save inode label and policy sequence number
+        * at open-time so that selinux_file_permission
+        * can determine whether revalidation is necessary.
+        * Task label is already saved in the file security
+        * struct as its SID.
+        */
+       fsec->isid = isec->sid;
+       fsec->pseqno = avc_policy_seqno();
+       /*
+        * Since the inode label or policy seqno may have changed
+        * between the selinux_inode_permission check and the saving
+        * of state above, recheck that access is still permitted.
+        * Otherwise, access might never be revalidated against the
+        * new inode label or new policy.
+        * This check is not redundant - do not remove.
+        */
+       return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
 /* task security operations */
 
 static int selinux_task_create(unsigned long clone_flags)
@@ -2828,6 +2883,12 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
+       int rc;
+
+       rc = secondary_ops->task_setioprio(p, ioprio);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
@@ -2857,6 +2918,12 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
 
 static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
 {
+       int rc;
+
+       rc = secondary_ops->task_setscheduler(p, policy, lp);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
@@ -3227,8 +3294,6 @@ static int selinux_socket_post_create(struct socket *sock, int family,
 /* Range of port numbers used to automatically bind.
    Need to determine whether we should perform a name_bind
    permission check between the socket and the port number. */
-#define ip_local_port_range_0 sysctl_local_port_range[0]
-#define ip_local_port_range_1 sysctl_local_port_range[1]
 
 static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
 {
@@ -3271,20 +3336,27 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
                        addrp = (char *)&addr6->sin6_addr.s6_addr;
                }
 
-               if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) ||
-                          snum > ip_local_port_range_1)) {
-                       err = security_port_sid(sk->sk_family, sk->sk_type,
-                                               sk->sk_protocol, snum, &sid);
-                       if (err)
-                               goto out;
-                       AVC_AUDIT_DATA_INIT(&ad,NET);
-                       ad.u.net.sport = htons(snum);
-                       ad.u.net.family = family;
-                       err = avc_has_perm(isec->sid, sid,
-                                          isec->sclass,
-                                          SOCKET__NAME_BIND, &ad);
-                       if (err)
-                               goto out;
+               if (snum) {
+                       int low, high;
+
+                       inet_get_local_port_range(&low, &high);
+
+                       if (snum < max(PROT_SOCK, low) || snum > high) {
+                               err = security_port_sid(sk->sk_family,
+                                                       sk->sk_type,
+                                                       sk->sk_protocol, snum,
+                                                       &sid);
+                               if (err)
+                                       goto out;
+                               AVC_AUDIT_DATA_INIT(&ad,NET);
+                               ad.u.net.sport = htons(snum);
+                               ad.u.net.family = family;
+                               err = avc_has_perm(isec->sid, sid,
+                                                  isec->sclass,
+                                                  SOCKET__NAME_BIND, &ad);
+                               if (err)
+                                       goto out;
+                       }
                }
                
                switch(isec->sclass) {
@@ -3922,7 +3994,7 @@ out:
 }
 
 static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
-                                              struct sk_buff **pskb,
+                                              struct sk_buff *skb,
                                               const struct net_device *in,
                                               const struct net_device *out,
                                               int (*okfn)(struct sk_buff *),
@@ -3931,7 +4003,6 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
        char *addrp;
        int len, err = 0;
        struct sock *sk;
-       struct sk_buff *skb = *pskb;
        struct avc_audit_data ad;
        struct net_device *dev = (struct net_device *)out;
        struct sk_security_struct *sksec;
@@ -3967,23 +4038,23 @@ out:
 }
 
 static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
+                                               struct sk_buff *skb,
                                                const struct net_device *in,
                                                const struct net_device *out,
                                                int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET);
+       return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET);
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 
 static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
-                                               struct sk_buff **pskb,
+                                               struct sk_buff *skb,
                                                const struct net_device *in,
                                                const struct net_device *out,
                                                int (*okfn)(struct sk_buff *))
 {
-       return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6);
+       return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET6);
 }
 
 #endif /* IPV6 */
@@ -4478,19 +4549,6 @@ static int selinux_register_security (const char *name, struct security_operatio
        return 0;
 }
 
-static int selinux_unregister_security (const char *name, struct security_operations *ops)
-{
-       if (ops != secondary_ops) {
-               printk(KERN_ERR "%s:  trying to unregister a security module "
-                       "that is not registered.\n", __FUNCTION__);
-               return -EINVAL;
-       }
-
-       secondary_ops = original_ops;
-
-       return 0;
-}
-
 static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
 {
        if (inode)
@@ -4658,8 +4716,7 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 
 static void selinux_release_secctx(char *secdata, u32 seclen)
 {
-       if (secdata)
-               kfree(secdata);
+       kfree(secdata);
 }
 
 #ifdef CONFIG_KEYS
@@ -4769,10 +4826,11 @@ static struct security_operations selinux_ops = {
        .inode_getxattr =               selinux_inode_getxattr,
        .inode_listxattr =              selinux_inode_listxattr,
        .inode_removexattr =            selinux_inode_removexattr,
-       .inode_xattr_getsuffix =        selinux_inode_xattr_getsuffix,
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
+       .inode_need_killpriv =          selinux_inode_need_killpriv,
+       .inode_killpriv =               selinux_inode_killpriv,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,
@@ -4786,6 +4844,8 @@ static struct security_operations selinux_ops = {
        .file_send_sigiotask =          selinux_file_send_sigiotask,
        .file_receive =                 selinux_file_receive,
 
+       .dentry_open =                  selinux_dentry_open,
+
        .task_create =                  selinux_task_create,
        .task_alloc_security =          selinux_task_alloc_security,
        .task_free_security =           selinux_task_free_security,
@@ -4835,7 +4895,6 @@ static struct security_operations selinux_ops = {
        .sem_semop =                    selinux_sem_semop,
 
        .register_security =            selinux_register_security,
-       .unregister_security =          selinux_unregister_security,
 
        .d_instantiate =                selinux_d_instantiate,