diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 199 |
1 files changed, 127 insertions, 72 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0753b20e23f..24e1b1885de 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -14,13 +14,14 @@ * <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; @@ -2295,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; @@ -2305,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) @@ -2391,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 @@ -2462,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; @@ -2486,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); @@ -2725,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) @@ -2833,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); } @@ -2862,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); } @@ -3232,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) { @@ -3276,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) { @@ -3927,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 *), @@ -3936,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; @@ -3972,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 */ @@ -4483,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) @@ -4773,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, @@ -4790,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, @@ -4839,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, |