aboutsummaryrefslogtreecommitdiff
path: root/security/smack
diff options
context:
space:
mode:
Diffstat (limited to 'security/smack')
-rw-r--r--security/smack/smack.h32
-rw-r--r--security/smack/smack_access.c66
-rw-r--r--security/smack/smack_lsm.c282
-rw-r--r--security/smack/smackfs.c218
4 files changed, 352 insertions, 246 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 cfa19ca125e..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.
*/
@@ -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 84b62b5e9e2..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>
@@ -606,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);
@@ -1275,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;
@@ -1295,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
@@ -1365,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.
@@ -1387,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
@@ -1428,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);
@@ -1488,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
@@ -1536,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);
}
/**
@@ -2260,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
@@ -2270,22 +2279,7 @@ 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);
}
@@ -2490,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() */
}
/**
@@ -2529,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
*
@@ -2909,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
@@ -2930,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
*
@@ -2950,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 a1b57e4dba3..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;
@@ -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;
@@ -433,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;
}
/*
@@ -459,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 = '/';
@@ -558,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;
/*
@@ -638,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)
@@ -658,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);
@@ -702,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;
}
}
@@ -755,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;
@@ -789,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;
@@ -808,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;
@@ -827,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);