|
@@ -87,16 +87,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
|
|
*/
|
|
*/
|
|
int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
|
|
int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
|
|
|
|
|
|
-/*
|
|
|
|
- * Unless a process is running with this label even
|
|
|
|
- * having CAP_MAC_OVERRIDE isn't enough to grant
|
|
|
|
- * privilege to violate MAC policy. If no label is
|
|
|
|
- * designated (the NULL case) capabilities apply to
|
|
|
|
- * everyone. It is expected that the hat (^) label
|
|
|
|
- * will be used if any label is used.
|
|
|
|
- */
|
|
|
|
-struct smack_known *smack_onlycap;
|
|
|
|
-
|
|
|
|
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
|
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
|
/*
|
|
/*
|
|
* Allow one label to be unconfined. This is for
|
|
* Allow one label to be unconfined. This is for
|
|
@@ -338,8 +328,7 @@ static int smk_perm_from_str(const char *string)
|
|
* @import: if non-zero, import labels
|
|
* @import: if non-zero, import labels
|
|
* @len: label length limit
|
|
* @len: label length limit
|
|
*
|
|
*
|
|
- * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject
|
|
|
|
- * or object is missing.
|
|
|
|
|
|
+ * Returns 0 on success, appropriate error code on failure.
|
|
*/
|
|
*/
|
|
static int smk_fill_rule(const char *subject, const char *object,
|
|
static int smk_fill_rule(const char *subject, const char *object,
|
|
const char *access1, const char *access2,
|
|
const char *access1, const char *access2,
|
|
@@ -351,16 +340,16 @@ static int smk_fill_rule(const char *subject, const char *object,
|
|
|
|
|
|
if (import) {
|
|
if (import) {
|
|
rule->smk_subject = smk_import_entry(subject, len);
|
|
rule->smk_subject = smk_import_entry(subject, len);
|
|
- if (rule->smk_subject == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(rule->smk_subject))
|
|
|
|
+ return PTR_ERR(rule->smk_subject);
|
|
|
|
|
|
rule->smk_object = smk_import_entry(object, len);
|
|
rule->smk_object = smk_import_entry(object, len);
|
|
- if (rule->smk_object == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(rule->smk_object))
|
|
|
|
+ return PTR_ERR(rule->smk_object);
|
|
} else {
|
|
} else {
|
|
cp = smk_parse_smack(subject, len);
|
|
cp = smk_parse_smack(subject, len);
|
|
- if (cp == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(cp))
|
|
|
|
+ return PTR_ERR(cp);
|
|
skp = smk_find_entry(cp);
|
|
skp = smk_find_entry(cp);
|
|
kfree(cp);
|
|
kfree(cp);
|
|
if (skp == NULL)
|
|
if (skp == NULL)
|
|
@@ -368,8 +357,8 @@ static int smk_fill_rule(const char *subject, const char *object,
|
|
rule->smk_subject = skp;
|
|
rule->smk_subject = skp;
|
|
|
|
|
|
cp = smk_parse_smack(object, len);
|
|
cp = smk_parse_smack(object, len);
|
|
- if (cp == NULL)
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(cp))
|
|
|
|
+ return PTR_ERR(cp);
|
|
skp = smk_find_entry(cp);
|
|
skp = smk_find_entry(cp);
|
|
kfree(cp);
|
|
kfree(cp);
|
|
if (skp == NULL)
|
|
if (skp == NULL)
|
|
@@ -412,7 +401,7 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
|
|
* @import: if non-zero, import labels
|
|
* @import: if non-zero, import labels
|
|
* @tokens: numer of substrings expected in data
|
|
* @tokens: numer of substrings expected in data
|
|
*
|
|
*
|
|
- * Returns number of processed bytes on success, -1 on failure.
|
|
|
|
|
|
+ * Returns number of processed bytes on success, -ERRNO on failure.
|
|
*/
|
|
*/
|
|
static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
|
|
static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
|
|
int import, int tokens)
|
|
int import, int tokens)
|
|
@@ -431,7 +420,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
|
|
|
|
|
|
if (data[cnt] == '\0')
|
|
if (data[cnt] == '\0')
|
|
/* Unexpected end of data */
|
|
/* Unexpected end of data */
|
|
- return -1;
|
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
tok[i] = data + cnt;
|
|
tok[i] = data + cnt;
|
|
|
|
|
|
@@ -529,14 +518,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
|
|
while (cnt < count) {
|
|
while (cnt < count) {
|
|
if (format == SMK_FIXED24_FMT) {
|
|
if (format == SMK_FIXED24_FMT) {
|
|
rc = smk_parse_rule(data, &rule, 1);
|
|
rc = smk_parse_rule(data, &rule, 1);
|
|
- if (rc != 0) {
|
|
|
|
- rc = -EINVAL;
|
|
|
|
|
|
+ if (rc < 0)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
cnt = count;
|
|
cnt = count;
|
|
} else {
|
|
} else {
|
|
rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
|
|
rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
|
|
- if (rc <= 0) {
|
|
|
|
|
|
+ if (rc < 0)
|
|
|
|
+ goto out;
|
|
|
|
+ if (rc == 0) {
|
|
rc = -EINVAL;
|
|
rc = -EINVAL;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -567,23 +556,17 @@ static void *smk_seq_start(struct seq_file *s, loff_t *pos,
|
|
struct list_head *head)
|
|
struct list_head *head)
|
|
{
|
|
{
|
|
struct list_head *list;
|
|
struct list_head *list;
|
|
|
|
+ int i = *pos;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ for (list = rcu_dereference(list_next_rcu(head));
|
|
|
|
+ list != head;
|
|
|
|
+ list = rcu_dereference(list_next_rcu(list))) {
|
|
|
|
+ if (i-- == 0)
|
|
|
|
+ return list;
|
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
|
- * This is 0 the first time through.
|
|
|
|
- */
|
|
|
|
- if (s->index == 0)
|
|
|
|
- s->private = head;
|
|
|
|
-
|
|
|
|
- if (s->private == NULL)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- list = s->private;
|
|
|
|
- if (list_empty(list))
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- if (s->index == 0)
|
|
|
|
- return list->next;
|
|
|
|
- return list;
|
|
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
|
|
static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
|
|
@@ -591,17 +574,15 @@ static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
|
|
|
|
- if (list_is_last(list, head)) {
|
|
|
|
- s->private = NULL;
|
|
|
|
- return NULL;
|
|
|
|
- }
|
|
|
|
- s->private = list->next;
|
|
|
|
- return list->next;
|
|
|
|
|
|
+ ++*pos;
|
|
|
|
+ list = rcu_dereference(list_next_rcu(list));
|
|
|
|
+
|
|
|
|
+ return (list == head) ? NULL : list;
|
|
}
|
|
}
|
|
|
|
|
|
static void smk_seq_stop(struct seq_file *s, void *v)
|
|
static void smk_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
- /* No-op */
|
|
|
|
|
|
+ rcu_read_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
|
|
static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
|
|
@@ -661,7 +642,7 @@ static int load_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_master_list *smlp =
|
|
struct smack_master_list *smlp =
|
|
- list_entry(list, struct smack_master_list, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_master_list, list);
|
|
|
|
|
|
smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
|
|
smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
|
|
|
|
|
|
@@ -809,7 +790,7 @@ static int cipso_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_known *skp =
|
|
struct smack_known *skp =
|
|
- list_entry(list, struct smack_known, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_known, list);
|
|
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
|
|
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
|
|
char sep = '/';
|
|
char sep = '/';
|
|
int i;
|
|
int i;
|
|
@@ -915,8 +896,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
|
|
mutex_lock(&smack_cipso_lock);
|
|
mutex_lock(&smack_cipso_lock);
|
|
|
|
|
|
skp = smk_import_entry(rule, 0);
|
|
skp = smk_import_entry(rule, 0);
|
|
- if (skp == NULL)
|
|
|
|
|
|
+ if (IS_ERR(skp)) {
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
goto out;
|
|
goto out;
|
|
|
|
+ }
|
|
|
|
|
|
if (format == SMK_FIXED24_FMT)
|
|
if (format == SMK_FIXED24_FMT)
|
|
rule += SMK_LABELLEN;
|
|
rule += SMK_LABELLEN;
|
|
@@ -998,7 +981,7 @@ static int cipso2_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_known *skp =
|
|
struct smack_known *skp =
|
|
- list_entry(list, struct smack_known, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_known, list);
|
|
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
|
|
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
|
|
char sep = '/';
|
|
char sep = '/';
|
|
int i;
|
|
int i;
|
|
@@ -1082,7 +1065,7 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smk_netlbladdr *skp =
|
|
struct smk_netlbladdr *skp =
|
|
- list_entry(list, struct smk_netlbladdr, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smk_netlbladdr, list);
|
|
unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
|
|
unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
|
|
int maskn;
|
|
int maskn;
|
|
u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
|
|
u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
|
|
@@ -1237,8 +1220,8 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
|
*/
|
|
*/
|
|
if (smack[0] != '-') {
|
|
if (smack[0] != '-') {
|
|
skp = smk_import_entry(smack, 0);
|
|
skp = smk_import_entry(smack, 0);
|
|
- if (skp == NULL) {
|
|
|
|
- rc = -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(skp)) {
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
goto free_out;
|
|
goto free_out;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
@@ -1619,8 +1602,8 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
|
|
}
|
|
}
|
|
|
|
|
|
skp = smk_import_entry(data, count);
|
|
skp = smk_import_entry(data, count);
|
|
- if (skp == NULL) {
|
|
|
|
- rc = -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(skp)) {
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1643,34 +1626,79 @@ static const struct file_operations smk_ambient_ops = {
|
|
.llseek = default_llseek,
|
|
.llseek = default_llseek,
|
|
};
|
|
};
|
|
|
|
|
|
-/**
|
|
|
|
- * smk_read_onlycap - read() for smackfs/onlycap
|
|
|
|
- * @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
|
|
|
|
|
|
+/*
|
|
|
|
+ * Seq_file operations for /smack/onlycap
|
|
*/
|
|
*/
|
|
-static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
|
|
|
|
- size_t cn, loff_t *ppos)
|
|
|
|
|
|
+static void *onlycap_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
{
|
|
- char *smack = "";
|
|
|
|
- ssize_t rc = -EINVAL;
|
|
|
|
- int asize;
|
|
|
|
|
|
+ return smk_seq_start(s, pos, &smack_onlycap_list);
|
|
|
|
+}
|
|
|
|
|
|
- if (*ppos != 0)
|
|
|
|
- return 0;
|
|
|
|
|
|
+static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
|
+{
|
|
|
|
+ return smk_seq_next(s, v, pos, &smack_onlycap_list);
|
|
|
|
+}
|
|
|
|
|
|
- if (smack_onlycap != NULL)
|
|
|
|
- smack = smack_onlycap->smk_known;
|
|
|
|
|
|
+static int onlycap_seq_show(struct seq_file *s, void *v)
|
|
|
|
+{
|
|
|
|
+ struct list_head *list = v;
|
|
|
|
+ struct smack_onlycap *sop =
|
|
|
|
+ list_entry_rcu(list, struct smack_onlycap, list);
|
|
|
|
|
|
- asize = strlen(smack) + 1;
|
|
|
|
|
|
+ seq_puts(s, sop->smk_label->smk_known);
|
|
|
|
+ seq_putc(s, ' ');
|
|
|
|
|
|
- if (cn >= asize)
|
|
|
|
- rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- return rc;
|
|
|
|
|
|
+static const struct seq_operations onlycap_seq_ops = {
|
|
|
|
+ .start = onlycap_seq_start,
|
|
|
|
+ .next = onlycap_seq_next,
|
|
|
|
+ .show = onlycap_seq_show,
|
|
|
|
+ .stop = smk_seq_stop,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int smk_open_onlycap(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ return seq_open(file, &onlycap_seq_ops);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * smk_list_swap_rcu - swap public list with a private one in RCU-safe way
|
|
|
|
+ * The caller must hold appropriate mutex to prevent concurrent modifications
|
|
|
|
+ * to the public list.
|
|
|
|
+ * Private list is assumed to be not accessible to other threads yet.
|
|
|
|
+ *
|
|
|
|
+ * @public: public list
|
|
|
|
+ * @private: private list
|
|
|
|
+ */
|
|
|
|
+static void smk_list_swap_rcu(struct list_head *public,
|
|
|
|
+ struct list_head *private)
|
|
|
|
+{
|
|
|
|
+ struct list_head *first, *last;
|
|
|
|
+
|
|
|
|
+ if (list_empty(public)) {
|
|
|
|
+ list_splice_init_rcu(private, public, synchronize_rcu);
|
|
|
|
+ } else {
|
|
|
|
+ /* Remember public list before replacing it */
|
|
|
|
+ first = public->next;
|
|
|
|
+ last = public->prev;
|
|
|
|
+
|
|
|
|
+ /* Publish private list in place of public in RCU-safe way */
|
|
|
|
+ private->prev->next = public;
|
|
|
|
+ private->next->prev = public;
|
|
|
|
+ rcu_assign_pointer(public->next, private->next);
|
|
|
|
+ public->prev = private->prev;
|
|
|
|
+
|
|
|
|
+ synchronize_rcu();
|
|
|
|
+
|
|
|
|
+ /* When all readers are done with the old public list,
|
|
|
|
+ * attach it in place of private */
|
|
|
|
+ private->next = first;
|
|
|
|
+ private->prev = last;
|
|
|
|
+ first->prev = private;
|
|
|
|
+ last->next = private;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -1686,47 +1714,79 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
{
|
|
char *data;
|
|
char *data;
|
|
- struct smack_known *skp = smk_of_task(current->cred->security);
|
|
|
|
|
|
+ char *data_parse;
|
|
|
|
+ char *tok;
|
|
|
|
+ struct smack_known *skp;
|
|
|
|
+ struct smack_onlycap *sop;
|
|
|
|
+ struct smack_onlycap *sop2;
|
|
|
|
+ LIST_HEAD(list_tmp);
|
|
int rc = count;
|
|
int rc = count;
|
|
|
|
|
|
if (!smack_privileged(CAP_MAC_ADMIN))
|
|
if (!smack_privileged(CAP_MAC_ADMIN))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
|
|
|
|
- /*
|
|
|
|
- * This can be done using smk_access() but is done
|
|
|
|
- * explicitly for clarity. The smk_access() implementation
|
|
|
|
- * would use smk_access(smack_onlycap, MAY_WRITE)
|
|
|
|
- */
|
|
|
|
- if (smack_onlycap != NULL && smack_onlycap != skp)
|
|
|
|
- return -EPERM;
|
|
|
|
-
|
|
|
|
data = kzalloc(count + 1, GFP_KERNEL);
|
|
data = kzalloc(count + 1, GFP_KERNEL);
|
|
if (data == NULL)
|
|
if (data == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ if (copy_from_user(data, buf, count) != 0) {
|
|
|
|
+ kfree(data);
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ data_parse = data;
|
|
|
|
+ while ((tok = strsep(&data_parse, " ")) != NULL) {
|
|
|
|
+ if (!*tok)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ skp = smk_import_entry(tok, 0);
|
|
|
|
+ if (IS_ERR(skp)) {
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sop = kzalloc(sizeof(*sop), GFP_KERNEL);
|
|
|
|
+ if (sop == NULL) {
|
|
|
|
+ rc = -ENOMEM;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sop->smk_label = skp;
|
|
|
|
+ list_add_rcu(&sop->list, &list_tmp);
|
|
|
|
+ }
|
|
|
|
+ kfree(data);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
- * Should the null string be passed in unset the onlycap value.
|
|
|
|
- * This seems like something to be careful with as usually
|
|
|
|
- * smk_import only expects to return NULL for errors. It
|
|
|
|
- * is usually the case that a nullstring or "\n" would be
|
|
|
|
- * bad to pass to smk_import but in fact this is useful here.
|
|
|
|
|
|
+ * Clear the smack_onlycap on invalid label errors. This means
|
|
|
|
+ * that we can pass a null string to unset the onlycap value.
|
|
*
|
|
*
|
|
- * smk_import will also reject a label beginning with '-',
|
|
|
|
|
|
+ * Importing will also reject a label beginning with '-',
|
|
* so "-usecapabilities" will also work.
|
|
* so "-usecapabilities" will also work.
|
|
|
|
+ *
|
|
|
|
+ * But do so only on invalid label, not on system errors.
|
|
|
|
+ * The invalid label must be first to count as clearing attempt.
|
|
*/
|
|
*/
|
|
- if (copy_from_user(data, buf, count) != 0)
|
|
|
|
- rc = -EFAULT;
|
|
|
|
- else
|
|
|
|
- smack_onlycap = smk_import_entry(data, count);
|
|
|
|
|
|
+ if (rc == -EINVAL && list_empty(&list_tmp))
|
|
|
|
+ rc = count;
|
|
|
|
+
|
|
|
|
+ if (rc >= 0) {
|
|
|
|
+ mutex_lock(&smack_onlycap_lock);
|
|
|
|
+ smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
|
|
|
|
+ mutex_unlock(&smack_onlycap_lock);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list_for_each_entry_safe(sop, sop2, &list_tmp, list)
|
|
|
|
+ kfree(sop);
|
|
|
|
|
|
- kfree(data);
|
|
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static const struct file_operations smk_onlycap_ops = {
|
|
static const struct file_operations smk_onlycap_ops = {
|
|
- .read = smk_read_onlycap,
|
|
|
|
|
|
+ .open = smk_open_onlycap,
|
|
|
|
+ .read = seq_read,
|
|
.write = smk_write_onlycap,
|
|
.write = smk_write_onlycap,
|
|
- .llseek = default_llseek,
|
|
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = seq_release,
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
|
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
|
@@ -1773,6 +1833,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
|
|
size_t count, loff_t *ppos)
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
{
|
|
char *data;
|
|
char *data;
|
|
|
|
+ struct smack_known *skp;
|
|
int rc = count;
|
|
int rc = count;
|
|
|
|
|
|
if (!smack_privileged(CAP_MAC_ADMIN))
|
|
if (!smack_privileged(CAP_MAC_ADMIN))
|
|
@@ -1782,21 +1843,31 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
|
|
if (data == NULL)
|
|
if (data == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
+ if (copy_from_user(data, buf, count) != 0) {
|
|
|
|
+ rc = -EFAULT;
|
|
|
|
+ goto freeout;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
- * Should the null string be passed in unset the unconfined value.
|
|
|
|
- * This seems like something to be careful with as usually
|
|
|
|
- * smk_import only expects to return NULL for errors. It
|
|
|
|
- * is usually the case that a nullstring or "\n" would be
|
|
|
|
- * bad to pass to smk_import but in fact this is useful here.
|
|
|
|
|
|
+ * Clear the smack_unconfined on invalid label errors. This means
|
|
|
|
+ * that we can pass a null string to unset the unconfined value.
|
|
*
|
|
*
|
|
- * smk_import will also reject a label beginning with '-',
|
|
|
|
|
|
+ * Importing will also reject a label beginning with '-',
|
|
* so "-confine" will also work.
|
|
* so "-confine" will also work.
|
|
|
|
+ *
|
|
|
|
+ * But do so only on invalid label, not on system errors.
|
|
*/
|
|
*/
|
|
- if (copy_from_user(data, buf, count) != 0)
|
|
|
|
- rc = -EFAULT;
|
|
|
|
- else
|
|
|
|
- smack_unconfined = smk_import_entry(data, count);
|
|
|
|
|
|
+ skp = smk_import_entry(data, count);
|
|
|
|
+ if (PTR_ERR(skp) == -EINVAL)
|
|
|
|
+ skp = NULL;
|
|
|
|
+ else if (IS_ERR(skp)) {
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
|
|
+ goto freeout;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ smack_unconfined = skp;
|
|
|
|
|
|
|
|
+freeout:
|
|
kfree(data);
|
|
kfree(data);
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
@@ -1895,7 +1966,7 @@ static int load_self_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_rule *srp =
|
|
struct smack_rule *srp =
|
|
- list_entry(list, struct smack_rule, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_rule, list);
|
|
|
|
|
|
smk_rule_show(s, srp, SMK_LABELLEN);
|
|
smk_rule_show(s, srp, SMK_LABELLEN);
|
|
|
|
|
|
@@ -1980,7 +2051,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
|
|
res = smk_access(rule.smk_subject, rule.smk_object,
|
|
res = smk_access(rule.smk_subject, rule.smk_object,
|
|
rule.smk_access1, NULL);
|
|
rule.smk_access1, NULL);
|
|
else if (res != -ENOENT)
|
|
else if (res != -ENOENT)
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return res;
|
|
|
|
|
|
/*
|
|
/*
|
|
* smk_access() can return a value > 0 in the "bringup" case.
|
|
* smk_access() can return a value > 0 in the "bringup" case.
|
|
@@ -2024,7 +2095,7 @@ static int load2_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_master_list *smlp =
|
|
struct smack_master_list *smlp =
|
|
- list_entry(list, struct smack_master_list, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_master_list, list);
|
|
|
|
|
|
smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
|
|
smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
|
|
|
|
|
|
@@ -2101,7 +2172,7 @@ static int load_self2_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
{
|
|
struct list_head *list = v;
|
|
struct list_head *list = v;
|
|
struct smack_rule *srp =
|
|
struct smack_rule *srp =
|
|
- list_entry(list, struct smack_rule, list);
|
|
|
|
|
|
+ list_entry_rcu(list, struct smack_rule, list);
|
|
|
|
|
|
smk_rule_show(s, srp, SMK_LONGLABEL);
|
|
smk_rule_show(s, srp, SMK_LONGLABEL);
|
|
|
|
|
|
@@ -2209,8 +2280,8 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
|
|
}
|
|
}
|
|
|
|
|
|
cp = smk_parse_smack(data, count);
|
|
cp = smk_parse_smack(data, count);
|
|
- if (cp == NULL) {
|
|
|
|
- rc = -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(cp)) {
|
|
|
|
+ rc = PTR_ERR(cp);
|
|
goto free_out;
|
|
goto free_out;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2341,10 +2412,10 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf,
|
|
rc = -EFAULT;
|
|
rc = -EFAULT;
|
|
else {
|
|
else {
|
|
skp = smk_import_entry(data, count);
|
|
skp = smk_import_entry(data, count);
|
|
- if (skp == NULL)
|
|
|
|
- rc = -EINVAL;
|
|
|
|
|
|
+ if (IS_ERR(skp))
|
|
|
|
+ rc = PTR_ERR(skp);
|
|
else
|
|
else
|
|
- smack_syslog_label = smk_import_entry(data, count);
|
|
|
|
|
|
+ smack_syslog_label = skp;
|
|
}
|
|
}
|
|
|
|
|
|
kfree(data);
|
|
kfree(data);
|