X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=security%2Fselinux%2Fhooks.c;h=b1ac22d231954bc5e900c07da670962c953ba1c2;hb=e98ff7f6d8164c5636538998fb7d1e08b1fbbddd;hp=9f9463000683e94f942dc29bdbadf08087603ecf;hpb=c973b112c76c9d8fd042991128f218a738cc8d0a;p=safe%2Fjmp%2Flinux-2.6 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9f94630..b1ac22d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -12,13 +12,14 @@ * Copyright (C) 2003 Red Hat, Inc., James Morris * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * + * Copyright (C) 2006 Hewlett-Packard Development Company, L.P. + * Paul Moore, * * 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 #include #include #include @@ -50,7 +51,6 @@ #include /* for sysctl_local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ #include -#include #include #include #include @@ -58,6 +58,7 @@ #include #include #include +#include #include #include /* for Unix socket types */ #include /* for Unix socket types */ @@ -69,16 +70,21 @@ #include #include #include +#include +#include #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 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; @@ -100,6 +106,8 @@ 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. */ @@ -116,18 +124,44 @@ static struct security_operations *secondary_ops = NULL; static LIST_HEAD(superblock_security_head); static DEFINE_SPINLOCK(sb_security_lock); +static struct kmem_cache *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) { struct task_security_struct *tsec; - tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL); + tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL); if (!tsec) return -ENOMEM; - memset(tsec, 0, sizeof(struct task_security_struct)); - tsec->magic = SELINUX_MAGIC; tsec->task = task; tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED; task->security = tsec; @@ -138,10 +172,6 @@ static int task_alloc_security(struct task_struct *task) static void task_free_security(struct task_struct *task) { struct task_security_struct *tsec = task->security; - - if (!tsec || tsec->magic != SELINUX_MAGIC) - return; - task->security = NULL; kfree(tsec); } @@ -151,21 +181,16 @@ static int inode_alloc_security(struct inode *inode) struct task_security_struct *tsec = current->security; struct inode_security_struct *isec; - isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL); + isec = kmem_cache_zalloc(sel_inode_cache, GFP_KERNEL); if (!isec) return -ENOMEM; - memset(isec, 0, sizeof(struct inode_security_struct)); - init_MUTEX(&isec->sem); + mutex_init(&isec->lock); INIT_LIST_HEAD(&isec->list); - isec->magic = SELINUX_MAGIC; isec->inode = inode; isec->sid = SECINITSID_UNLABELED; isec->sclass = SECCLASS_FILE; - if (tsec && tsec->magic == SELINUX_MAGIC) - isec->task_sid = tsec->sid; - else - isec->task_sid = SECINITSID_UNLABELED; + isec->task_sid = tsec->sid; inode->i_security = isec; return 0; @@ -176,16 +201,13 @@ static void inode_free_security(struct inode *inode) struct inode_security_struct *isec = inode->i_security; struct superblock_security_struct *sbsec = inode->i_sb->s_security; - if (!isec || isec->magic != SELINUX_MAGIC) - return; - spin_lock(&sbsec->isec_lock); if (!list_empty(&isec->list)) list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); inode->i_security = NULL; - kfree(isec); + kmem_cache_free(sel_inode_cache, isec); } static int file_alloc_security(struct file *file) @@ -193,20 +215,13 @@ static int file_alloc_security(struct file *file) struct task_security_struct *tsec = current->security; struct file_security_struct *fsec; - fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC); + fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL); if (!fsec) return -ENOMEM; - memset(fsec, 0, sizeof(struct file_security_struct)); - fsec->magic = SELINUX_MAGIC; fsec->file = file; - if (tsec && tsec->magic == SELINUX_MAGIC) { - fsec->sid = tsec->sid; - fsec->fown_sid = tsec->sid; - } else { - fsec->sid = SECINITSID_UNLABELED; - fsec->fown_sid = SECINITSID_UNLABELED; - } + fsec->sid = tsec->sid; + fsec->fown_sid = tsec->sid; file->f_security = fsec; return 0; @@ -215,10 +230,6 @@ static int file_alloc_security(struct file *file) static void file_free_security(struct file *file) { struct file_security_struct *fsec = file->f_security; - - if (!fsec || fsec->magic != SELINUX_MAGIC) - return; - file->f_security = NULL; kfree(fsec); } @@ -227,19 +238,18 @@ static int superblock_alloc_security(struct super_block *sb) { struct superblock_security_struct *sbsec; - sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL); + sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL); if (!sbsec) return -ENOMEM; - memset(sbsec, 0, sizeof(struct superblock_security_struct)); - 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); - sbsec->magic = SELINUX_MAGIC; sbsec->sb = sb; sbsec->sid = SECINITSID_UNLABELED; sbsec->def_sid = SECINITSID_FILE; + sbsec->mntpoint_sid = SECINITSID_UNLABELED; sb->s_security = sbsec; return 0; @@ -249,9 +259,6 @@ static void superblock_free_security(struct super_block *sb) { struct superblock_security_struct *sbsec = sb->s_security; - if (!sbsec || sbsec->magic != SELINUX_MAGIC) - return; - spin_lock(&sb_security_lock); if (!list_empty(&sbsec->list)) list_del_init(&sbsec->list); @@ -261,24 +268,21 @@ static void superblock_free_security(struct super_block *sb) kfree(sbsec); } -#ifdef CONFIG_SECURITY_NETWORK -static int sk_alloc_security(struct sock *sk, int family, int priority) +static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) { struct sk_security_struct *ssec; - if (family != PF_UNIX) - return 0; - - ssec = kmalloc(sizeof(*ssec), priority); + ssec = kzalloc(sizeof(*ssec), priority); if (!ssec) return -ENOMEM; - memset(ssec, 0, sizeof(*ssec)); - ssec->magic = SELINUX_MAGIC; ssec->sk = sk; ssec->peer_sid = SECINITSID_UNLABELED; + ssec->sid = SECINITSID_UNLABELED; sk->sk_security = ssec; + selinux_netlbl_sk_security_init(ssec, family); + return 0; } @@ -286,13 +290,9 @@ static void sk_free_security(struct sock *sk) { struct sk_security_struct *ssec = sk->sk_security; - if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC) - return; - sk->sk_security = NULL; kfree(ssec); } -#endif /* CONFIG_SECURITY_NETWORK */ /* The security server must be initialized before any labeling or access decisions can be provided. */ @@ -320,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; @@ -364,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]; @@ -375,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; @@ -391,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; } @@ -406,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; @@ -442,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) { @@ -451,20 +523,38 @@ 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); - if (rc) + 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); goto out_free; + } - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELTO, NULL); + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); if (rc) goto out_free; - sbsec->sid = sid; - - if (seen & Opt_context) - sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + isec->sid = sid; + isec->initialized = 1; } if (defcontext) { @@ -479,13 +569,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; @@ -496,6 +580,8 @@ out_free: if (alloc) { kfree(context); kfree(defcontext); + kfree(fscontext); + kfree(rootcontext); } out: return rc; @@ -508,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; @@ -603,7 +689,7 @@ next_inode: } spin_unlock(&sbsec->isec_lock); out: - up(&sbsec->sem); + mutex_unlock(&sbsec->lock); return rc; } @@ -630,6 +716,16 @@ static inline u16 inode_mode_to_security_class(umode_t mode) return SECCLASS_FILE; } +static inline int default_protocol_stream(int protocol) +{ + return (protocol == IPPROTO_IP || protocol == IPPROTO_TCP); +} + +static inline int default_protocol_dgram(int protocol) +{ + return (protocol == IPPROTO_IP || protocol == IPPROTO_UDP); +} + static inline u16 socket_type_to_security_class(int family, int type, int protocol) { switch (family) { @@ -646,10 +742,18 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc case PF_INET6: switch (type) { case SOCK_STREAM: - return SECCLASS_TCP_SOCKET; + if (default_protocol_stream(protocol)) + return SECCLASS_TCP_SOCKET; + else + return SECCLASS_RAWIP_SOCKET; case SOCK_DGRAM: - return SECCLASS_UDP_SOCKET; - case SOCK_RAW: + if (default_protocol_dgram(protocol)) + return SECCLASS_UDP_SOCKET; + else + return SECCLASS_RAWIP_SOCKET; + case SOCK_DCCP: + return SECCLASS_DCCP_SOCKET; + default: return SECCLASS_RAWIP_SOCKET; } break; @@ -659,7 +763,7 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_NETLINK_ROUTE_SOCKET; case NETLINK_FIREWALL: return SECCLASS_NETLINK_FIREWALL_SOCKET; - case NETLINK_TCPDIAG: + case NETLINK_INET_DIAG: return SECCLASS_NETLINK_TCPDIAG_SOCKET; case NETLINK_NFLOG: return SECCLASS_NETLINK_NFLOG_SOCKET; @@ -682,6 +786,8 @@ 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; @@ -739,15 +845,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) { @@ -758,7 +862,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) { @@ -781,7 +885,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; @@ -789,7 +893,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); @@ -799,7 +903,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; @@ -807,7 +911,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, @@ -820,7 +924,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; @@ -856,11 +960,14 @@ 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: + 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) { @@ -871,7 +978,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; } } @@ -880,12 +987,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; } @@ -971,6 +1077,9 @@ static int inode_has_perm(struct task_struct *tsk, struct inode_security_struct *isec; struct avc_audit_data ad; + if (unlikely (IS_PRIVATE (inode))) + return 0; + tsec = tsk->security; isec = inode->i_security; @@ -1007,14 +1116,14 @@ static inline int dentry_has_perm(struct task_struct *tsk, has the same SID as the process. If av is zero, then access to the file is not checked, e.g. for cases where only the descriptor is affected like seek. */ -static inline int file_has_perm(struct task_struct *tsk, +static int file_has_perm(struct task_struct *tsk, struct file *file, u32 av) { struct task_security_struct *tsec = tsk->security; struct file_security_struct *fsec = file->f_security; - struct vfsmount *mnt = file->f_vfsmnt; - struct dentry *dentry = file->f_dentry; + struct vfsmount *mnt = file->f_path.mnt; + struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; struct avc_audit_data ad; int rc; @@ -1082,6 +1191,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 @@ -1246,104 +1366,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; -} - -/* Set the security attributes on a newly created file. */ -static int post_create(struct inode *dir, - struct dentry *dentry) -{ - - struct task_security_struct *tsec; - struct inode *inode; - struct inode_security_struct *dsec; - struct superblock_security_struct *sbsec; - u32 newsid; - char *context; - unsigned int len; - int rc; - - tsec = current->security; - dsec = dir->i_security; - sbsec = dir->i_sb->s_security; - - inode = dentry->d_inode; - if (!inode) { - /* Some file system types (e.g. NFS) may not instantiate - a dentry for all create operations (e.g. symlink), - so we have to check to see if the inode is non-NULL. */ - printk(KERN_WARNING "post_create: no inode, dir (dev=%s, " - "ino=%ld)\n", dir->i_sb->s_id, dir->i_ino); - return 0; - } - - if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { - newsid = tsec->create_sid; - } else { - rc = security_transition_sid(tsec->sid, dsec->sid, - inode_mode_to_security_class(inode->i_mode), - &newsid); - if (rc) { - printk(KERN_WARNING "post_create: " - "security_transition_sid failed, rc=%d (dev=%s " - "ino=%ld)\n", - -rc, inode->i_sb->s_id, inode->i_ino); - return rc; - } - } - - rc = inode_security_set_sid(inode, newsid); - if (rc) { - printk(KERN_WARNING "post_create: inode_security_set_sid " - "failed, rc=%d (dev=%s ino=%ld)\n", - -rc, inode->i_sb->s_id, inode->i_ino); - return rc; - } - - if (sbsec->behavior == SECURITY_FS_USE_XATTR && - inode->i_op->setxattr) { - /* Use extended attributes. */ - rc = security_sid_to_context(newsid, &context, &len); - if (rc) { - printk(KERN_WARNING "post_create: sid_to_context " - "failed, rc=%d (dev=%s ino=%ld)\n", - -rc, inode->i_sb->s_id, inode->i_ino); - return rc; - } - down(&inode->i_sem); - rc = inode->i_op->setxattr(dentry, - XATTR_NAME_SELINUX, - context, len, 0); - up(&inode->i_sem); - kfree(context); - if (rc < 0) { - printk(KERN_WARNING "post_create: setxattr failed, " - "rc=%d (dev=%s ino=%ld)\n", - -rc, inode->i_sb->s_id, inode->i_ino); - return rc; - } - } - - return 0; -} - - /* Hook functions begin here. */ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) @@ -1358,7 +1380,7 @@ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) rc = task_has_perm(parent, child, PROCESS__PTRACE); /* Save the SID of the tracing process for later use in apply_creds. */ - if (!rc) + if (!(child->ptrace & PT_PTRACED) && !rc) csec->ptrace_sid = psec->sid; return rc; } @@ -1404,6 +1426,47 @@ static int selinux_capable(struct task_struct *tsk, int cap) return task_has_capability(tsk,cap); } +static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) +{ + int buflen, rc; + char *buffer, *path, *end; + + rc = -ENOMEM; + buffer = (char*)__get_free_page(GFP_KERNEL); + if (!buffer) + goto out; + + buflen = PAGE_SIZE; + end = buffer+buflen; + *--end = '\0'; + buflen--; + path = end-1; + *path = '/'; + while (table) { + const char *name = table->procname; + size_t namelen = strlen(name); + buflen -= namelen + 1; + if (buflen < 0) + goto out_free; + end -= namelen; + memcpy(end, name, namelen); + *--end = '/'; + path = end; + table = table->parent; + } + buflen -= 4; + if (buflen < 0) + goto out_free; + end -= 4; + memcpy(end, "/sys", 4); + path = end; + rc = security_genfs_sid("proc", path, tclass, sid); +out_free: + free_page((unsigned long)buffer); +out: + return rc; +} + static int selinux_sysctl(ctl_table *table, int op) { int error = 0; @@ -1418,8 +1481,8 @@ static int selinux_sysctl(ctl_table *table, int op) tsec = current->security; - rc = selinux_proc_get_sid(table->de, (op == 001) ? - SECCLASS_DIR : SECCLASS_FILE, &tsid); + rc = selinux_sysctl_get_sid(table, (op == 0001) ? + SECCLASS_DIR : SECCLASS_FILE, &tsid); if (rc) { /* Default to the well-defined sysctl SID. */ tsid = SECINITSID_SYSCTL; @@ -1546,12 +1609,10 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm) { struct bprm_security_struct *bsec; - bsec = kmalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); + bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); if (!bsec) return -ENOMEM; - memset(bsec, 0, sizeof *bsec); - bsec->magic = SELINUX_MAGIC; bsec->bprm = bprm; bsec->sid = SECINITSID_UNLABELED; bsec->set = 0; @@ -1563,7 +1624,7 @@ static int selinux_bprm_alloc_security(struct linux_binprm *bprm) static int selinux_bprm_set_security(struct linux_binprm *bprm) { struct task_security_struct *tsec; - struct inode *inode = bprm->file->f_dentry->d_inode; + struct inode *inode = bprm->file->f_path.dentry->d_inode; struct inode_security_struct *isec; struct bprm_security_struct *bsec; u32 newsid; @@ -1585,8 +1646,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; @@ -1601,10 +1664,10 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) } AVC_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.mnt = bprm->file->f_vfsmnt; - ad.u.fs.dentry = bprm->file->f_dentry; + ad.u.fs.mnt = bprm->file->f_path.mnt; + ad.u.fs.dentry = bprm->file->f_path.dentry; - if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) newsid = tsec->sid; if (tsec->sid == newsid) { @@ -1672,28 +1735,35 @@ 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; + 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_list); + file = list_entry(tty->tty_files.next, typeof(*file), f_u.fu_list); if (file) { /* Revalidate access to controlling tty. Use inode_has_perm on the tty inode directly rather than using file_has_perm, as this particular open file may belong to another process and we are only interested in the inode-based check here. */ - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; if (inode_has_perm(current, inode, FILE__READ | FILE__WRITE, NULL)) { - /* Reset controlling tty. */ - current->signal->tty = NULL; - current->signal->tty_old_pgrp = 0; + drop_tty = 1; } } file_list_unlock(); + + /* Reset controlling tty. */ + if (drop_tty) + proc_set_tty(current, NULL); } + mutex_unlock(&tty_mutex); /* Revalidate access to inherited open files. */ @@ -1706,9 +1776,10 @@ static inline void flush_unauthorized_files(struct files_struct * files) j++; i = j * __NFDBITS; - if (i >= files->max_fds || i >= files->max_fdset) + fdt = files_fdtable(files); + if (i >= fdt->max_fds) break; - set = files->open_fds->fds_bits[j]; + set = fdt->open_fds->fds_bits[j]; if (!set) continue; spin_unlock(&files->file_lock); @@ -1729,10 +1800,11 @@ static inline void flush_unauthorized_files(struct files_struct * files) continue; } if (devnull) { - atomic_inc(&devnull->f_count); + 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; @@ -1892,7 +1964,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) @@ -1900,18 +1973,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; @@ -1933,11 +2028,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); @@ -1965,13 +2063,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, @@ -2018,29 +2116,81 @@ static void selinux_inode_free_security(struct inode *inode) inode_free_security(inode); } -static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) -{ - return may_create(dir, dentry, SECCLASS_FILE); -} - -static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask) -{ - post_create(dir, dentry); -} - -static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) +static int selinux_inode_init_security(struct inode *inode, struct inode *dir, + char **name, void **value, + size_t *len) { + struct task_security_struct *tsec; + struct inode_security_struct *dsec; + struct superblock_security_struct *sbsec; + u32 newsid, clen; int rc; + char *namep = NULL, *context; - rc = secondary_ops->inode_link(old_dentry,dir,new_dentry); - if (rc) - return rc; - return may_link(dir, old_dentry, MAY_LINK); -} + tsec = current->security; + dsec = dir->i_security; + sbsec = dir->i_sb->s_security; -static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) -{ - return; + if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { + newsid = tsec->create_sid; + } else { + rc = security_transition_sid(tsec->sid, dsec->sid, + inode_mode_to_security_class(inode->i_mode), + &newsid); + if (rc) { + printk(KERN_WARNING "%s: " + "security_transition_sid failed, rc=%d (dev=%s " + "ino=%ld)\n", + __FUNCTION__, + -rc, inode->i_sb->s_id, inode->i_ino); + return rc; + } + } + + /* 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; + + if (name) { + namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL); + if (!namep) + return -ENOMEM; + *name = namep; + } + + if (value && len) { + rc = security_sid_to_context(newsid, &context, &clen); + if (rc) { + kfree(namep); + return rc; + } + *value = context; + *len = clen; + } + + return 0; +} + +static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) +{ + return may_create(dir, dentry, SECCLASS_FILE); +} + +static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) +{ + int rc; + + rc = secondary_ops->inode_link(old_dentry,dir,new_dentry); + if (rc) + return rc; + return may_link(dir, old_dentry, MAY_LINK); } static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) @@ -2058,21 +2208,11 @@ static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const return may_create(dir, dentry, SECCLASS_LNK_FILE); } -static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name) -{ - post_create(dir, dentry); -} - static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask) { return may_create(dir, dentry, SECCLASS_DIR); } -static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask) -{ - post_create(dir, dentry); -} - static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry) { return may_link(dir, dentry, MAY_RMDIR); @@ -2089,23 +2229,12 @@ static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mod return may_create(dir, dentry, inode_mode_to_security_class(mode)); } -static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) -{ - post_create(dir, dentry); -} - static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { return may_rename(old_inode, old_dentry, new_inode, new_dentry); } -static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry) -{ - return; -} - static int selinux_inode_readlink(struct dentry *dentry) { return dentry_has_perm(current, NULL, dentry, FILE__READ); @@ -2248,12 +2377,6 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, char *name, static int selinux_inode_getxattr (struct dentry *dentry, char *name) { - struct inode *inode = dentry->d_inode; - struct superblock_security_struct *sbsec = inode->i_sb->s_security; - - if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) - return -EOPNOTSUPP; - return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); } @@ -2284,33 +2407,26 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) return -EACCES; } -static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) +static const char *selinux_inode_xattr_getsuffix(void) { - struct inode_security_struct *isec = inode->i_security; - char *context; - unsigned len; - int rc; + return XATTR_SELINUX_SUFFIX; +} - /* Permission check handled by selinux_inode_getxattr hook.*/ +/* + * 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 + * canonicalize the value to be finally returned to the user. + * + * 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) +{ + struct inode_security_struct *isec = inode->i_security; if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; - rc = security_sid_to_context(isec->sid, &context, &len); - if (rc) - return rc; - - if (!buffer || !size) { - kfree(context); - return len; - } - if (size < len) { - kfree(context); - return -ERANGE; - } - memcpy(buffer, context, len); - kfree(context); - return len; + return selinux_getsecurity(isec->sid, buffer, size); } static int selinux_inode_setsecurity(struct inode *inode, const char *name, @@ -2346,7 +2462,8 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t static int selinux_file_permission(struct file *file, int mask) { - struct inode *inode = file->f_dentry->d_inode; + int rc; + struct inode *inode = file->f_path.dentry->d_inode; if (!mask) { /* No permission to check. Existence test. */ @@ -2357,8 +2474,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) @@ -2478,35 +2599,27 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, prot = reqprot; #ifndef CONFIG_PPC32 - if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXECUTABLE) && - (vma->vm_start >= vma->vm_mm->start_brk && - vma->vm_end <= vma->vm_mm->brk)) { - /* - * We are making an executable mapping in the brk region. - * This has an additional execheap check. - */ - rc = task_has_perm(current, current, PROCESS__EXECHEAP); - if (rc) - return rc; - } - if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) { - /* - * We are making executable a file mapping that has - * had some COW done. Since pages might have been written, - * check ability to execute the possibly modified content. - * This typically should only occur for text relocations. - */ - int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD); - if (rc) - return rc; - } - if (!vma->vm_file && (prot & PROT_EXEC) && - vma->vm_start <= vma->vm_mm->start_stack && - vma->vm_end >= vma->vm_mm->start_stack) { - /* Attempt to make the process stack executable. - * This has an additional execstack check. - */ - rc = task_has_perm(current, current, PROCESS__EXECSTACK); + if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { + rc = 0; + if (vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk) { + rc = task_has_perm(current, current, + PROCESS__EXECHEAP); + } else if (!vma->vm_file && + vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) { + rc = task_has_perm(current, current, PROCESS__EXECSTACK); + } else if (vma->vm_file && vma->anon_vma) { + /* + * We are making executable a file mapping that has + * had some COW done. Since pages might have been + * written, check ability to execute the possibly + * modified content. This typically should only + * occur for text relocations. + */ + rc = file_has_perm(current, vma->vm_file, + FILE__EXECMOD); + } if (rc) return rc; } @@ -2527,7 +2640,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, switch (cmd) { case F_SETFL: - if (!file->f_dentry || !file->f_dentry->d_inode) { + if (!file->f_path.dentry || !file->f_path.dentry->d_inode) { err = -EINVAL; break; } @@ -2553,7 +2666,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, case F_SETLK64: case F_SETLKW64: #endif - if (!file->f_dentry || !file->f_dentry->d_inode) { + if (!file->f_path.dentry || !file->f_path.dentry->d_inode) { err = -EINVAL; break; } @@ -2585,7 +2698,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, struct file_security_struct *fsec; /* struct fown_struct is never outside the context of a struct file */ - file = (struct file *)((long)fown - offsetof(struct file,f_owner)); + file = container_of(fown, struct file, f_owner); tsec = tsk->security; fsec = file->f_security; @@ -2632,9 +2745,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 @@ -2686,6 +2801,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. */ @@ -2703,6 +2823,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; @@ -2732,25 +2862,35 @@ 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; - if (info && ((unsigned long)info == 1 || - (unsigned long)info == 2 || SI_FROMKERNEL(info))) + if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info))) return 0; if (!sig) 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, @@ -2797,10 +2937,9 @@ static void selinux_task_to_inode(struct task_struct *p, return; } -#ifdef CONFIG_SECURITY_NETWORK - /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv4(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; @@ -2818,6 +2957,9 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad ad->u.net.v4info.daddr = ih->daddr; ret = 0; + if (proto) + *proto = ih->protocol; + switch (ih->protocol) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -2851,6 +2993,22 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + offset += ihlen; + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + default: break; } @@ -2861,7 +3019,8 @@ out: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) /* Returns error only if unable to parse addresses */ -static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) +static int selinux_parse_skb_ipv6(struct sk_buff *skb, + struct avc_audit_data *ad, u8 *proto) { u8 nexthdr; int ret = -EINVAL, offset; @@ -2882,6 +3041,9 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad if (offset < 0) goto out; + if (proto) + *proto = nexthdr; + switch (nexthdr) { case IPPROTO_TCP: { struct tcphdr _tcph, *th; @@ -2907,6 +3069,18 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad break; } + case IPPROTO_DCCP: { + struct dccp_hdr _dccph, *dh; + + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh == NULL) + break; + + ad->u.net.sport = dh->dccph_sport; + ad->u.net.dport = dh->dccph_dport; + break; + } + /* includes fragments */ default: break; @@ -2918,13 +3092,13 @@ out: #endif /* IPV6 */ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, - char **addrp, int *len, int src) + char **addrp, int *len, int src, u8 *proto) { int ret = 0; switch (ad->u.net.family) { case PF_INET: - ret = selinux_parse_skb_ipv4(skb, ad); + ret = selinux_parse_skb_ipv4(skb, ad, proto); if (ret || !addrp) break; *len = 4; @@ -2934,7 +3108,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case PF_INET6: - ret = selinux_parse_skb_ipv6(skb, ad); + ret = selinux_parse_skb_ipv6(skb, ad, proto); if (ret || !addrp) break; *len = 16; @@ -2977,12 +3151,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); @@ -2990,20 +3166,30 @@ 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; + 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; + if (sock->sk) { + sksec = sock->sk->sk_security; + sksec->sid = isec->sid; + err = selinux_netlbl_socket_post_create(sock); + } + + return err; } /* Range of port numbers used to automatically bind. @@ -3023,6 +3209,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in /* * If PF_INET or PF_INET6, check name_bind permission for the port. + * Multiple address binding for SCTP is not supported yet: we just + * check the first address now. */ family = sock->sk->sk_family; if (family == PF_INET || family == PF_INET6) { @@ -3067,15 +3255,19 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in goto out; } - switch(sk->sk_protocol) { - case IPPROTO_TCP: + switch(isec->sclass) { + case SECCLASS_TCP_SOCKET: node_perm = TCP_SOCKET__NODE_BIND; break; - case IPPROTO_UDP: + case SECCLASS_UDP_SOCKET: node_perm = UDP_SOCKET__NODE_BIND; break; - + + case SECCLASS_DCCP_SOCKET: + node_perm = DCCP_SOCKET__NODE_BIND; + break; + default: node_perm = RAWIP_SOCKET__NODE_BIND; break; @@ -3113,16 +3305,17 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, return err; /* - * If a TCP socket, check name_connect permission for the port. + * If a TCP or DCCP socket, check name_connect permission for the port. */ isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET) { + if (isec->sclass == SECCLASS_TCP_SOCKET || + isec->sclass == SECCLASS_DCCP_SOCKET) { struct sock *sk = sock->sk; struct avc_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - u32 sid; + u32 sid, perm; if (sk->sk_family == PF_INET) { addr4 = (struct sockaddr_in *)address; @@ -3141,11 +3334,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (err) goto out; + perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? + TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; + AVC_AUDIT_DATA_INIT(&ad,NET); ad.u.net.dport = htons(snum); ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, - TCP_SOCKET__NAME_CONNECT, &ad); + err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); if (err) goto out; } @@ -3182,7 +3377,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, @@ -3203,7 +3404,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, @@ -3250,8 +3457,9 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, /* server child socket */ ssec = newsk->sk_security; ssec->peer_sid = isec->sid; - - return 0; + err = security_sid_mls_copy(other_isec->sid, ssec->peer_sid, &ssec->sid); + + return err; } static int selinux_socket_unix_may_send(struct socket *sock, @@ -3276,25 +3484,14 @@ static int selinux_socket_unix_may_send(struct socket *sock, return 0; } -static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, + struct avc_audit_data *ad, u16 family, char *addrp, int len) { - u16 family; - char *addrp; - int len, err = 0; + int 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; + u16 sock_class = 0; + u32 sock_sid = 0; read_lock_bh(&sk->sk_callback_lock); sock = sk->sk_socket; @@ -3312,11 +3509,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!sock_sid) goto out; - dev = skb->dev; - if (!dev) + if (!skb->dev) goto out; - err = sel_netif_sids(dev, &if_sid, NULL); + err = sel_netif_sids(skb->dev, &if_sid, NULL); if (err) goto out; @@ -3332,69 +3528,114 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) node_perm = NODE__TCP_RECV; recv_perm = TCP_SOCKET__RECV_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_RECV; + node_perm = NODE__DCCP_RECV; + recv_perm = DCCP_SOCKET__RECV_MSG; + break; + default: netif_perm = NETIF__RAWIP_RECV; node_perm = NODE__RAWIP_RECV; break; } - 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); + 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; + char *addrp; + int len, err = 0; + struct avc_audit_data ad; + struct sk_security_struct *sksec = sk->sk_security; + + 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 == htons(ETH_P_IP)) + family = PF_INET; + + 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, NULL); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_sock_rcv_skb_compat(sk, skb, &ad, family, + addrp, len); + else + err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + 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; } -static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, - int __user *optlen, unsigned len) +static int selinux_socket_getpeersec_stream(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 = SECSID_NULL; isec = SOCK_INODE(sock)->i_security; - if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { + + if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + isec->sclass == SECCLASS_TCP_SOCKET) { + ssec = sock->sk->sk_security; + peer_sid = ssec->peer_sid; + } + if (peer_sid == SECSID_NULL) { err = -ENOPROTOOPT; goto out; } - ssec = sock->sk->sk_security; - - err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); + err = security_sid_to_context(peer_sid, &scontext, &scontext_len); + if (err) goto out; @@ -3415,7 +3656,26 @@ out: return err; } -static int selinux_sk_alloc_security(struct sock *sk, int family, int priority) +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) + security_skb_extlbl_sid(skb, + SECINITSID_UNLABELED, + &peer_secid); + + 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); } @@ -3425,6 +3685,95 @@ static void selinux_sk_free_security(struct sock *sk) sk_free_security(sk); } +static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) +{ + struct sk_security_struct *ssec = sk->sk_security; + struct sk_security_struct *newssec = newsk->sk_security; + + newssec->sid = ssec->sid; + newssec->peer_sid = ssec->peer_sid; + + selinux_netlbl_sk_security_clone(ssec, newssec); +} + +static void selinux_sk_getsecid(struct sock *sk, u32 *secid) +{ + if (!sk) + *secid = SECINITSID_ANY_SOCKET; + else { + struct sk_security_struct *sksec = sk->sk_security; + + *secid = sksec->sid; + } +} + +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; + + 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); +} + +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; + u32 peersid; + + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &peersid); + if (peersid == SECSID_NULL) { + req->secid = sksec->sid; + req->peer_secid = SECSID_NULL; + return 0; + } + + err = security_sid_mls_copy(sksec->sid, peersid, &newsid); + if (err) + return err; + + req->secid = newsid; + req->peer_secid = peersid; + return 0; +} + +static void selinux_inet_csk_clone(struct sock *newsk, + const struct request_sock *req) +{ + struct sk_security_struct *newsksec = newsk->sk_security; + + newsksec->sid = req->secid; + newsksec->peer_sid = req->peer_secid; + /* NOTE: Ideally, we should also get the isec->sid for the + 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. */ + + /* We don't need to take any sort of lock here as we are the only + * thread with access to newsksec */ + selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); +} + +static void selinux_inet_conn_established(struct sock *sk, + struct sk_buff *skb) +{ + struct sk_security_struct *sksec = sk->sk_security; + + security_skb_extlbl_sid(skb, SECINITSID_UNLABELED, &sksec->peer_sid); +} + +static void selinux_req_classify_flow(const struct request_sock *req, + struct flowi *fl) +{ + fl->secid = req->secid; +} + static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) { int err = 0; @@ -3463,42 +3812,30 @@ out: #ifdef CONFIG_NETFILTER -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) +static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, + struct avc_audit_data *ad, + u16 family, char *addrp, int len) { - char *addrp; - int len, err = NF_ACCEPT; + int err = 0; 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; + isec = inode->i_security; + 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; @@ -3511,59 +3848,94 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, node_perm = NODE__TCP_SEND; send_perm = TCP_SOCKET__SEND_MSG; break; - + + case SECCLASS_DCCP_SOCKET: + netif_perm = NETIF__DCCP_SEND; + node_perm = NODE__DCCP_SEND; + send_perm = DCCP_SOCKET__SEND_MSG; + break; + default: netif_perm = NETIF__RAWIP_SEND; node_perm = NODE__RAWIP_SEND; break; } - - 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) + err = avc_has_perm(isec->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) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = security_node_sid(family, addrp, len, &node_sid); + if (err) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, - node_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad); + if (err) 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) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + ntohs(ad->u.net.dport), + &port_sid); + if (err) goto out; err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, &ad) ? NF_DROP : NF_ACCEPT; + send_perm, ad); } - 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 sk_buff *skb = *pskb; + struct avc_audit_data ad; + struct net_device *dev = (struct net_device *)out; + struct sk_security_struct *sksec; + u8 proto; + + sk = skb->sk; + if (!sk) + goto out; + + sksec = sk->sk_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, &proto); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_ip_postroute_last_compat(sk, dev, &ad, + family, addrp, len); + else + err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET, + PACKET__SEND, &ad); + + if (err) + goto out; + + err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto); +out: + return err ? NF_DROP : NF_ACCEPT; +} + static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, @@ -3588,43 +3960,34 @@ static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, #endif /* CONFIG_NETFILTER */ -#else - -static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) -{ - return 0; -} - -#endif /* CONFIG_SECURITY_NETWORK */ - 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, @@ -3634,19 +3997,13 @@ static int ipc_alloc_security(struct task_struct *task, struct task_security_struct *tsec = task->security; struct ipc_security_struct *isec; - isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); + isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); if (!isec) return -ENOMEM; - memset(isec, 0, sizeof(struct ipc_security_struct)); - isec->magic = SELINUX_MAGIC; isec->sclass = sclass; isec->ipc_perm = perm; - if (tsec) { - isec->sid = tsec->sid; - } else { - isec->sid = SECINITSID_UNLABELED; - } + isec->sid = tsec->sid; perm->security = isec; return 0; @@ -3655,9 +4012,6 @@ static int ipc_alloc_security(struct task_struct *task, static void ipc_free_security(struct kern_ipc_perm *perm) { struct ipc_security_struct *isec = perm->security; - if (!isec || isec->magic != SELINUX_MAGIC) - return; - perm->security = NULL; kfree(isec); } @@ -3666,12 +4020,10 @@ static int msg_msg_alloc_security(struct msg_msg *msg) { struct msg_security_struct *msec; - msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL); + msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL); if (!msec) return -ENOMEM; - memset(msec, 0, sizeof(struct msg_security_struct)); - msec->magic = SELINUX_MAGIC; msec->msg = msg; msec->sid = SECINITSID_UNLABELED; msg->security = msec; @@ -3682,8 +4034,6 @@ static int msg_msg_alloc_security(struct msg_msg *msg) static void msg_msg_free_security(struct msg_msg *msg) { struct msg_security_struct *msec = msg->security; - if (!msec || msec->magic != SELINUX_MAGIC) - return; msg->security = NULL; kfree(msec); @@ -4121,8 +4471,7 @@ static int selinux_getprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - u32 sid, len; - char *context; + u32 sid; int error; if (current != p) { @@ -4131,9 +4480,6 @@ static int selinux_getprocattr(struct task_struct *p, return error; } - if (!size) - return -ERANGE; - tsec = p->security; if (!strcmp(name, "current")) @@ -4144,22 +4490,17 @@ 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; - 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; + return selinux_getsecurity(sid, value, size); } static int selinux_setprocattr(struct task_struct *p, @@ -4185,6 +4526,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 @@ -4214,6 +4559,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; @@ -4265,6 +4617,72 @@ 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, @@ -4298,20 +4716,15 @@ static struct security_operations selinux_ops = { .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, + .inode_init_security = selinux_inode_init_security, .inode_create = selinux_inode_create, - .inode_post_create = selinux_inode_post_create, .inode_link = selinux_inode_link, - .inode_post_link = selinux_inode_post_link, .inode_unlink = selinux_inode_unlink, .inode_symlink = selinux_inode_symlink, - .inode_post_symlink = selinux_inode_post_symlink, .inode_mkdir = selinux_inode_mkdir, - .inode_post_mkdir = selinux_inode_post_mkdir, .inode_rmdir = selinux_inode_rmdir, .inode_mknod = selinux_inode_mknod, - .inode_post_mknod = selinux_inode_post_mknod, .inode_rename = selinux_inode_rename, - .inode_post_rename = selinux_inode_post_rename, .inode_readlink = selinux_inode_readlink, .inode_follow_link = selinux_inode_follow_link, .inode_permission = selinux_inode_permission, @@ -4322,6 +4735,7 @@ 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, @@ -4347,11 +4761,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, @@ -4390,7 +4808,9 @@ static struct security_operations selinux_ops = { .getprocattr = selinux_getprocattr, .setprocattr = selinux_setprocattr, -#ifdef CONFIG_SECURITY_NETWORK + .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, @@ -4408,9 +4828,35 @@ 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 = selinux_socket_getpeersec, + .socket_getpeersec_stream = selinux_socket_getpeersec_stream, + .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram, .sk_alloc_security = selinux_sk_alloc_security, .sk_free_security = selinux_sk_free_security, + .sk_clone_security = selinux_sk_clone_security, + .sk_getsecid = selinux_sk_getsecid, + .sock_graft = selinux_sock_graft, + .inet_conn_request = selinux_inet_conn_request, + .inet_csk_clone = selinux_inet_csk_clone, + .inet_conn_established = selinux_inet_conn_established, + .req_classify_flow = selinux_req_classify_flow, + +#ifdef CONFIG_SECURITY_NETWORK_XFRM + .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, + .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match, + .xfrm_decode_session = selinux_xfrm_decode_session, +#endif + +#ifdef CONFIG_KEYS + .key_alloc = selinux_key_alloc, + .key_free = selinux_key_free, + .key_permission = selinux_key_permission, #endif }; @@ -4431,6 +4877,9 @@ 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; @@ -4444,6 +4893,15 @@ 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; } @@ -4453,6 +4911,7 @@ 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)) { @@ -4461,26 +4920,27 @@ next_sb: struct superblock_security_struct, list); struct super_block *sb = sbsec->sb; - spin_lock(&sb_lock); sb->s_count++; - spin_unlock(&sb_lock); spin_unlock(&sb_security_lock); + spin_unlock(&sb_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 all processes and objects when they are created. */ security_initcall(selinux_init); -#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER) +#if defined(CONFIG_NETFILTER) static struct nf_hook_ops selinux_ipv4_op = { .hook = selinux_ipv4_postroute_last, @@ -4522,6 +4982,7 @@ static int __init selinux_nf_ip_init(void) panic("SELinux: nf_register_hook for IPv6: error %d\n", err); #endif /* IPV6 */ + out: return err; } @@ -4540,13 +5001,13 @@ static void selinux_nf_ip_exit(void) } #endif -#else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ +#else /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE #define selinux_nf_ip_exit() #endif -#endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ +#endif /* CONFIG_NETFILTER */ #ifdef CONFIG_SECURITY_SELINUX_DISABLE int selinux_disable(void) @@ -4567,6 +5028,7 @@ 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;