summaryrefslogtreecommitdiff
path: root/security/smack/smack_lsm.c
diff options
context:
space:
mode:
authorJames Morris <jmorris@namei.org>2009-01-07 09:58:22 +1100
committerJames Morris <jmorris@namei.org>2009-01-07 09:58:22 +1100
commitac8cc0fa5395fe2278e305a4cbed48e90d88d878 (patch)
tree515f577bfddd054ee4373228be7c974dfb8133af /security/smack/smack_lsm.c
parent238c6d54830c624f34ac9cf123ac04aebfca5013 (diff)
parent3699c53c485bf0168e6500d0ed18bf931584dd7c (diff)
Merge branch 'next' into for-linus
Diffstat (limited to 'security/smack/smack_lsm.c')
-rw-r--r--security/smack/smack_lsm.c310
1 files changed, 255 insertions, 55 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 848212fd484..0278bc08304 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1277,6 +1277,7 @@ 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;
@@ -1341,45 +1342,69 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
struct smack_cipso cipso;
int rc;
- switch (smack_net_nltype) {
- case NETLBL_NLTYPE_CIPSOV4:
- nlsp->domain = smack;
- nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
+ nlsp->domain = smack;
+ nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
- rc = smack_to_cipso(smack, &cipso);
- if (rc == 0) {
- nlsp->attr.mls.lvl = cipso.smk_level;
- smack_set_catset(cipso.smk_catset, nlsp);
- } else {
- nlsp->attr.mls.lvl = smack_cipso_direct;
- smack_set_catset(smack, nlsp);
- }
- break;
- default:
- break;
+ rc = smack_to_cipso(smack, &cipso);
+ if (rc == 0) {
+ nlsp->attr.mls.lvl = cipso.smk_level;
+ smack_set_catset(cipso.smk_catset, nlsp);
+ } else {
+ nlsp->attr.mls.lvl = smack_cipso_direct;
+ smack_set_catset(smack, nlsp);
}
}
/**
* smack_netlabel - Set the secattr on a socket
* @sk: the socket
+ * @labeled: socket label scheme
*
* Convert the outbound smack value (smk_out) to a
* secattr and attach it to the socket.
*
* Returns 0 on success or an error code
*/
-static int smack_netlabel(struct sock *sk)
+static int smack_netlabel(struct sock *sk, int labeled)
{
struct socket_smack *ssp;
struct netlbl_lsm_secattr secattr;
- int rc;
+ int rc = 0;
ssp = sk->sk_security;
- netlbl_secattr_init(&secattr);
- smack_to_secattr(ssp->smk_out, &secattr);
- rc = netlbl_sock_setattr(sk, &secattr);
- netlbl_secattr_destroy(&secattr);
+ /*
+ * Usually the netlabel code will handle changing the
+ * packet labeling based on the label.
+ * The case of a single label host is different, because
+ * a single label host should never get a labeled packet
+ * even though the label is usually associated with a packet
+ * label.
+ */
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+
+ if (ssp->smk_out == smack_net_ambient ||
+ labeled == SMACK_UNLABELED_SOCKET)
+ netlbl_sock_delattr(sk);
+ else {
+ netlbl_secattr_init(&secattr);
+ smack_to_secattr(ssp->smk_out, &secattr);
+ rc = netlbl_sock_setattr(sk, &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;
}
@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
ssp->smk_in = sp;
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = sp;
- rc = smack_netlabel(sock->sk);
+ rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc);
@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family,
/*
* Set the outbound netlbl.
*/
- return smack_netlabel(sock->sk);
+ 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. Because of the masking
+ * it cannot shortcut out on the first match. There are
+ * numerious ways to address the problem, but none of them
+ * have been applied here.
+ *
+ * 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;
+ char *bestlabel = NULL;
+ struct in_addr *siap = &sip->sin_addr;
+ struct in_addr *liap;
+ struct in_addr *miap;
+ struct in_addr bestmask;
+
+ if (siap->s_addr == 0)
+ return NULL;
+
+ bestmask.s_addr = 0;
+
+ for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) {
+ liap = &snp->smk_host.sin_addr;
+ miap = &snp->smk_mask;
+ /*
+ * If the addresses match after applying the list entry mask
+ * the entry matches the address. If it doesn't move along to
+ * the next entry.
+ */
+ if ((liap->s_addr & miap->s_addr) !=
+ (siap->s_addr & miap->s_addr))
+ continue;
+ /*
+ * If the list entry mask identifies a single address
+ * it can't get any more specific.
+ */
+ if (miap->s_addr == 0xffffffff)
+ return snp->smk_label;
+ /*
+ * If the list entry mask is less specific than the best
+ * already found this entry is uninteresting.
+ */
+ if ((miap->s_addr | bestmask.s_addr) == bestmask.s_addr)
+ continue;
+ /*
+ * This is better than any entry found so far.
+ */
+ bestmask.s_addr = miap->s_addr;
+ bestlabel = snp->smk_label;
+ }
+
+ return bestlabel;
+}
+
+/**
+ * smack_socket_connect - connect access check
+ * @sock: the socket
+ * @sap: the other end
+ * @addrlen: size of sap
+ *
+ * Verifies that a connection may be possible
+ *
+ * Returns 0 on success, and error code otherwise
+ */
+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;
}
/**
@@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == NULL)
return -EINVAL;
+ /*
+ * No process is ever allowed the web ("@") label.
+ */
+ if (newsmack == smack_known_web.smk_known)
+ return -EPERM;
+
new = prepare_creds();
- if (!new)
+ if (new == NULL)
return -ENOMEM;
new->security = newsmack;
commit_creds(new);
@@ -2144,6 +2276,49 @@ 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
+ * @size: the size of the message
+ *
+ * Return 0 if the current subject can write to the destination
+ * host. This is only a question if the destination is a single
+ * label host.
+ */
+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
+ */
+ 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;
+
+}
+
+
+/**
* smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
* pair to smack
* @sap: netlabel secattr
@@ -2154,44 +2329,66 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
{
char smack[SMK_LABELLEN];
+ char *sp;
int pcat;
- if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) {
+ if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/*
+ * Looks like a CIPSO packet.
* If there are flags but no level netlabel isn't
* behaving the way we expect it to.
*
+ * Get the categories, if any
* Without guidance regarding the smack value
* for the packet fall back on the network
* ambient value.
*/
- strncpy(sip, smack_net_ambient, SMK_MAXLEN);
+ memset(smack, '\0', SMK_LABELLEN);
+ if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
+ for (pcat = -1;;) {
+ pcat = netlbl_secattr_catmap_walk(
+ sap->attr.mls.cat, pcat + 1);
+ if (pcat < 0)
+ break;
+ smack_catset_bit(pcat, smack);
+ }
+ /*
+ * If it is CIPSO using smack direct mapping
+ * we are already done. WeeHee.
+ */
+ if (sap->attr.mls.lvl == smack_cipso_direct) {
+ memcpy(sip, smack, SMK_MAXLEN);
+ return;
+ }
+ /*
+ * Look it up in the supplied table if it is not
+ * a direct mapping.
+ */
+ smack_from_cipso(sap->attr.mls.lvl, smack, sip);
return;
}
- /*
- * Get the categories, if any
- */
- memset(smack, '\0', SMK_LABELLEN);
- if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
- for (pcat = -1;;) {
- pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat,
- pcat + 1);
- if (pcat < 0)
- break;
- smack_catset_bit(pcat, smack);
- }
- /*
- * If it is CIPSO using smack direct mapping
- * we are already done. WeeHee.
- */
- if (sap->attr.mls.lvl == smack_cipso_direct) {
- memcpy(sip, smack, SMK_MAXLEN);
+ if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
+ /*
+ * Looks like a fallback, which gives us a secid.
+ */
+ sp = smack_from_secid(sap->attr.secid);
+ /*
+ * This has got to be a bug because it is
+ * impossible to specify a fallback without
+ * specifying the label, which will ensure
+ * it has a secid, and the only way to get a
+ * secid is from a fallback.
+ */
+ BUG_ON(sp == NULL);
+ strncpy(sip, sp, SMK_MAXLEN);
return;
}
/*
- * Look it up in the supplied table if it is not a direct mapping.
+ * Without guidance regarding the smack value
+ * for the packet fall back on the network
+ * ambient value.
*/
- smack_from_cipso(sap->attr.mls.lvl, smack, sip);
+ strncpy(sip, smack_net_ambient, SMK_MAXLEN);
return;
}
@@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = sk->sk_security;
char smack[SMK_LABELLEN];
+ char *csp;
int rc;
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
@@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
/*
* Translate what netlabel gave us.
*/
- memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr);
+
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
- if (rc == 0)
+ if (rc == 0) {
smack_from_secattr(&secattr, smack);
- else
- strncpy(smack, smack_net_ambient, SMK_MAXLEN);
+ csp = smack;
+ } else
+ csp = smack_net_ambient;
+
netlbl_secattr_destroy(&secattr);
+
/*
* Receiving a packet requires that the other end
* be able to write here. Read access is not required.
* This is the simplist possible security model
* for networking.
*/
- rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+ rc = smk_access(csp, ssp->smk_in, MAY_WRITE);
if (rc != 0)
netlbl_skbuff_err(skb, rc, 0);
return rc;
@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
/*
* Translate what netlabel gave us.
*/
- memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0)
@@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
ssp->smk_in = ssp->smk_out = current_security();
ssp->smk_packet[0] = '\0';
- rc = smack_netlabel(sk);
+ rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET);
if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc);
@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
if (skb == NULL)
return -EACCES;
- memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&skb_secattr);
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);
if (rc == 0)
@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = {
.unix_may_send = smack_unix_may_send,
.socket_post_create = smack_socket_post_create,
+ .socket_connect = smack_socket_connect,
+ .socket_sendmsg = smack_socket_sendmsg,
.socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
.socket_getpeersec_stream = smack_socket_getpeersec_stream,
.socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
@@ -2783,7 +2984,6 @@ static __init int smack_init(void)
/*
* Initialize locks
*/
- spin_lock_init(&smack_known_unset.smk_cipsolock);
spin_lock_init(&smack_known_huh.smk_cipsolock);
spin_lock_init(&smack_known_hat.smk_cipsolock);
spin_lock_init(&smack_known_star.smk_cipsolock);