X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=security%2Fselinux%2Fhooks.c;fp=security%2Fselinux%2Fhooks.c;h=b65c201e9ff50386ea6cb43ed2c0c91f63a4cfe7;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=0b32f3020703df2ed266764b87aed32b0724f4ad;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0b32f3020..b65c201e9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -18,6 +18,7 @@ * as published by the Free Software Foundation. */ +#include #include #include #include @@ -68,7 +69,6 @@ #include #include #include -#include #include "avc.h" #include "objsec.h" @@ -80,7 +80,6 @@ extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); -extern int selinux_compat_net; #ifdef CONFIG_SECURITY_SELINUX_DEVELOP int selinux_enforcing = 0; @@ -102,8 +101,6 @@ static int __init selinux_enabled_setup(char *str) return 1; } __setup("selinux=", selinux_enabled_setup); -#else -int selinux_enabled = 1; #endif /* Original (dummy) security module. */ @@ -120,34 +117,6 @@ static struct security_operations *secondary_ops = NULL; static LIST_HEAD(superblock_security_head); static DEFINE_SPINLOCK(sb_security_lock); -static kmem_cache_t *sel_inode_cache; - -/* Return security context for a given sid or just the context - length if the buffer is null or length is 0 */ -static int selinux_getsecurity(u32 sid, void *buffer, size_t size) -{ - char *context; - unsigned len; - int rc; - - rc = security_sid_to_context(sid, &context, &len); - if (rc) - return rc; - - if (!buffer || !size) - goto getsecurity_exit; - - if (size < len) { - len = -ERANGE; - goto getsecurity_exit; - } - memcpy(buffer, context, len); - -getsecurity_exit: - kfree(context); - return len; -} - /* Allocate and free functions for each kind of security blob. */ static int task_alloc_security(struct task_struct *task) @@ -177,11 +146,10 @@ static int inode_alloc_security(struct inode *inode) struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; - isec = kmem_cache_alloc(sel_inode_cache, SLAB_KERNEL); + isec = kzalloc(sizeof(struct inode_security_struct), GFP_KERNEL); if (!isec) return -ENOMEM; - memset(isec, 0, sizeof(*isec)); init_MUTEX(&isec->sem); INIT_LIST_HEAD(&isec->list); isec->inode = inode; @@ -204,7 +172,7 @@ static void inode_free_security(struct inode *inode) spin_unlock(&sbsec->isec_lock); inode->i_security = NULL; - kmem_cache_free(sel_inode_cache, isec); + kfree(isec); } static int file_alloc_security(struct file *file) @@ -246,7 +214,6 @@ 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; @@ -320,53 +287,19 @@ 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; @@ -409,7 +342,7 @@ static int try_context_mount(struct super_block *sb, void *data) switch (token) { case Opt_context: - if (seen & (Opt_context|Opt_defcontext)) { + if (seen) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; @@ -425,13 +358,13 @@ static int try_context_mount(struct super_block *sb, void *data) break; case Opt_fscontext: - if (seen & Opt_fscontext) { + if (seen & (Opt_context|Opt_fscontext)) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } - fscontext = match_strdup(&args[0]); - if (!fscontext) { + context = match_strdup(&args[0]); + if (!context) { rc = -ENOMEM; goto out_free; } @@ -440,22 +373,6 @@ 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; @@ -492,28 +409,6 @@ 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) { @@ -523,38 +418,20 @@ static int try_context_mount(struct super_block *sb, void *data) goto out_free; } - if (!fscontext) { - rc = may_context_mount_sb_relabel(sid, sbsec, tsec); - if (rc) - goto out_free; - sbsec->sid = sid; - } else { - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); - if (rc) - goto out_free; - } - 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); + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL); + if (rc) goto out_free; - } - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELTO, NULL); if (rc) goto out_free; - isec->sid = sid; - isec->initialized = 1; + sbsec->sid = sid; + + if (seen & Opt_context) + sbsec->behavior = SECURITY_FS_USE_MNTPOINT; } if (defcontext) { @@ -569,7 +446,13 @@ static int try_context_mount(struct super_block *sb, void *data) if (sid == sbsec->def_sid) goto out_free; - rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + 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); if (rc) goto out_free; @@ -580,8 +463,6 @@ out_free: if (alloc) { kfree(context); kfree(defcontext); - kfree(fscontext); - kfree(rootcontext); } out: return rc; @@ -784,8 +665,6 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_PACKET_SOCKET; case PF_KEY: return SECCLASS_KEY_SOCKET; - case PF_APPLETALK: - return SECCLASS_APPLETALK_SOCKET; } return SECCLASS_SOCKET; @@ -963,11 +842,8 @@ 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 superblock SID. */ + /* Default to the fs SID. */ isec->sid = sbsec->sid; if (sbsec->proc) { @@ -1189,17 +1065,6 @@ 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 @@ -1622,10 +1487,8 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) /* Default to the current task SID. */ bsec->sid = tsec->sid; - /* Reset fs, key, and sock SIDs on execve. */ + /* Reset create SID on execve. */ tsec->create_sid = 0; - tsec->keycreate_sid = 0; - tsec->sockcreate_sid = 0; if (tsec->exec_sid) { newsid = tsec->exec_sid; @@ -1711,13 +1574,10 @@ static inline void flush_unauthorized_files(struct files_struct * files) { struct avc_audit_data ad; struct file *file, *devnull = NULL; - struct tty_struct *tty; + struct tty_struct *tty = current->signal->tty; struct fdtable *fdt; long j = -1; - int drop_tty = 0; - mutex_lock(&tty_mutex); - tty = get_current_tty(); if (tty) { file_list_lock(); file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list); @@ -1730,16 +1590,13 @@ static inline void flush_unauthorized_files(struct files_struct * files) struct inode *inode = file->f_dentry->d_inode; if (inode_has_perm(current, inode, FILE__READ | FILE__WRITE, NULL)) { - drop_tty = 1; + /* Reset controlling tty. */ + current->signal->tty = NULL; + current->signal->tty_old_pgrp = 0; } } file_list_unlock(); - - /* Reset controlling tty. */ - if (drop_tty) - proc_set_tty(current, NULL); } - mutex_unlock(&tty_mutex); /* Revalidate access to inherited open files. */ @@ -1939,8 +1796,7 @@ 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("rootcontext=", sizeof("rootcontext=")-1, option, len)); + match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); } static inline void take_option(char **to, char *from, int *first, int len) @@ -2013,13 +1869,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 dentry *dentry) +static int selinux_sb_statfs(struct super_block *sb) { struct avc_audit_data ad; AVC_AUDIT_DATA_INIT(&ad,FS); - ad.u.fs.dentry = dentry->d_sb->s_root; - return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad); + ad.u.fs.dentry = sb->s_root; + return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad); } static int selinux_mount(char * dev_name, @@ -2073,6 +1929,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, struct task_security_struct *tsec; struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; + struct inode_security_struct *isec; u32 newsid, clen; int rc; char *namep = NULL, *context; @@ -2080,6 +1937,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, tsec = current->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; + isec = inode->i_security; if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { newsid = tsec->create_sid; @@ -2099,7 +1957,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, inode_security_set_sid(inode, newsid); - if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) + if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) return -EOPNOTSUPP; if (name) { @@ -2351,11 +2209,6 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) 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 @@ -2363,14 +2216,47 @@ static const char *selinux_inode_xattr_getsuffix(void) * * Permission check is handled by selinux_inode_getxattr hook. */ -static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err) +static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size, int err) { struct inode_security_struct *isec = inode->i_security; + char *context; + unsigned len; + int rc; - if (strcmp(name, XATTR_SELINUX_SUFFIX)) - return -EOPNOTSUPP; + if (strcmp(name, XATTR_SELINUX_SUFFIX)) { + rc = -EOPNOTSUPP; + goto out; + } - return selinux_getsecurity(isec->sid, buffer, size); + rc = security_sid_to_context(isec->sid, &context, &len); + if (rc) + goto out; + + /* Probe for required buffer size */ + if (!buffer || !size) { + rc = len; + goto out_free; + } + + if (size < len) { + rc = -ERANGE; + goto out_free; + } + + if (err > 0) { + if ((len == err) && !(memcmp(context, buffer, len))) { + /* Don't need to canonicalize value */ + rc = err; + goto out_free; + } + memset(buffer, 0, size); + } + memcpy(buffer, context, len); + rc = len; +out_free: + kfree(context); +out: + return rc; } static int selinux_inode_setsecurity(struct inode *inode, const char *name, @@ -2479,6 +2365,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, static int file_map_prot_check(struct file *file, unsigned long prot, int shared) { +#ifndef CONFIG_PPC32 if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) { /* * We are making executable an anonymous mapping or a @@ -2489,6 +2376,7 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared if (rc) return rc; } +#endif if (file) { /* read access is always possible with a mapping */ @@ -2535,6 +2423,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, if (selinux_checkreqprot) prot = reqprot; +#ifndef CONFIG_PPC32 if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { rc = 0; if (vma->vm_start >= vma->vm_mm->start_brk && @@ -2559,6 +2448,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, if (rc) return rc; } +#endif return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); } @@ -2680,11 +2570,9 @@ static int selinux_task_alloc_security(struct task_struct *tsk) tsec2->osid = tsec1->osid; tsec2->sid = tsec1->sid; - /* Retain the exec, fs, key, and sock SIDs across fork */ + /* Retain the exec and create 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 @@ -2736,11 +2624,6 @@ 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. */ @@ -2758,16 +2641,6 @@ 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; @@ -2797,19 +2670,12 @@ static int selinux_task_getscheduler(struct task_struct *p) return task_has_perm(current, p, PROCESS__GETSCHED); } -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) +static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) { u32 perm; int rc; - struct task_security_struct *tsec; - rc = secondary_ops->task_kill(p, info, sig, secid); + rc = secondary_ops->task_kill(p, info, sig); if (rc) return rc; @@ -2820,12 +2686,8 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, perm = PROCESS__SIGNULL; /* null signal; existence test */ else perm = signal_to_av(sig); - 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; + + return task_has_perm(current, p, perm); } static int selinux_task_prctl(int option, @@ -3050,14 +2912,12 @@ 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; - newsid = tsec->sockcreate_sid ? : tsec->sid; - err = avc_has_perm(tsec->sid, newsid, + err = avc_has_perm(tsec->sid, tsec->sid, socket_type_to_security_class(family, type, protocol), SOCKET__CREATE, NULL); @@ -3070,14 +2930,12 @@ 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 : newsid; + isec->sid = kern ? SECINITSID_KERNEL : tsec->sid; isec->initialized = 1; return; @@ -3355,17 +3213,47 @@ static int selinux_socket_unix_may_send(struct socket *sock, return 0; } -static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, - struct avc_audit_data *ad, u32 sock_sid, u16 sock_class, - u16 family, char *addrp, int len) +static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { - int err = 0; + u16 family; + char *addrp; + int len, err = 0; u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; + u32 sock_sid = 0; + u16 sock_class = 0; + struct socket *sock; + struct net_device *dev; + struct avc_audit_data ad; + + family = sk->sk_family; + if (family != PF_INET && family != PF_INET6) + goto out; + + /* Handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) + family = PF_INET; + + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (sock) { + struct inode *inode; + inode = SOCK_INODE(sock); + if (inode) { + struct inode_security_struct *isec; + isec = inode->i_security; + sock_sid = isec->sid; + sock_class = isec->sclass; + } + } + read_unlock_bh(&sk->sk_callback_lock); + if (!sock_sid) + goto out; - if (!skb->dev) + dev = skb->dev; + if (!dev) goto out; - err = sel_netif_sids(skb->dev, &if_sid, NULL); + err = sel_netif_sids(dev, &if_sid, NULL); if (err) goto out; @@ -3388,124 +3276,66 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, break; } - err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = dev->name; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); + if (err) + goto out; + + err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); if (err) goto out; + /* Fixme: this lookup is inefficient */ err = security_node_sid(family, addrp, len, &node_sid); if (err) goto out; - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); if (err) goto out; if (recv_perm) { u32 port_sid; + /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, ntohs(ad->u.net.sport), + sk->sk_protocol, ntohs(ad.u.net.sport), &port_sid); if (err) goto out; err = avc_has_perm(sock_sid, port_sid, - sock_class, recv_perm, ad); + sock_class, recv_perm, &ad); } -out: - return err; -} - -static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) -{ - u16 family; - u16 sock_class = 0; - char *addrp; - int len, err = 0; - u32 sock_sid = 0; - struct socket *sock; - struct avc_audit_data ad; - - family = sk->sk_family; - if (family != PF_INET && family != PF_INET6) - goto out; + if (!err) + err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); - /* Handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) - family = PF_INET; - - read_lock_bh(&sk->sk_callback_lock); - sock = sk->sk_socket; - if (sock) { - struct inode *inode; - inode = SOCK_INODE(sock); - if (inode) { - struct inode_security_struct *isec; - isec = inode->i_security; - sock_sid = isec->sid; - sock_class = isec->sclass; - } - } - read_unlock_bh(&sk->sk_callback_lock); - if (!sock_sid) - goto out; - - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); - if (err) - goto out; - - if (selinux_compat_net) - err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid, - sock_class, family, - addrp, len); - else - err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, &ad); - if (err) - goto out; - - err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); out: return err; } -static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, + int __user *optlen, unsigned len) { int err = 0; char *scontext; u32 scontext_len; struct sk_security_struct *ssec; struct inode_security_struct *isec; - u32 peer_sid = 0; isec = SOCK_INODE(sock)->i_security; - - /* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */ - if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) { - ssec = sock->sk->sk_security; - peer_sid = ssec->peer_sid; - } - else if (isec->sclass == SECCLASS_TCP_SOCKET) { - peer_sid = selinux_socket_getpeer_stream(sock->sk); - - if (peer_sid == SECSID_NULL) { - err = -ENOPROTOOPT; - goto out; - } - } - else { + if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { err = -ENOPROTOOPT; goto out; } - err = security_sid_to_context(peer_sid, &scontext, &scontext_len); - + ssec = sock->sk->sk_security; + + err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); if (err) goto out; @@ -3526,23 +3356,6 @@ out: return err; } -static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) -{ - u32 peer_secid = SECSID_NULL; - int err = 0; - - 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); - - if (peer_secid == SECSID_NULL) - err = -EINVAL; - *secid = peer_secid; - - return err; -} - static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { return sk_alloc_security(sk, family, priority); @@ -3609,18 +3422,42 @@ out: #ifdef CONFIG_NETFILTER -static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, - struct inode_security_struct *isec, - struct avc_audit_data *ad, - u16 family, char *addrp, int len) +static unsigned int selinux_ip_postroute_last(unsigned int hooknum, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *), + u16 family) { - int err; + char *addrp; + int len, err = NF_ACCEPT; u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; + struct sock *sk; + struct socket *sock; + struct inode *inode; + struct sk_buff *skb = *pskb; + struct inode_security_struct *isec; + struct avc_audit_data ad; + struct net_device *dev = (struct net_device *)out; + sk = skb->sk; + if (!sk) + goto out; + + sock = sk->sk_socket; + if (!sock) + goto out; + + inode = SOCK_INODE(sock); + if (!inode) + goto out; + err = sel_netif_sids(dev, &if_sid, NULL); if (err) goto out; + isec = inode->i_security; + switch (isec->sclass) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_SEND; @@ -3640,88 +3477,55 @@ static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device * break; } - err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad); - if (err) + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = dev->name; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, + &len, 0) ? NF_DROP : NF_ACCEPT; + if (err != NF_ACCEPT) + goto out; + + err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, + netif_perm, &ad) ? NF_DROP : NF_ACCEPT; + if (err != NF_ACCEPT) goto out; - err = security_node_sid(family, addrp, len, &node_sid); - if (err) + /* Fixme: this lookup is inefficient */ + err = security_node_sid(family, addrp, len, + &node_sid) ? NF_DROP : NF_ACCEPT; + if (err != NF_ACCEPT) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad); - if (err) + err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, + node_perm, &ad) ? NF_DROP : NF_ACCEPT; + if (err != NF_ACCEPT) goto out; if (send_perm) { u32 port_sid; + /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, sk->sk_protocol, - ntohs(ad->u.net.dport), - &port_sid); - if (err) + ntohs(ad.u.net.dport), + &port_sid) ? NF_DROP : NF_ACCEPT; + if (err != NF_ACCEPT) goto out; err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, ad); + send_perm, &ad) ? NF_DROP : NF_ACCEPT; } -out: - return err; -} - -static unsigned int selinux_ip_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *), - u16 family) -{ - char *addrp; - int len, err = 0; - struct sock *sk; - struct socket *sock; - struct inode *inode; - struct sk_buff *skb = *pskb; - struct inode_security_struct *isec; - struct avc_audit_data ad; - struct net_device *dev = (struct net_device *)out; - - sk = skb->sk; - if (!sk) - goto out; - sock = sk->sk_socket; - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; - - isec = inode->i_security; - - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); - if (err) - goto out; - - if (selinux_compat_net) - err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad, - family, addrp, len); - else - err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET, - PACKET__SEND, &ad); - - if (err) + if (err != NF_ACCEPT) goto out; err = selinux_xfrm_postroute_last(isec->sid, skb); + out: - return err ? NF_DROP : NF_ACCEPT; + return err; } static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, @@ -3750,32 +3554,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, int capability) +static int selinux_netlink_recv(struct sk_buff *skb) { - 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); + if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) + return -EPERM; + return 0; } static int ipc_alloc_security(struct task_struct *task, @@ -4259,7 +4063,8 @@ static int selinux_getprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - u32 sid; + u32 sid, len; + char *context; int error; if (current != p) { @@ -4268,6 +4073,9 @@ static int selinux_getprocattr(struct task_struct *p, return error; } + if (!size) + return -ERANGE; + tsec = p->security; if (!strcmp(name, "current")) @@ -4278,17 +4086,22 @@ 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; if (!sid) return 0; - return selinux_getsecurity(sid, value, size); + error = security_sid_to_context(sid, &context, &len); + if (error) + return error; + if (len > size) { + kfree(context); + return -ERANGE; + } + memcpy(value, context, len); + kfree(context); + return len; } static int selinux_setprocattr(struct task_struct *p, @@ -4314,10 +4127,6 @@ 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 @@ -4347,13 +4156,6 @@ 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; @@ -4405,72 +4207,6 @@ static int selinux_setprocattr(struct task_struct *p, return size; } -static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) -{ - return security_sid_to_context(secid, secdata, seclen); -} - -static void selinux_release_secctx(char *secdata, u32 seclen) -{ - if (secdata) - kfree(secdata); -} - -#ifdef CONFIG_KEYS - -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; - - ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL); - if (!ksec) - return -ENOMEM; - - ksec->obj = k; - if (tsec->keycreate_sid) - ksec->sid = tsec->keycreate_sid; - else - ksec->sid = tsec->sid; - k->security = ksec; - - return 0; -} - -static void selinux_key_free(struct key *k) -{ - struct key_security_struct *ksec = k->security; - - k->security = NULL; - kfree(ksec); -} - -static int selinux_key_permission(key_ref_t key_ref, - struct task_struct *ctx, - key_perm_t perm) -{ - struct key *key; - struct task_security_struct *tsec; - struct key_security_struct *ksec; - - key = key_ref_to_ptr(key_ref); - - tsec = ctx->security; - ksec = key->security; - - /* if no specific permissions are requested, we skip the - permission check. No serious, additional covert channels - appear to be created. */ - if (perm == 0) - return 0; - - return avc_has_perm(tsec->sid, ksec->sid, - SECCLASS_KEY, perm, NULL); -} - -#endif - static struct security_operations selinux_ops = { .ptrace = selinux_ptrace, .capget = selinux_capget, @@ -4523,7 +4259,6 @@ 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, @@ -4549,15 +4284,11 @@ 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, @@ -4596,9 +4327,6 @@ static struct security_operations selinux_ops = { .getprocattr = selinux_getprocattr, .setprocattr = selinux_setprocattr, - .secid_to_secctx = selinux_secid_to_secctx, - .release_secctx = selinux_release_secctx, - .unix_stream_connect = selinux_socket_unix_stream_connect, .unix_may_send = selinux_socket_unix_may_send, @@ -4616,8 +4344,7 @@ static struct security_operations selinux_ops = { .socket_setsockopt = selinux_socket_setsockopt, .socket_shutdown = selinux_socket_shutdown, .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, - .socket_getpeersec_stream = selinux_socket_getpeersec_stream, - .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram, + .socket_getpeersec = selinux_socket_getpeersec, .sk_alloc_security = selinux_sk_alloc_security, .sk_free_security = selinux_sk_free_security, .sk_getsid = selinux_sk_getsid_security, @@ -4626,18 +4353,10 @@ static struct security_operations selinux_ops = { .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc, .xfrm_policy_clone_security = selinux_xfrm_policy_clone, .xfrm_policy_free_security = selinux_xfrm_policy_free, - .xfrm_policy_delete_security = selinux_xfrm_policy_delete, .xfrm_state_alloc_security = selinux_xfrm_state_alloc, .xfrm_state_free_security = selinux_xfrm_state_free, - .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, #endif - -#ifdef CONFIG_KEYS - .key_alloc = selinux_key_alloc, - .key_free = selinux_key_free, - .key_permission = selinux_key_permission, -#endif }; static __init int selinux_init(void) @@ -4657,9 +4376,6 @@ static __init int selinux_init(void) tsec = current->security; tsec->osid = tsec->sid = SECINITSID_KERNEL; - sel_inode_cache = kmem_cache_create("selinux_inode_security", - sizeof(struct inode_security_struct), - 0, SLAB_PANIC, NULL, NULL); avc_init(); original_ops = secondary_ops = security_ops; @@ -4673,15 +4389,6 @@ static __init int selinux_init(void) } else { printk(KERN_INFO "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; } @@ -4691,7 +4398,6 @@ void selinux_complete_init(void) /* Set up any superblocks initialized prior to the policy load. */ printk(KERN_INFO "SELinux: Setting up existing superblocks.\n"); - spin_lock(&sb_lock); spin_lock(&sb_security_lock); next_sb: if (!list_empty(&superblock_security_head)) { @@ -4700,20 +4406,19 @@ next_sb: struct superblock_security_struct, list); struct super_block *sb = sbsec->sb; + spin_lock(&sb_lock); sb->s_count++; - spin_unlock(&sb_security_lock); spin_unlock(&sb_lock); + spin_unlock(&sb_security_lock); down_read(&sb->s_umount); if (sb->s_root) superblock_doinit(sb, NULL); drop_super(sb); - spin_lock(&sb_lock); spin_lock(&sb_security_lock); list_del_init(&sbsec->list); goto next_sb; } spin_unlock(&sb_security_lock); - spin_unlock(&sb_lock); } /* SELinux requires early initialization in order to label @@ -4808,7 +4513,6 @@ int selinux_disable(void) printk(KERN_INFO "SELinux: Disabled at runtime.\n"); selinux_disabled = 1; - selinux_enabled = 0; /* Reset security_ops to the secondary module, dummy or capability. */ security_ops = secondary_ops;