aboutsummaryrefslogtreecommitdiff
path: root/security/smack
diff options
context:
space:
mode:
authorLinus Torvalds2013-09-07 14:34:07 -0700
committerLinus Torvalds2013-09-07 14:34:07 -0700
commit11c7b03d42a847db90862d0f9d8be6ce9b2f0553 (patch)
tree9c1d9e95c8ae09b4ee44821a9e570247d5a85a72 /security/smack
parent6be48f2940af9ea8d93c23a0dd8e322672c92efd (diff)
parent73203361468894c3c017bfbdd9ddcbb468039604 (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Nothing major for this kernel, just maintenance updates" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (21 commits) apparmor: add the ability to report a sha1 hash of loaded policy apparmor: export set of capabilities supported by the apparmor module apparmor: add the profile introspection file to interface apparmor: add an optional profile attachment string for profiles apparmor: add interface files for profiles and namespaces apparmor: allow setting any profile into the unconfined state apparmor: make free_profile available outside of policy.c apparmor: rework namespace free path apparmor: update how unconfined is handled apparmor: change how profile replacement update is done apparmor: convert profile lists to RCU based locking apparmor: provide base for multiple profiles to be replaced at once apparmor: add a features/policy dir to interface apparmor: enable users to query whether apparmor is enabled apparmor: remove minimum size check for vmalloc() Smack: parse multiple rules per write to load2, up to PAGE_SIZE-1 bytes Smack: network label match fix security: smack: add a hash table to quicken smk_find_entry() security: smack: fix memleak in smk_write_rules_list() xattr: Constify ->name member of "struct xattr". ...
Diffstat (limited to 'security/smack')
-rw-r--r--security/smack/smack.h13
-rw-r--r--security/smack/smack_access.c29
-rw-r--r--security/smack/smack_lsm.c51
-rw-r--r--security/smack/smackfs.c180
4 files changed, 153 insertions, 120 deletions
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 339614c76e63..076b8e8a51ab 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -53,6 +53,7 @@
*/
struct smack_known {
struct list_head list;
+ struct hlist_node smk_hashed;
char *smk_known;
u32 smk_secid;
struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */
@@ -167,9 +168,13 @@ struct smk_port_label {
#define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */
#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
#define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */
-#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
-#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
+/*
+ * CIPSO 2.2 standard is 239, but Smack wants to use the
+ * categories in a structured way that limits the value to
+ * the bits in 23 bytes, hence the unusual number.
+ */
+#define SMACK_CIPSO_MAXCATNUM 184 /* 23 * 8 */
/*
* Flag for transmute access
@@ -222,6 +227,7 @@ char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
char *smk_import(const char *, int);
struct smack_known *smk_import_entry(const char *, int);
+void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
u32 smack_to_secid(const char *);
@@ -247,6 +253,9 @@ extern struct list_head smk_netlbladdr_list;
extern struct security_operations smack_ops;
+#define SMACK_HASH_SLOTS 16
+extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
+
/*
* Is the directory transmuting?
*/
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 6a0377f38620..b3b59b1e93d6 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -325,6 +325,25 @@ void smack_log(char *subject_label, char *object_label, int request,
DEFINE_MUTEX(smack_known_lock);
+struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
+
+/**
+ * smk_insert_entry - insert a smack label into a hash map,
+ *
+ * this function must be called under smack_known_lock
+ */
+void smk_insert_entry(struct smack_known *skp)
+{
+ unsigned int hash;
+ struct hlist_head *head;
+
+ hash = full_name_hash(skp->smk_known, strlen(skp->smk_known));
+ head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
+
+ hlist_add_head_rcu(&skp->smk_hashed, head);
+ list_add_rcu(&skp->list, &smack_known_list);
+}
+
/**
* smk_find_entry - find a label on the list, return the list entry
* @string: a text string that might be a Smack label
@@ -334,12 +353,16 @@ DEFINE_MUTEX(smack_known_lock);
*/
struct smack_known *smk_find_entry(const char *string)
{
+ unsigned int hash;
+ struct hlist_head *head;
struct smack_known *skp;
- list_for_each_entry_rcu(skp, &smack_known_list, list) {
+ hash = full_name_hash(string, strlen(string));
+ head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
+
+ hlist_for_each_entry_rcu(skp, head, smk_hashed)
if (strcmp(skp->smk_known, string) == 0)
return skp;
- }
return NULL;
}
@@ -475,7 +498,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.
*/
- list_add_rcu(&skp->list, &smack_known_list);
+ smk_insert_entry(skp);
goto unlockout;
}
/*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index eefbd10e408f..8825375cc031 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode)
* Returns 0 if it all works out, -ENOMEM if there's no memory
*/
static int smack_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, char **name,
+ const struct qstr *qstr, const char **name,
void **value, size_t *len)
{
struct inode_smack *issp = inode->i_security;
@@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
char *dsp = smk_of_inode(dir);
int may;
- if (name) {
- *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS);
- if (*name == NULL)
- return -ENOMEM;
- }
+ if (name)
+ *name = XATTR_SMACK_SUFFIX;
if (value) {
rcu_read_lock();
@@ -3065,6 +3062,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
{
struct smack_known *skp;
int found = 0;
+ int acat;
+ int kcat;
if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
/*
@@ -3081,12 +3080,28 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
list_for_each_entry(skp, &smack_known_list, list) {
if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl)
continue;
- if (memcmp(sap->attr.mls.cat,
- skp->smk_netlabel.attr.mls.cat,
- SMK_CIPSOLEN) != 0)
- continue;
- found = 1;
- break;
+ /*
+ * Compare the catsets. Use the netlbl APIs.
+ */
+ if ((sap->flags & NETLBL_SECATTR_MLS_CAT) == 0) {
+ if ((skp->smk_netlabel.flags &
+ NETLBL_SECATTR_MLS_CAT) == 0)
+ found = 1;
+ break;
+ }
+ for (acat = -1, kcat = -1; acat == kcat; ) {
+ acat = netlbl_secattr_catmap_walk(
+ sap->attr.mls.cat, acat + 1);
+ kcat = netlbl_secattr_catmap_walk(
+ skp->smk_netlabel.attr.mls.cat,
+ kcat + 1);
+ if (acat < 0 || kcat < 0)
+ break;
+ }
+ if (acat == kcat) {
+ found = 1;
+ break;
+ }
}
rcu_read_unlock();
@@ -3877,12 +3892,12 @@ static __init void init_smack_known_list(void)
/*
* Create the known labels list
*/
- 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);
+ smk_insert_entry(&smack_known_huh);
+ smk_insert_entry(&smack_known_hat);
+ smk_insert_entry(&smack_known_star);
+ smk_insert_entry(&smack_known_floor);
+ smk_insert_entry(&smack_known_invalid);
+ smk_insert_entry(&smack_known_web);
}
/**
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index ab167037b2dd..80f4b4a45725 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
* @data: string to be parsed, null terminated
* @rule: Will be filled with Smack parsed rule
* @import: if non-zero, import labels
- * @change: if non-zero, data is from /smack/change-rule
+ * @tokens: numer of substrings expected in data
*
- * Returns 0 on success, -1 on failure
+ * Returns number of processed bytes on success, -1 on failure.
*/
-static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule,
- int import, int change)
+static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
+ int import, int tokens)
{
- char *subject;
- char *object;
- char *access1;
- char *access2;
- int datalen;
- int rc = -1;
+ ssize_t cnt = 0;
+ char *tok[4];
+ int i;
- /* This is inefficient */
- datalen = strlen(data);
+ /*
+ * Parsing the rule in-place, filling all white-spaces with '\0'
+ */
+ for (i = 0; i < tokens; ++i) {
+ while (isspace(data[cnt]))
+ data[cnt++] = '\0';
- /* Our first element can be 64 + \0 with no spaces */
- subject = kzalloc(datalen + 1, GFP_KERNEL);
- if (subject == NULL)
- return -1;
- object = kzalloc(datalen, GFP_KERNEL);
- if (object == NULL)
- goto free_out_s;
- access1 = kzalloc(datalen, GFP_KERNEL);
- if (access1 == NULL)
- goto free_out_o;
- access2 = kzalloc(datalen, GFP_KERNEL);
- if (access2 == NULL)
- goto free_out_a;
-
- if (change) {
- if (sscanf(data, "%s %s %s %s",
- subject, object, access1, access2) == 4)
- rc = smk_fill_rule(subject, object, access1, access2,
- rule, import, 0);
- } else {
- if (sscanf(data, "%s %s %s", subject, object, access1) == 3)
- rc = smk_fill_rule(subject, object, access1, NULL,
- rule, import, 0);
+ if (data[cnt] == '\0')
+ /* Unexpected end of data */
+ return -1;
+
+ tok[i] = data + cnt;
+
+ while (data[cnt] && !isspace(data[cnt]))
+ ++cnt;
}
+ while (isspace(data[cnt]))
+ data[cnt++] = '\0';
- kfree(access2);
-free_out_a:
- kfree(access1);
-free_out_o:
- kfree(object);
-free_out_s:
- kfree(subject);
- return rc;
+ while (i < 4)
+ tok[i++] = NULL;
+
+ if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0))
+ return -1;
+
+ return cnt;
}
#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */
@@ -447,11 +434,12 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
struct list_head *rule_list,
struct mutex *rule_lock, int format)
{
- struct smack_parsed_rule *rule;
+ struct smack_parsed_rule rule;
char *data;
- int datalen;
- int rc = -EINVAL;
- int load = 0;
+ int rc;
+ int trunc = 0;
+ int tokens;
+ ssize_t cnt = 0;
/*
* No partial writes.
@@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
*/
if (count != SMK_OLOADLEN && count != SMK_LOADLEN)
return -EINVAL;
- datalen = SMK_LOADLEN;
- } else
- datalen = count + 1;
+ } else {
+ if (count >= PAGE_SIZE) {
+ count = PAGE_SIZE - 1;
+ trunc = 1;
+ }
+ }
- data = kzalloc(datalen, GFP_KERNEL);
+ data = kmalloc(count + 1, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -479,47 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
goto out;
}
- rule = kzalloc(sizeof(*rule), GFP_KERNEL);
- if (rule == NULL) {
- rc = -ENOMEM;
- goto out;
+ /*
+ * In case of parsing only part of user buf,
+ * avoid having partial rule at the data buffer
+ */
+ if (trunc) {
+ while (count > 0 && (data[count - 1] != '\n'))
+ --count;
+ if (count == 0) {
+ rc = -EINVAL;
+ goto out;
+ }
}
- if (format == SMK_LONG_FMT) {
- /*
- * Be sure the data string is terminated.
- */
- data[count] = '\0';
- if (smk_parse_long_rule(data, rule, 1, 0))
- goto out_free_rule;
- } else if (format == SMK_CHANGE_FMT) {
- data[count] = '\0';
- if (smk_parse_long_rule(data, rule, 1, 1))
- goto out_free_rule;
- } else {
- /*
- * More on the minor hack for backward compatibility
- */
- if (count == (SMK_OLOADLEN))
- data[SMK_OLOADLEN] = '-';
- if (smk_parse_rule(data, rule, 1))
- goto out_free_rule;
- }
+ data[count] = '\0';
+ tokens = (format == SMK_CHANGE_FMT ? 4 : 3);
+ while (cnt < count) {
+ if (format == SMK_FIXED24_FMT) {
+ rc = smk_parse_rule(data, &rule, 1);
+ if (rc != 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+ cnt = count;
+ } else {
+ rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
+ if (rc <= 0) {
+ rc = -EINVAL;
+ goto out;
+ }
+ cnt += rc;
+ }
- if (rule_list == NULL) {
- load = 1;
- rule_list = &rule->smk_subject->smk_rules;
- rule_lock = &rule->smk_subject->smk_rules_lock;
- }
+ if (rule_list == NULL)
+ rc = smk_set_access(&rule, &rule.smk_subject->smk_rules,
+ &rule.smk_subject->smk_rules_lock, 1);
+ else
+ rc = smk_set_access(&rule, rule_list, rule_lock, 0);
- rc = smk_set_access(rule, rule_list, rule_lock, load);
- if (rc == 0) {
- rc = count;
- goto out;
+ if (rc)
+ goto out;
}
-out_free_rule:
- kfree(rule);
+ rc = cnt;
out:
kfree(data);
return rc;
@@ -901,7 +894,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
for (i = 0; i < catlen; i++) {
rule += SMK_DIGITLEN;
ret = sscanf(rule, "%u", &cat);
- if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL)
+ if (ret != 1 || cat > SMACK_CIPSO_MAXCATNUM)
goto out;
smack_catset_bit(cat, mapcatset);
@@ -1840,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
{
struct smack_parsed_rule rule;
char *data;
- char *cod;
int res;
data = simple_transaction_get(file, buf, count);
@@ -1853,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
res = smk_parse_rule(data, &rule, 0);
} else {
/*
- * Copy the data to make sure the string is terminated.
+ * simple_transaction_get() returns null-terminated data
*/
- cod = kzalloc(count + 1, GFP_KERNEL);
- if (cod == NULL)
- return -ENOMEM;
- memcpy(cod, data, count);
- cod[count] = '\0';
- res = smk_parse_long_rule(cod, &rule, 0, 0);
- kfree(cod);
+ res = smk_parse_long_rule(data, &rule, 0, 3);
}
- if (res)
+ if (res < 0)
return -EINVAL;
res = smk_access(rule.smk_subject, rule.smk_object,