X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=security%2Fselinux%2Fhooks.c;h=7a374c2eb043b1f611069bd1c67d64f7f64c7f13;hb=28b4d5cc17c20786848cdc07b7ea237a309776bb;hp=254b7983657de8735ce5a7ba92e62c3756fb590c;hpb=2bf49690325b62480a42f7afed5e9f164173c570;p=safe%2Fjmp%2Flinux-2.6 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 254b798..7a374c2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -13,8 +13,8 @@ * Eric Paris * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * - * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. - * Paul Moore + * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P. + * Paul Moore * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. * Yuichi Nakamura * @@ -91,7 +91,6 @@ #define NUM_SEL_MNT_OPTS 5 -extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); extern struct security_operations *security_ops; @@ -448,6 +447,10 @@ static int sb_finish_set_opts(struct super_block *sb) sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) sbsec->flags &= ~SE_SBLABELSUPP; + /* Special handling for sysfs. Is genfs but also has setxattr handler*/ + if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) + sbsec->flags |= SE_SBLABELSUPP; + /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); @@ -1285,6 +1288,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, context, len); if (rc == -ERANGE) { + kfree(context); + /* Need a larger buffer. Query for the right size. */ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, NULL, 0); @@ -1292,7 +1297,6 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent dput(dentry); goto out_unlock; } - kfree(context); len = rc; context = kmalloc(len+1, GFP_NOFS); if (!context) { @@ -1530,6 +1534,8 @@ static int inode_has_perm(const struct cred *cred, struct common_audit_data ad; u32 sid; + validate_creds(cred); + if (unlikely(IS_PRIVATE(inode))) return 0; @@ -2404,7 +2410,7 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm) /* Wake up the parent if it is waiting so that it can recheck * wait permission to the new task SID. */ read_lock(&tasklist_lock); - wake_up_interruptible(¤t->real_parent->signal->wait_chldexit); + __wake_up_parent(current, current->real_parent); read_unlock(&tasklist_lock); } @@ -2710,12 +2716,18 @@ static int selinux_inode_permission(struct inode *inode, int mask) static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) { const struct cred *cred = current_cred(); + unsigned int ia_valid = iattr->ia_valid; + + /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */ + if (ia_valid & ATTR_FORCE) { + ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE | + ATTR_FORCE); + if (!ia_valid) + return 0; + } - if (iattr->ia_valid & ATTR_FORCE) - return 0; - - if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | - ATTR_ATIME_SET | ATTR_MTIME_SET)) + if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | + ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR); return dentry_has_perm(cred, NULL, dentry, FILE__WRITE); @@ -2914,6 +2926,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, return rc; isec->sid = newsid; + isec->initialized = 1; return 0; } @@ -3224,12 +3237,29 @@ static int selinux_task_create(unsigned long clone_flags) } /* + * allocate the SELinux part of blank credentials + */ +static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ + struct task_security_struct *tsec; + + tsec = kzalloc(sizeof(struct task_security_struct), gfp); + if (!tsec) + return -ENOMEM; + + cred->security = tsec; + return 0; +} + +/* * detach and free the LSM part of a set of credentials */ static void selinux_cred_free(struct cred *cred) { struct task_security_struct *tsec = cred->security; - cred->security = NULL; + + BUG_ON((unsigned long) cred->security < PAGE_SIZE); + cred->security = (void *) 0x7UL; kfree(tsec); } @@ -3253,6 +3283,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old, } /* + * transfer the SELinux data to a blank set of creds + */ +static void selinux_cred_transfer(struct cred *new, const struct cred *old) +{ + const struct task_security_struct *old_tsec = old->security; + struct task_security_struct *tsec = new->security; + + *tsec = *old_tsec; +} + +/* * set the security data for a kernel service * - all the creation contexts are set to unlabelled */ @@ -3296,9 +3337,18 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) return 0; } -static int selinux_kernel_module_request(void) +static int selinux_kernel_module_request(char *kmod_name) { - return task_has_system(current, SYSTEM__MODULE_REQUEST); + u32 sid; + struct common_audit_data ad; + + sid = task_sid(current); + + COMMON_AUDIT_DATA_INIT(&ad, KMOD); + ad.u.kmod_name = kmod_name; + + return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, + SYSTEM__MODULE_REQUEST, &ad); } static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) @@ -4043,7 +4093,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, char *addrp; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->iif; + ad.u.net.netif = skb->skb_iif; ad.u.net.family = family; err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) @@ -4105,7 +4155,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return 0; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = skb->iif; + ad.u.net.netif = skb->skb_iif; ad.u.net.family = family; err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); if (err) @@ -4117,7 +4167,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); if (err) return err; - err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family, + err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 0); @@ -4318,6 +4368,59 @@ static void selinux_req_classify_flow(const struct request_sock *req, fl->secid = req->secid; } +static int selinux_tun_dev_create(void) +{ + u32 sid = current_sid(); + + /* we aren't taking into account the "sockcreate" SID since the socket + * that is being created here is not a socket in the traditional sense, + * instead it is a private sock, accessible only to the kernel, and + * representing a wide range of network traffic spanning multiple + * connections unlike traditional sockets - check the TUN driver to + * get a better understanding of why this socket is special */ + + return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, + NULL); +} + +static void selinux_tun_dev_post_create(struct sock *sk) +{ + struct sk_security_struct *sksec = sk->sk_security; + + /* we don't currently perform any NetLabel based labeling here and it + * isn't clear that we would want to do so anyway; while we could apply + * labeling without the support of the TUN user the resulting labeled + * traffic from the other end of the connection would almost certainly + * cause confusion to the TUN user that had no idea network labeling + * protocols were being used */ + + /* see the comments in selinux_tun_dev_create() about why we don't use + * the sockcreate SID here */ + + sksec->sid = current_sid(); + sksec->sclass = SECCLASS_TUN_SOCKET; +} + +static int selinux_tun_dev_attach(struct sock *sk) +{ + struct sk_security_struct *sksec = sk->sk_security; + u32 sid = current_sid(); + int err; + + err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET, + TUN_SOCKET__RELABELFROM, NULL); + if (err) + return err; + err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, + TUN_SOCKET__RELABELTO, NULL); + if (err) + return err; + + sksec->sid = sid; + + return 0; +} + static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) { int err = 0; @@ -4619,10 +4722,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) if (err) return err; - if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) - err = selinux_nlmsg_perm(sk, skb); - - return err; + return selinux_nlmsg_perm(sk, skb); } static int selinux_netlink_recv(struct sk_buff *skb, int capability) @@ -5261,6 +5361,32 @@ static void selinux_release_secctx(char *secdata, u32 seclen) kfree(secdata); } +/* + * called with inode->i_mutex locked + */ +static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) +{ + return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0); +} + +/* + * called with inode->i_mutex locked + */ +static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) +{ + return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0); +} + +static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) +{ + int len = 0; + len = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX, + ctx, true); + if (len < 0) + return len; + *ctxlen = len; + return 0; +} #ifdef CONFIG_KEYS static int selinux_key_alloc(struct key *k, const struct cred *cred, @@ -5405,8 +5531,10 @@ static struct security_operations selinux_ops = { .dentry_open = selinux_dentry_open, .task_create = selinux_task_create, + .cred_alloc_blank = selinux_cred_alloc_blank, .cred_free = selinux_cred_free, .cred_prepare = selinux_cred_prepare, + .cred_transfer = selinux_cred_transfer, .kernel_act_as = selinux_kernel_act_as, .kernel_create_files_as = selinux_kernel_create_files_as, .kernel_module_request = selinux_kernel_module_request, @@ -5458,6 +5586,9 @@ static struct security_operations selinux_ops = { .secid_to_secctx = selinux_secid_to_secctx, .secctx_to_secid = selinux_secctx_to_secid, .release_secctx = selinux_release_secctx, + .inode_notifysecctx = selinux_inode_notifysecctx, + .inode_setsecctx = selinux_inode_setsecctx, + .inode_getsecctx = selinux_inode_getsecctx, .unix_stream_connect = selinux_socket_unix_stream_connect, .unix_may_send = selinux_socket_unix_may_send, @@ -5487,6 +5618,9 @@ static struct security_operations selinux_ops = { .inet_csk_clone = selinux_inet_csk_clone, .inet_conn_established = selinux_inet_conn_established, .req_classify_flow = selinux_req_classify_flow, + .tun_dev_create = selinux_tun_dev_create, + .tun_dev_post_create = selinux_tun_dev_post_create, + .tun_dev_attach = selinux_tun_dev_attach, #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc, @@ -5701,12 +5835,12 @@ int selinux_disable(void) selinux_disabled = 1; selinux_enabled = 0; - /* Try to destroy the avc node cache */ - avc_disable(); - /* Reset security_ops to the secondary module, dummy or capability. */ security_ops = secondary_ops; + /* Try to destroy the avc node cache */ + avc_disable(); + /* Unregister netfilter hooks. */ selinux_nf_ip_exit();