SMK_AMBIENT = 7, /* internet ambient label */
SMK_NETLBLADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */
+ SMK_LOGGING = 10, /* logging */
};
/*
* 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
{
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);
/* No-op */
}
-static struct seq_operations load_seq_ops = {
+static const struct seq_operations load_seq_ops = {
.start = load_seq_start,
.next = load_seq_next,
.show = load_seq_show,
*/
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;
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;
*/
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);
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]) {
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]) {
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]) {
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;
{
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;
}
/*
*/
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 = '/';
/* No-op */
}
-static struct seq_operations cipso_seq_ops = {
+static const struct seq_operations cipso_seq_ops = {
.start = cipso_seq_start,
.stop = cipso_seq_stop,
.next = cipso_seq_next,
goto unlockedout;
}
+ /* labels cannot begin with a '-' */
+ if (data[0] == '-') {
+ rc = -EINVAL;
+ goto unlockedout;
+ }
data[count] = '\0';
rule = data;
/*
{
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)
*/
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);
/* No-op */
}
-static struct seq_operations netlbladdr_seq_ops = {
+static const struct seq_operations netlbladdr_seq_ops = {
.start = netlbladdr_seq_start,
.stop = netlbladdr_seq_stop,
.next = netlbladdr_seq_next,
*
* 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(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(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;
}
}
struct sockaddr_in newname;
char smack[SMK_LABELLEN];
char *sp;
- char data[SMK_NETLBLADDRMAX];
+ char data[SMK_NETLBLADDRMAX + 1];
char *host = (char *)&newname.sin_addr.s_addr;
int rc;
struct netlbl_audit audit_info;
struct in_addr mask;
unsigned int m;
+ int found;
u32 mask_bits = (1<<31);
__be32 nsa;
u32 temp_mask;
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;
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;
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);
};
/**
+ * smk_read_logging - read() for /smack/logging
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @cn: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_logging(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[32];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d\n", log_policy);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+ return rc;
+}
+
+/**
+ * smk_write_logging - write() for /smack/logging
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_logging(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[32];
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= sizeof(temp) || count == 0)
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ temp[count] = '\0';
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+ if (i < 0 || i > 3)
+ return -EINVAL;
+ log_policy = i;
+ return count;
+}
+
+
+
+static const struct file_operations smk_logging_ops = {
+ .read = smk_read_logging,
+ .write = smk_write_logging,
+};
+/**
* smk_fill_super - fill the /smackfs superblock
* @sb: the empty superblock
* @data: unused
{"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] =
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
+ [SMK_LOGGING] =
+ {"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
/* last one */ {""}
};