|
@@ -80,10 +80,11 @@ 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;
|
|
|
|
|
|
#define SEQ_READ_FINISHED 1
|
|
|
|
|
@@ -134,24 +135,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 +216,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 +256,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 +267,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 +280,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 +312,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 +323,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 +334,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 +436,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 +464,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 = '/';
|
|
@@ -638,18 +645,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 +668,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 +714,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 +773,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;
|
|
@@ -808,14 +827,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;
|