diff options
Diffstat (limited to 'security/smack/smack_lsm.c')
-rw-r--r-- | security/smack/smack_lsm.c | 334 |
1 files changed, 189 insertions, 145 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e7ded1326b0..921514902ec 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -7,6 +7,8 @@ * Casey Schaufler <casey@schaufler-ca.com> * * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * Paul Moore <paul.moore@hp.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,6 +22,7 @@ #include <linux/ext2_fs.h> #include <linux/kd.h> #include <asm/ioctls.h> +#include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/mutex.h> @@ -91,6 +94,7 @@ struct inode_smack *new_inode_smack(char *smack) /** * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH * @ctp: child task pointer + * @mode: ptrace attachment mode * * Returns 0 if access is OK, an error code otherwise * @@ -203,9 +207,8 @@ static void smack_sb_free_security(struct super_block *sb) /** * smack_sb_copy_data - copy mount options data for processing - * @type: file system type * @orig: where to start - * @smackopts + * @smackopts: mount options string * * Returns 0 on success or -ENOMEM on error. * @@ -331,7 +334,7 @@ static int smack_sb_statfs(struct dentry *dentry) /** * smack_sb_mount - Smack check for mounting * @dev_name: unused - * @nd: mount point + * @path: mount point * @type: unused * @flags: unused * @data: unused @@ -370,7 +373,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags) /** * smack_inode_alloc_security - allocate an inode blob - * @inode - the inode in need of a blob + * @inode: the inode in need of a blob * * Returns 0 if it gets a blob, -ENOMEM otherwise */ @@ -384,7 +387,7 @@ static int smack_inode_alloc_security(struct inode *inode) /** * smack_inode_free_security - free an inode blob - * @inode - the inode with a blob + * @inode: the inode with a blob * * Clears the blob pointer in inode */ @@ -538,7 +541,6 @@ static int smack_inode_rename(struct inode *old_inode, * smack_inode_permission - Smack version of permission() * @inode: the inode in question * @mask: the access requested - * @nd: unused * * This is the important Smack hook. * @@ -607,6 +609,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { if (!capable(CAP_MAC_ADMIN)) rc = -EPERM; + /* a label cannot be void and cannot begin with '-' */ + if (size == 0 || (size > 0 && ((char *)value)[0] == '-')) + rc = -EINVAL; } else rc = cap_inode_setxattr(dentry, name, value, size, flags); @@ -701,8 +706,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) * @inode: the object * @name: attribute name * @buffer: where to put the result - * @size: size of the buffer - * @err: unused + * @alloc: unused * * Returns the size of the attribute or an error code */ @@ -864,7 +868,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, /** * smack_file_lock - Smack check on file locking * @file: the object - * @cmd unused + * @cmd: unused * * Returns 0 if current has write access, error code otherwise */ @@ -1003,8 +1007,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, return 0; } -/* - * commit new credentials +/** + * smack_cred_commit - commit new credentials * @new: the new credentials * @old: the original credentials */ @@ -1014,8 +1018,8 @@ static void smack_cred_commit(struct cred *new, const struct cred *old) /** * smack_kernel_act_as - Set the subjective context in a set of credentials - * @new points to the set of credentials to be modified. - * @secid specifies the security ID to be set + * @new: points to the set of credentials to be modified. + * @secid: specifies the security ID to be set * * Set the security data for a kernel service. */ @@ -1032,8 +1036,8 @@ static int smack_kernel_act_as(struct cred *new, u32 secid) /** * smack_kernel_create_files_as - Set the file creation label in a set of creds - * @new points to the set of credentials to be modified - * @inode points to the inode to use as a reference + * @new: points to the set of credentials to be modified + * @inode: points to the inode to use as a reference * * Set the file creation context in a set of credentials to the same * as the objective context of the specified inode @@ -1242,7 +1246,7 @@ static int smack_task_wait(struct task_struct *p) /** * smack_task_to_inode - copy task smack into the inode blob * @p: task to copy from - * inode: inode to copy to + * @inode: inode to copy to * * Sets the smack pointer in the inode security blob */ @@ -1260,7 +1264,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) * smack_sk_alloc_security - Allocate a socket blob * @sk: the socket * @family: unused - * @priority: memory allocation priority + * @gfp_flags: memory allocation flags * * Assign Smack pointers to current * @@ -1277,7 +1281,6 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ssp->smk_in = csp; ssp->smk_out = csp; - ssp->smk_labeled = SMACK_CIPSO_SOCKET; ssp->smk_packet[0] = '\0'; sk->sk_security = ssp; @@ -1297,6 +1300,43 @@ static void smack_sk_free_security(struct sock *sk) } /** +* smack_host_label - check host based restrictions +* @sip: the object end +* +* looks for host based access restrictions +* +* This version will only be appropriate for really small sets of single label +* hosts. The caller is responsible for ensuring that the RCU read lock is +* taken before calling this function. +* +* Returns the label of the far end or NULL if it's not special. +*/ +static char *smack_host_label(struct sockaddr_in *sip) +{ + struct smk_netlbladdr *snp; + struct in_addr *siap = &sip->sin_addr; + + if (siap->s_addr == 0) + return NULL; + + list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) + /* + * we break after finding the first match because + * the list is sorted from longest to shortest mask + * so we have found the most specific match + */ + if ((&snp->smk_host.sin_addr)->s_addr == + (siap->s_addr & (&snp->smk_mask)->s_addr)) { + /* we have found the special CIPSO option */ + if (snp->smk_label == smack_cipso_option) + return NULL; + return snp->smk_label; + } + + return NULL; +} + +/** * smack_set_catset - convert a capset to netlabel mls categories * @catset: the Smack categories * @sap: where to put the netlabel categories @@ -1367,11 +1407,10 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) */ static int smack_netlabel(struct sock *sk, int labeled) { - struct socket_smack *ssp; + struct socket_smack *ssp = sk->sk_security; struct netlbl_lsm_secattr secattr; int rc = 0; - ssp = sk->sk_security; /* * Usually the netlabel code will handle changing the * packet labeling based on the label. @@ -1389,27 +1428,51 @@ static int smack_netlabel(struct sock *sk, int labeled) else { netlbl_secattr_init(&secattr); smack_to_secattr(ssp->smk_out, &secattr); - rc = netlbl_sock_setattr(sk, &secattr); + rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); netlbl_secattr_destroy(&secattr); } bh_unlock_sock(sk); local_bh_enable(); - /* - * Remember the label scheme used so that it is not - * necessary to do the netlabel setting if it has not - * changed the next time through. - * - * The -EDESTADDRREQ case is an indication that there's - * a single level host involved. - */ - if (rc == 0) - ssp->smk_labeled = labeled; return rc; } /** + * smack_netlbel_send - Set the secattr on a socket and perform access checks + * @sk: the socket + * @sap: the destination address + * + * Set the correct secattr for the given socket based on the destination + * address and perform any outbound access checks needed. + * + * Returns 0 on success or an error code. + * + */ +static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) +{ + int rc; + int sk_lbl; + char *hostsp; + struct socket_smack *ssp = sk->sk_security; + + rcu_read_lock(); + hostsp = smack_host_label(sap); + if (hostsp != NULL) { + sk_lbl = SMACK_UNLABELED_SOCKET; + rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); + } else { + sk_lbl = SMACK_CIPSO_SOCKET; + rc = 0; + } + rcu_read_unlock(); + if (rc != 0) + return rc; + + return smack_netlabel(sk, sk_lbl); +} + +/** * smack_inode_setsecurity - set smack xattrs * @inode: the object * @name: attribute name @@ -1430,7 +1493,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct socket *sock; int rc = 0; - if (value == NULL || size > SMK_LABELLEN) + if (value == NULL || size > SMK_LABELLEN || size == 0) return -EACCES; sp = smk_import(value, size); @@ -1490,41 +1553,6 @@ static int smack_socket_post_create(struct socket *sock, int family, return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); } - -/** - * smack_host_label - check host based restrictions - * @sip: the object end - * - * looks for host based access restrictions - * - * This version will only be appropriate for really small - * sets of single label hosts. - * - * Returns the label of the far end or NULL if it's not special. - */ -static char *smack_host_label(struct sockaddr_in *sip) -{ - struct smk_netlbladdr *snp; - struct in_addr *siap = &sip->sin_addr; - - if (siap->s_addr == 0) - return NULL; - - for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) { - /* - * we break after finding the first match because - * the list is sorted from longest to shortest mask - * so we have found the most specific match - */ - if ((&snp->smk_host.sin_addr)->s_addr == - (siap->s_addr & (&snp->smk_mask)->s_addr)) { - return snp->smk_label; - } - } - - return NULL; -} - /** * smack_socket_connect - connect access check * @sock: the socket @@ -1538,30 +1566,12 @@ static char *smack_host_label(struct sockaddr_in *sip) static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, int addrlen) { - struct socket_smack *ssp = sock->sk->sk_security; - char *hostsp; - int rc; - if (sock->sk == NULL || sock->sk->sk_family != PF_INET) return 0; - if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; - hostsp = smack_host_label((struct sockaddr_in *)sap); - if (hostsp == NULL) { - if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) - return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); - return 0; - } - - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); - if (rc != 0) - return rc; - - if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) - return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); - return 0; + return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); } /** @@ -1974,7 +1984,7 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) /** * smack_ipc_getsecid - Extract smack security id - * @ipcp: the object permissions + * @ipp: the object permissions * @secid: where result will be saved */ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) @@ -2251,7 +2261,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) /** * smack_socket_sendmsg - Smack check based on destination host * @sock: the socket - * @msghdr: the message + * @msg: the message * @size: the size of the message * * Return 0 if the current subject can write to the destination @@ -2262,9 +2272,6 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; - struct socket_smack *ssp = sock->sk->sk_security; - char *hostsp; - int rc; /* * Perfectly reasonable for this to be NULL @@ -2272,28 +2279,12 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, if (sip == NULL || sip->sin_family != PF_INET) return 0; - hostsp = smack_host_label(sip); - if (hostsp == NULL) { - if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) - return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); - return 0; - } - - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); - if (rc != 0) - return rc; - - if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) - return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); - - return 0; - + return smack_netlabel_send(sock->sk, sip); } /** - * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat - * pair to smack + * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack * @sap: netlabel secattr * @sip: where to put the result * @@ -2414,7 +2405,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * @sock: the socket * @optval: user's destination * @optlen: size thereof - * @len: max thereoe + * @len: max thereof * * returns zero on success, an error code otherwise */ @@ -2493,31 +2484,24 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, } /** - * smack_sock_graft - graft access state between two sockets - * @sk: fresh sock - * @parent: donor socket + * smack_sock_graft - Initialize a newly created socket with an existing sock + * @sk: child sock + * @parent: parent socket * - * Sets the netlabel socket state on sk from parent + * Set the smk_{in,out} state of an existing sock based on the process that + * is creating the new socket. */ static void smack_sock_graft(struct sock *sk, struct socket *parent) { struct socket_smack *ssp; - int rc; - if (sk == NULL) - return; - - if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + if (sk == NULL || + (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)) return; ssp = sk->sk_security; ssp->smk_in = ssp->smk_out = current_security(); - ssp->smk_packet[0] = '\0'; - - rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET); - if (rc != 0) - printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", - __func__, -rc); + /* cssp->smk_packet is already set in smack_inet_csk_clone() */ } /** @@ -2532,35 +2516,82 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { - struct netlbl_lsm_secattr skb_secattr; + u16 family = sk->sk_family; struct socket_smack *ssp = sk->sk_security; + struct netlbl_lsm_secattr secattr; + struct sockaddr_in addr; + struct iphdr *hdr; char smack[SMK_LABELLEN]; int rc; - if (skb == NULL) - return -EACCES; + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; - netlbl_secattr_init(&skb_secattr); - rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr); + netlbl_secattr_init(&secattr); + rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) - smack_from_secattr(&skb_secattr, smack); + smack_from_secattr(&secattr, smack); else strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); - netlbl_secattr_destroy(&skb_secattr); + netlbl_secattr_destroy(&secattr); + /* - * Receiving a packet requires that the other end - * be able to write here. Read access is not required. - * - * If the request is successful save the peer's label - * so that SO_PEERCRED can report it. + * Receiving a packet requires that the other end be able to write + * here. Read access is not required. */ rc = smk_access(smack, ssp->smk_in, MAY_WRITE); - if (rc == 0) - strncpy(ssp->smk_packet, smack, SMK_MAXLEN); + if (rc != 0) + return rc; + + /* + * Save the peer's label in the request_sock so we can later setup + * smk_packet in the child socket so that SO_PEERCRED can report it. + */ + req->peer_secid = smack_to_secid(smack); + + /* + * We need to decide if we want to label the incoming connection here + * if we do we only need to label the request_sock and the stack will + * propogate the wire-label to the sock when it is created. + */ + hdr = ip_hdr(skb); + addr.sin_addr.s_addr = hdr->saddr; + rcu_read_lock(); + if (smack_host_label(&addr) == NULL) { + rcu_read_unlock(); + netlbl_secattr_init(&secattr); + smack_to_secattr(smack, &secattr); + rc = netlbl_req_setattr(req, &secattr); + netlbl_secattr_destroy(&secattr); + } else { + rcu_read_unlock(); + netlbl_req_delattr(req); + } return rc; } +/** + * smack_inet_csk_clone - Copy the connection information to the new socket + * @sk: the new socket + * @req: the connection's request_sock + * + * Transfer the connection's peer label to the newly created socket. + */ +static void smack_inet_csk_clone(struct sock *sk, + const struct request_sock *req) +{ + struct socket_smack *ssp = sk->sk_security; + char *smack; + + if (req->peer_secid != 0) { + smack = smack_from_secid(req->peer_secid); + strncpy(ssp->smk_packet, smack, SMK_MAXLEN); + } else + ssp->smk_packet[0] = '\0'; +} + /* * Key management security hooks * @@ -2749,7 +2780,7 @@ static void smack_audit_rule_free(void *vrule) #endif /* CONFIG_AUDIT */ -/* +/** * smack_secid_to_secctx - return the smack label for a secid * @secid: incoming integer * @secdata: destination @@ -2766,7 +2797,7 @@ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) return 0; } -/* +/** * smack_secctx_to_secid - return the secid for a smack label * @secdata: smack label * @seclen: how long result is @@ -2780,11 +2811,10 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) return 0; } -/* +/** * smack_release_secctx - don't do anything. - * @key_ref: unused - * @context: unused - * @perm: unused + * @secdata: unused + * @seclen: unused * * Exists to make sure nothing gets done, and properly */ @@ -2913,6 +2943,7 @@ struct security_operations smack_ops = { .sk_free_security = smack_sk_free_security, .sock_graft = smack_sock_graft, .inet_conn_request = smack_inet_conn_request, + .inet_csk_clone = smack_inet_csk_clone, /* key management security hooks */ #ifdef CONFIG_KEYS @@ -2934,6 +2965,17 @@ struct security_operations smack_ops = { .release_secctx = smack_release_secctx, }; + +static __init void init_smack_know_list(void) +{ + list_add(&smack_known_huh.list, &smack_known_list); + list_add(&smack_known_hat.list, &smack_known_list); + list_add(&smack_known_star.list, &smack_known_list); + list_add(&smack_known_floor.list, &smack_known_list); + list_add(&smack_known_invalid.list, &smack_known_list); + list_add(&smack_known_web.list, &smack_known_list); +} + /** * smack_init - initialize the smack system * @@ -2954,6 +2996,8 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = &smack_known_floor.smk_known; + /* initilize the smack_know_list */ + init_smack_know_list(); /* * Initialize locks */ |