diff options
Diffstat (limited to 'security/smack')
-rw-r--r-- | security/smack/smack.h | 32 | ||||
-rw-r--r-- | security/smack/smack_access.c | 70 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 334 | ||||
-rw-r--r-- | security/smack/smackfs.c | 233 |
4 files changed, 386 insertions, 283 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h index b79582e4fbf..42ef313f985 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -18,6 +18,8 @@ #include <linux/security.h> #include <linux/in.h> #include <net/netlabel.h> +#include <linux/list.h> +#include <linux/rculist.h> /* * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is @@ -40,7 +42,6 @@ struct superblock_smack { struct socket_smack { char *smk_out; /* outbound label */ char *smk_in; /* inbound label */ - int smk_labeled; /* label scheme */ char smk_packet[SMK_LABELLEN]; /* TCP peer label */ }; @@ -59,17 +60,10 @@ struct inode_smack { * A label access rule. */ struct smack_rule { - char *smk_subject; - char *smk_object; - int smk_access; -}; - -/* - * An entry in the table of permitted label accesses. - */ -struct smk_list_entry { - struct smk_list_entry *smk_next; - struct smack_rule smk_rule; + struct list_head list; + char *smk_subject; + char *smk_object; + int smk_access; }; /* @@ -85,7 +79,7 @@ struct smack_cipso { * An entry in the table identifying hosts. */ struct smk_netlbladdr { - struct smk_netlbladdr *smk_next; + struct list_head list; struct sockaddr_in smk_host; /* network address */ struct in_addr smk_mask; /* network mask */ char *smk_label; /* label */ @@ -113,7 +107,7 @@ struct smk_netlbladdr { * the cipso direct mapping in used internally. */ struct smack_known { - struct smack_known *smk_next; + struct list_head list; char smk_known[SMK_LABELLEN]; u32 smk_secid; struct smack_cipso *smk_cipso; @@ -138,6 +132,8 @@ struct smack_known { #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT +#define SMACK_CIPSO_OPTION "-CIPSO" + /* * How communications on this socket are treated. * Usually it's determined by the underlying netlabel code @@ -205,8 +201,8 @@ u32 smack_to_secid(const char *); extern int smack_cipso_direct; extern char *smack_net_ambient; extern char *smack_onlycap; +extern const char *smack_cipso_option; -extern struct smack_known *smack_known; extern struct smack_known smack_known_floor; extern struct smack_known smack_known_hat; extern struct smack_known smack_known_huh; @@ -214,8 +210,10 @@ extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; -extern struct smk_list_entry *smack_list; -extern struct smk_netlbladdr *smack_netlbladdrs; +extern struct list_head smack_known_list; +extern struct list_head smack_rule_list; +extern struct list_head smk_netlbladdr_list; + extern struct security_operations smack_ops; /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 2e0b83e77ff..ac0a2707f6d 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -16,48 +16,42 @@ #include "smack.h" struct smack_known smack_known_huh = { - .smk_next = NULL, .smk_known = "?", .smk_secid = 2, .smk_cipso = NULL, }; struct smack_known smack_known_hat = { - .smk_next = &smack_known_huh, .smk_known = "^", .smk_secid = 3, .smk_cipso = NULL, }; struct smack_known smack_known_star = { - .smk_next = &smack_known_hat, .smk_known = "*", .smk_secid = 4, .smk_cipso = NULL, }; struct smack_known smack_known_floor = { - .smk_next = &smack_known_star, .smk_known = "_", .smk_secid = 5, .smk_cipso = NULL, }; struct smack_known smack_known_invalid = { - .smk_next = &smack_known_floor, .smk_known = "", .smk_secid = 6, .smk_cipso = NULL, }; struct smack_known smack_known_web = { - .smk_next = &smack_known_invalid, .smk_known = "@", .smk_secid = 7, .smk_cipso = NULL, }; -struct smack_known *smack_known = &smack_known_web; +LIST_HEAD(smack_known_list); /* * The initial value needs to be bigger than any of the @@ -87,7 +81,6 @@ static u32 smack_next_secid = 10; int smk_access(char *subject_label, char *object_label, int request) { u32 may = MAY_NOT; - struct smk_list_entry *sp; struct smack_rule *srp; /* @@ -139,9 +132,8 @@ int smk_access(char *subject_label, char *object_label, int request) * access (e.g. read is included in readwrite) it's * good. */ - for (sp = smack_list; sp != NULL; sp = sp->smk_next) { - srp = &sp->smk_rule; - + rcu_read_lock(); + list_for_each_entry_rcu(srp, &smack_rule_list, list) { if (srp->smk_subject == subject_label || strcmp(srp->smk_subject, subject_label) == 0) { if (srp->smk_object == object_label || @@ -151,6 +143,7 @@ int smk_access(char *subject_label, char *object_label, int request) } } } + rcu_read_unlock(); /* * This is a bit map operation. */ @@ -162,8 +155,8 @@ int smk_access(char *subject_label, char *object_label, int request) /** * smk_curacc - determine if current has a specific access to an object - * @object_label: a pointer to the object's Smack label - * @request: the access requested, in "MAY" format + * @obj_label: a pointer to the object's Smack label + * @mode: the access requested, in "MAY" format * * This function checks the current subject label/object label pair * in the access rule list and returns 0 if the access is permitted, @@ -228,14 +221,17 @@ struct smack_known *smk_import_entry(const char *string, int len) mutex_lock(&smack_known_lock); - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + found = 0; + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + found = 1; break; + } + } - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); if (skp != NULL) { - skp->smk_next = smack_known; strncpy(skp->smk_known, smack, SMK_MAXLEN); skp->smk_secid = smack_next_secid++; skp->smk_cipso = NULL; @@ -244,8 +240,7 @@ struct smack_known *smk_import_entry(const char *string, int len) * Make sure that the entry is actually * filled before putting it on the list. */ - smp_mb(); - smack_known = skp; + list_add_rcu(&skp->list, &smack_known_list); } } @@ -266,6 +261,9 @@ char *smk_import(const char *string, int len) { struct smack_known *skp; + /* labels cannot begin with a '-' */ + if (string[0] == '-') + return NULL; skp = smk_import_entry(string, len); if (skp == NULL) return NULL; @@ -283,14 +281,19 @@ char *smack_from_secid(const u32 secid) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (skp->smk_secid == secid) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (skp->smk_secid == secid) { + rcu_read_unlock(); return skp->smk_known; + } + } /* * If we got this far someone asked for the translation * of a secid that is not on the list. */ + rcu_read_unlock(); return smack_known_invalid.smk_known; } @@ -305,9 +308,14 @@ u32 smack_to_secid(const char *smack) { struct smack_known *skp; - for (skp = smack_known; skp != NULL; skp = skp->smk_next) - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + rcu_read_unlock(); return skp->smk_secid; + } + } + rcu_read_unlock(); return 0; } @@ -332,7 +340,8 @@ void smack_from_cipso(u32 level, char *cp, char *result) struct smack_known *kp; char *final = NULL; - for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) { + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { if (kp->smk_cipso == NULL) continue; @@ -344,6 +353,7 @@ void smack_from_cipso(u32 level, char *cp, char *result) spin_unlock_bh(&kp->smk_cipsolock); } + rcu_read_unlock(); if (final == NULL) final = smack_known_huh.smk_known; strncpy(result, final, SMK_MAXLEN); @@ -360,13 +370,19 @@ void smack_from_cipso(u32 level, char *cp, char *result) int smack_to_cipso(const char *smack, struct smack_cipso *cp) { struct smack_known *kp; + int found = 0; - for (kp = smack_known; kp != NULL; kp = kp->smk_next) + rcu_read_lock(); + list_for_each_entry_rcu(kp, &smack_known_list, list) { if (kp->smk_known == smack || - strcmp(kp->smk_known, smack) == 0) + strcmp(kp->smk_known, smack) == 0) { + found = 1; break; + } + } + rcu_read_unlock(); - if (kp == NULL || kp->smk_cipso == NULL) + if (found == 0 || kp->smk_cipso == NULL) return -ENOENT; memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso)); 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 */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 51f0efc50da..e03a7e19c73 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -80,10 +80,14 @@ char *smack_onlycap; * Packets are sent there unlabeled, but only from tasks that * can write to the specified label. */ -struct smk_netlbladdr *smack_netlbladdrs; + +LIST_HEAD(smk_netlbladdr_list); +LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; -struct smk_list_entry *smack_list; + +const char *smack_cipso_option = SMACK_CIPSO_OPTION; + #define SEQ_READ_FINISHED 1 @@ -134,24 +138,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_list; + if (list_empty(&smack_rule_list)) + return NULL; + return smack_rule_list.next; } static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smack_rule_list)) { *pos = SEQ_READ_FINISHED; - - return skp; + return NULL; + } + return list->next; } static int load_seq_show(struct seq_file *s, void *v) { - struct smk_list_entry *slp = (struct smk_list_entry *) v; - struct smack_rule *srp = &slp->smk_rule; + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); seq_printf(s, "%s %s", (char *)srp->smk_subject, (char *)srp->smk_object); @@ -212,32 +219,23 @@ static int smk_open_load(struct inode *inode, struct file *file) */ static int smk_set_access(struct smack_rule *srp) { - struct smk_list_entry *sp; - struct smk_list_entry *newp; + struct smack_rule *sp; int ret = 0; - + int found; mutex_lock(&smack_list_lock); - for (sp = smack_list; sp != NULL; sp = sp->smk_next) - if (sp->smk_rule.smk_subject == srp->smk_subject && - sp->smk_rule.smk_object == srp->smk_object) { - sp->smk_rule.smk_access = srp->smk_access; + found = 0; + list_for_each_entry_rcu(sp, &smack_rule_list, list) { + if (sp->smk_subject == srp->smk_subject && + sp->smk_object == srp->smk_object) { + found = 1; + sp->smk_access = srp->smk_access; break; } - - if (sp == NULL) { - newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL); - if (newp == NULL) { - ret = -ENOMEM; - goto out; - } - - newp->smk_rule = *srp; - newp->smk_next = smack_list; - smack_list = newp; } + if (found == 0) + list_add_rcu(&srp->list, &smack_rule_list); -out: mutex_unlock(&smack_list_lock); return ret; @@ -245,7 +243,7 @@ out: /** * smk_write_load - write() for /smack/load - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start - must be 0 @@ -261,7 +259,7 @@ out: static ssize_t smk_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct smack_rule rule; + struct smack_rule *rule; char *data; int rc = -EINVAL; @@ -272,9 +270,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, */ if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (*ppos != 0) - return -EINVAL; - if (count != SMK_LOADLEN) + + if (*ppos != 0 || count != SMK_LOADLEN) return -EINVAL; data = kzalloc(count, GFP_KERNEL); @@ -286,25 +283,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, goto out; } - rule.smk_subject = smk_import(data, 0); - if (rule.smk_subject == NULL) + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (rule == NULL) { + rc = -ENOMEM; goto out; + } - rule.smk_object = smk_import(data + SMK_LABELLEN, 0); - if (rule.smk_object == NULL) - goto out; + rule->smk_subject = smk_import(data, 0); + if (rule->smk_subject == NULL) + goto out_free_rule; - rule.smk_access = 0; + rule->smk_object = smk_import(data + SMK_LABELLEN, 0); + if (rule->smk_object == NULL) + goto out_free_rule; + + rule->smk_access = 0; switch (data[SMK_LABELLEN + SMK_LABELLEN]) { case '-': break; case 'r': case 'R': - rule.smk_access |= MAY_READ; + rule->smk_access |= MAY_READ; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { @@ -312,10 +315,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'w': case 'W': - rule.smk_access |= MAY_WRITE; + rule->smk_access |= MAY_WRITE; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { @@ -323,10 +326,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'x': case 'X': - rule.smk_access |= MAY_EXEC; + rule->smk_access |= MAY_EXEC; break; default: - goto out; + goto out_free_rule; } switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { @@ -334,17 +337,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, break; case 'a': case 'A': - rule.smk_access |= MAY_APPEND; + rule->smk_access |= MAY_APPEND; break; default: - goto out; + goto out_free_rule; } - rc = smk_set_access(&rule); + rc = smk_set_access(rule); if (!rc) rc = count; + goto out; +out_free_rule: + kfree(rule); out: kfree(data); return rc; @@ -402,6 +408,7 @@ static void smk_cipso_doi(void) /** * smk_unlbl_ambient - initialize the unlabeled domain + * @oldambient: previous domain string */ static void smk_unlbl_ambient(char *oldambient) { @@ -432,24 +439,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; + if (list_empty(&smack_known_list)) + return NULL; - return smack_known; + return smack_known_list.next; } static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smack_known *skp = ((struct smack_known *) v)->smk_next; + struct list_head *list = v; /* - * Omit labels with no associated cipso value + * labels with no associated cipso value wont be printed + * in cipso_seq_show */ - while (skp != NULL && !skp->smk_cipso) - skp = skp->smk_next; - - if (skp == NULL) + if (list_is_last(list, &smack_known_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } /* @@ -458,7 +467,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int cipso_seq_show(struct seq_file *s, void *v) { - struct smack_known *skp = (struct smack_known *) v; + struct list_head *list = v; + struct smack_known *skp = + list_entry(list, struct smack_known, list); struct smack_cipso *scp = skp->smk_cipso; char *cbp; char sep = '/'; @@ -513,7 +524,7 @@ static int smk_open_cipso(struct inode *inode, struct file *file) /** * smk_write_cipso - write() for /smack/cipso - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start @@ -557,6 +568,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, goto unlockedout; } + /* labels cannot begin with a '-' */ + if (data[0] == '-') { + rc = -EINVAL; + goto unlockedout; + } data[count] = '\0'; rule = data; /* @@ -637,18 +653,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) { if (*pos == SEQ_READ_FINISHED) return NULL; - - return smack_netlbladdrs; + if (list_empty(&smk_netlbladdr_list)) + return NULL; + return smk_netlbladdr_list.next; } static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; + struct list_head *list = v; - if (skp == NULL) + if (list_is_last(list, &smk_netlbladdr_list)) { *pos = SEQ_READ_FINISHED; + return NULL; + } - return skp; + return list->next; } #define BEBITS (sizeof(__be32) * 8) @@ -657,7 +676,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) */ static int netlbladdr_seq_show(struct seq_file *s, void *v) { - struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; + struct list_head *list = v; + struct smk_netlbladdr *skp = + list_entry(list, struct smk_netlbladdr, list); unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; int maskn; u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr); @@ -701,30 +722,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file) * * This helper insert netlabel in the smack_netlbladdrs list * sorted by netmask length (longest to smallest) + * locked by &smk_netlbladdr_lock in smk_write_netlbladdr + * */ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) { - struct smk_netlbladdr *m; + struct smk_netlbladdr *m, *m_next; - if (smack_netlbladdrs == NULL) { - smack_netlbladdrs = new; + if (list_empty(&smk_netlbladdr_list)) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } + m = list_entry(rcu_dereference(smk_netlbladdr_list.next), + struct smk_netlbladdr, list); + /* the comparison '>' is a bit hacky, but works */ - if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) { - new->smk_next = smack_netlbladdrs; - smack_netlbladdrs = new; + if (new->smk_mask.s_addr > m->smk_mask.s_addr) { + list_add_rcu(&new->list, &smk_netlbladdr_list); return; } - for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) { - if (m->smk_next == NULL) { - m->smk_next = new; + + list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) { + if (list_is_last(&m->list, &smk_netlbladdr_list)) { + list_add_rcu(&new->list, &m->list); return; } - if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) { - new->smk_next = m->smk_next; - m->smk_next = new; + m_next = list_entry(rcu_dereference(m->list.next), + struct smk_netlbladdr, list); + if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) { + list_add_rcu(&new->list, &m->list); return; } } @@ -733,7 +760,7 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new) /** * smk_write_netlbladdr - write() for /smack/netlabel - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start @@ -754,6 +781,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, struct netlbl_audit audit_info; struct in_addr mask; unsigned int m; + int found; u32 mask_bits = (1<<31); __be32 nsa; u32 temp_mask; @@ -788,9 +816,18 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, if (m > BEBITS) return -EINVAL; - sp = smk_import(smack, 0); - if (sp == NULL) - return -EINVAL; + /* if smack begins with '-', its an option, don't import it */ + if (smack[0] != '-') { + sp = smk_import(smack, 0); + if (sp == NULL) + return -EINVAL; + } else { + /* check known options */ + if (strcmp(smack, smack_cipso_option) == 0) + sp = (char *)smack_cipso_option; + else + return -EINVAL; + } for (temp_mask = 0; m > 0; m--) { temp_mask |= mask_bits; @@ -807,14 +844,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, nsa = newname.sin_addr.s_addr; /* try to find if the prefix is already in the list */ - for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) + found = 0; + list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { if (skp->smk_host.sin_addr.s_addr == nsa && - skp->smk_mask.s_addr == mask.s_addr) + skp->smk_mask.s_addr == mask.s_addr) { + found = 1; break; - + } + } smk_netlabel_audit_set(&audit_info); - if (skp == NULL) { + if (found == 0) { skp = kzalloc(sizeof(*skp), GFP_KERNEL); if (skp == NULL) rc = -ENOMEM; @@ -826,18 +866,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, smk_netlbladdr_insert(skp); } } else { - rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, - &skp->smk_host.sin_addr, &skp->smk_mask, - PF_INET, &audit_info); + /* we delete the unlabeled entry, only if the previous label + * wasnt the special CIPSO option */ + if (skp->smk_label != smack_cipso_option) + rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, + &skp->smk_host.sin_addr, &skp->smk_mask, + PF_INET, &audit_info); + else + rc = 0; skp->smk_label = sp; } /* * Now tell netlabel about the single label nature of * this host so that incoming packets get labeled. + * but only if we didn't get the special CIPSO option */ - - if (rc == 0) + if (rc == 0 && sp != smack_cipso_option) rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, smack_to_secid(skp->smk_label), &audit_info); @@ -884,7 +929,7 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf, /** * smk_write_doi - write() for /smack/doi - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start @@ -949,7 +994,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf, /** * smk_write_direct - write() for /smack/direct - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start @@ -1024,7 +1069,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, /** * smk_write_ambient - write() for /smack/ambient - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start @@ -1099,7 +1144,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, /** * smk_write_onlycap - write() for /smack/onlycap - * @filp: file pointer, not actually used + * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start |