diff options
Diffstat (limited to 'security/smack/smack_access.c')
-rw-r--r-- | security/smack/smack_access.c | 346 |
1 files changed, 170 insertions, 176 deletions
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 425a6a2..9637e10 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -19,31 +19,37 @@ struct smack_known smack_known_huh = { .smk_known = "?", .smk_secid = 2, + .smk_cipso = NULL, }; struct smack_known smack_known_hat = { .smk_known = "^", .smk_secid = 3, + .smk_cipso = NULL, }; struct smack_known smack_known_star = { .smk_known = "*", .smk_secid = 4, + .smk_cipso = NULL, }; struct smack_known smack_known_floor = { .smk_known = "_", .smk_secid = 5, + .smk_cipso = NULL, }; struct smack_known smack_known_invalid = { .smk_known = "", .smk_secid = 6, + .smk_cipso = NULL, }; struct smack_known smack_known_web = { .smk_known = "@", .smk_secid = 7, + .smk_cipso = NULL, }; LIST_HEAD(smack_known_list); @@ -71,19 +77,14 @@ int log_policy = SMACK_AUDIT_DENIED; * entry is found returns -ENOENT. * * NOTE: + * Even though Smack labels are usually shared on smack_list + * labels that come in off the network can't be imported + * and added to the list for locking reasons. * - * Earlier versions of this function allowed for labels that - * were not on the label list. This was done to allow for - * labels to come over the network that had never been seen - * before on this host. Unless the receiving socket has the - * star label this will always result in a failure check. The - * star labeled socket case is now handled in the networking - * hooks so there is no case where the label is not on the - * label list. Checking to see if the address of two labels - * is the same is now a reliable test. - * - * Do the object check first because that is more - * likely to differ. + * Therefore, it is necessary to check the contents of the labels, + * not just the pointer values. Of course, in most cases the labels + * will be on the list, so checking the pointers may be a worthwhile + * optimization. */ int smk_access_entry(char *subject_label, char *object_label, struct list_head *rule_list) @@ -92,10 +93,13 @@ int smk_access_entry(char *subject_label, char *object_label, struct smack_rule *srp; list_for_each_entry_rcu(srp, rule_list, list) { - if (srp->smk_object == object_label && - srp->smk_subject == subject_label) { - may = srp->smk_access; - break; + if (srp->smk_subject == subject_label || + strcmp(srp->smk_subject, subject_label) == 0) { + if (srp->smk_object == object_label || + strcmp(srp->smk_object, object_label) == 0) { + may = srp->smk_access; + break; + } } } @@ -113,12 +117,18 @@ int smk_access_entry(char *subject_label, char *object_label, * access rule list and returns 0 if the access is permitted, * non zero otherwise. * - * Smack labels are shared on smack_list + * Even though Smack labels are usually shared on smack_list + * labels that come in off the network can't be imported + * and added to the list for locking reasons. + * + * Therefore, it is necessary to check the contents of the labels, + * not just the pointer values. Of course, in most cases the labels + * will be on the list, so checking the pointers may be a worthwhile + * optimization. */ int smk_access(char *subject_label, char *object_label, int request, struct smk_audit_info *a) { - struct smack_known *skp; int may = MAY_NOT; int rc = 0; @@ -127,7 +137,8 @@ int smk_access(char *subject_label, char *object_label, int request, * * A star subject can't access any object. */ - if (subject_label == smack_known_star.smk_known) { + if (subject_label == smack_known_star.smk_known || + strcmp(subject_label, smack_known_star.smk_known) == 0) { rc = -EACCES; goto out_audit; } @@ -137,27 +148,33 @@ int smk_access(char *subject_label, char *object_label, int request, * An internet subject can access any object. */ if (object_label == smack_known_web.smk_known || - subject_label == smack_known_web.smk_known) + subject_label == smack_known_web.smk_known || + strcmp(object_label, smack_known_web.smk_known) == 0 || + strcmp(subject_label, smack_known_web.smk_known) == 0) goto out_audit; /* * A star object can be accessed by any subject. */ - if (object_label == smack_known_star.smk_known) + if (object_label == smack_known_star.smk_known || + strcmp(object_label, smack_known_star.smk_known) == 0) goto out_audit; /* * An object can be accessed in any way by a subject * with the same label. */ - if (subject_label == object_label) + if (subject_label == object_label || + strcmp(subject_label, object_label) == 0) goto out_audit; /* * A hat subject can read any object. * A floor object can be read by any subject. */ if ((request & MAY_ANYREAD) == request) { - if (object_label == smack_known_floor.smk_known) + if (object_label == smack_known_floor.smk_known || + strcmp(object_label, smack_known_floor.smk_known) == 0) goto out_audit; - if (subject_label == smack_known_hat.smk_known) + if (subject_label == smack_known_hat.smk_known || + strcmp(subject_label, smack_known_hat.smk_known) == 0) goto out_audit; } /* @@ -167,9 +184,8 @@ int smk_access(char *subject_label, char *object_label, int request, * good. A negative response from smk_access_entry() * indicates there is no entry for this pair. */ - skp = smk_find_entry(subject_label); rcu_read_lock(); - may = smk_access_entry(subject_label, object_label, &skp->smk_rules); + may = smk_access_entry(subject_label, object_label, &smack_rule_list); rcu_read_unlock(); if (may > 0 && (request & may) == request) @@ -220,9 +236,14 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) } /* - * Allow for priviliged to override policy. + * Return if a specific label has been designated as the + * only one that gets privilege and current does not + * have that label. */ - if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE)) + if (smack_onlycap != NULL && smack_onlycap != sp) + goto out_audit; + + if (capable(CAP_MAC_OVERRIDE)) rc = 0; out_audit: @@ -252,8 +273,6 @@ static inline void smack_str_from_perm(char *string, int access) string[i++] = 'x'; if (access & MAY_APPEND) string[i++] = 'a'; - if (access & MAY_TRANSMUTE) - string[i++] = 't'; string[i] = '\0'; } /** @@ -322,102 +341,7 @@ void smack_log(char *subject_label, char *object_label, int request, } #endif -DEFINE_MUTEX(smack_known_lock); - -/** - * smk_find_entry - find a label on the list, return the list entry - * @string: a text string that might be a Smack label - * - * Returns a pointer to the entry in the label list that - * matches the passed string. - */ -struct smack_known *smk_find_entry(const char *string) -{ - struct smack_known *skp; - - list_for_each_entry_rcu(skp, &smack_known_list, list) { - if (strcmp(skp->smk_known, string) == 0) - return skp; - } - - return NULL; -} - -/** - * smk_parse_smack - parse smack label from a text string - * @string: a text string that might contain a Smack label - * @len: the maximum size, or zero if it is NULL terminated. - * - * Returns a pointer to the clean label, or NULL - */ -char *smk_parse_smack(const char *string, int len) -{ - char *smack; - int i; - - if (len <= 0) - len = strlen(string) + 1; - - /* - * Reserve a leading '-' as an indicator that - * this isn't a label, but an option to interfaces - * including /smack/cipso and /smack/cipso2 - */ - if (string[0] == '-') - return NULL; - - for (i = 0; i < len; i++) - if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' || - string[i] == '"' || string[i] == '\\' || string[i] == '\'') - break; - - if (i == 0 || i >= SMK_LONGLABEL) - return NULL; - - smack = kzalloc(i + 1, GFP_KERNEL); - if (smack != NULL) { - strncpy(smack, string, i + 1); - smack[i] = '\0'; - } - return smack; -} - -/** - * smk_netlbl_mls - convert a catset to netlabel mls categories - * @catset: the Smack categories - * @sap: where to put the netlabel categories - * - * Allocates and fills attr.mls - * Returns 0 on success, error code on failure. - */ -int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, - int len) -{ - unsigned char *cp; - unsigned char m; - int cat; - int rc; - int byte; - - sap->flags |= NETLBL_SECATTR_MLS_CAT; - sap->attr.mls.lvl = level; - sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - sap->attr.mls.cat->startbit = 0; - - for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) - for (m = 0x80; m != 0; m >>= 1, cat++) { - if ((m & *cp) == 0) - continue; - rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat, - cat, GFP_ATOMIC); - if (rc < 0) { - netlbl_secattr_catmap_free(sap->attr.mls.cat); - return rc; - } - } - - return 0; -} +static DEFINE_MUTEX(smack_known_lock); /** * smk_import_entry - import a label, return the list entry @@ -430,59 +354,53 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, struct smack_known *smk_import_entry(const char *string, int len) { struct smack_known *skp; - char *smack; - int slen; - int rc; + char smack[SMK_LABELLEN]; + int found; + int i; - smack = smk_parse_smack(string, len); - if (smack == NULL) + if (len <= 0 || len > SMK_MAXLEN) + len = SMK_MAXLEN; + + for (i = 0, found = 0; i < SMK_LABELLEN; i++) { + if (found) + smack[i] = '\0'; + else if (i >= len || string[i] > '~' || string[i] <= ' ' || + string[i] == '/' || string[i] == '"' || + string[i] == '\\' || string[i] == '\'') { + smack[i] = '\0'; + found = 1; + } else + smack[i] = string[i]; + } + + if (smack[0] == '\0') return NULL; mutex_lock(&smack_known_lock); - skp = smk_find_entry(smack); - if (skp != NULL) - goto freeout; - - skp = kzalloc(sizeof(*skp), GFP_KERNEL); - if (skp == NULL) - goto freeout; + found = 0; + list_for_each_entry_rcu(skp, &smack_known_list, list) { + if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { + found = 1; + break; + } + } - skp->smk_known = smack; - skp->smk_secid = smack_next_secid++; - skp->smk_netlabel.domain = skp->smk_known; - skp->smk_netlabel.flags = - NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; - /* - * If direct labeling works use it. - * Otherwise use mapped labeling. - */ - slen = strlen(smack); - if (slen < SMK_CIPSOLEN) - rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known, - &skp->smk_netlabel, slen); - else - rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid, - &skp->smk_netlabel, sizeof(skp->smk_secid)); - - if (rc >= 0) { - INIT_LIST_HEAD(&skp->smk_rules); - mutex_init(&skp->smk_rules_lock); - /* - * Make sure that the entry is actually - * filled before putting it on the list. - */ - list_add_rcu(&skp->list, &smack_known_list); - goto unlockout; + if (found == 0) { + skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); + if (skp != NULL) { + strncpy(skp->smk_known, smack, SMK_MAXLEN); + skp->smk_secid = smack_next_secid++; + skp->smk_cipso = NULL; + spin_lock_init(&skp->smk_cipsolock); + /* + * Make sure that the entry is actually + * filled before putting it on the list. + */ + list_add_rcu(&skp->list, &smack_known_list); + } } - /* - * smk_netlbl_mls failed. - */ - kfree(skp); - skp = NULL; -freeout: - kfree(smack); -unlockout: + mutex_unlock(&smack_known_lock); return skp; @@ -545,9 +463,85 @@ char *smack_from_secid(const u32 secid) */ u32 smack_to_secid(const char *smack) { - struct smack_known *skp = smk_find_entry(smack); + struct smack_known *skp; - if (skp == NULL) - return 0; - return skp->smk_secid; + 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; +} + +/** + * smack_from_cipso - find the Smack label associated with a CIPSO option + * @level: Bell & LaPadula level from the network + * @cp: Bell & LaPadula categories from the network + * @result: where to put the Smack value + * + * This is a simple lookup in the label table. + * + * This is an odd duck as far as smack handling goes in that + * it sends back a copy of the smack label rather than a pointer + * to the master list. This is done because it is possible for + * a foreign host to send a smack label that is new to this + * machine and hence not on the list. That would not be an + * issue except that adding an entry to the master list can't + * be done at that point. + */ +void smack_from_cipso(u32 level, char *cp, char *result) +{ + struct smack_known *kp; + char *final = NULL; + + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { + if (kp->smk_cipso == NULL) + continue; + + spin_lock_bh(&kp->smk_cipsolock); + + if (kp->smk_cipso->smk_level == level && + memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0) + final = kp->smk_known; + + spin_unlock_bh(&kp->smk_cipsolock); + } + rcu_read_unlock(); + if (final == NULL) + final = smack_known_huh.smk_known; + strncpy(result, final, SMK_MAXLEN); + return; +} + +/** + * smack_to_cipso - find the CIPSO option to go with a Smack label + * @smack: a pointer to the smack label in question + * @cp: where to put the result + * + * Returns zero if a value is available, non-zero otherwise. + */ +int smack_to_cipso(const char *smack, struct smack_cipso *cp) +{ + struct smack_known *kp; + int found = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(kp, &smack_known_list, list) { + if (kp->smk_known == smack || + strcmp(kp->smk_known, smack) == 0) { + found = 1; + break; + } + } + rcu_read_unlock(); + + if (found == 0 || kp->smk_cipso == NULL) + return -ENOENT; + + memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso)); + return 0; } |