diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/apparmor/.gitignore | 6 | ||||
-rw-r--r-- | security/commoncap.c | 11 | ||||
-rw-r--r-- | security/security.c | 1 | ||||
-rw-r--r-- | security/selinux/.gitignore | 2 | ||||
-rw-r--r-- | security/smack/smack.h | 84 | ||||
-rw-r--r-- | security/smack/smack_access.c | 346 | ||||
-rw-r--r-- | security/smack/smack_lsm.c | 480 | ||||
-rw-r--r-- | security/smack/smackfs.c | 1337 |
8 files changed, 1540 insertions, 727 deletions
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore deleted file mode 100644 index 4d995ae..0000000 --- a/security/apparmor/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# -# Generated include files -# -af_names.h -capability_names.h -rlim_names.h diff --git a/security/commoncap.c b/security/commoncap.c index 44f0969..8bfbd13 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -30,6 +30,10 @@ #include <linux/user_namespace.h> #include <linux/personality.h> +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include <linux/android_aid.h> +#endif + /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. @@ -84,6 +88,13 @@ EXPORT_SYMBOL(cap_netlink_recv); int cap_capable(struct task_struct *tsk, const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 0; + if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) + return 0; +#endif + for (;;) { /* The creator of the user namespace has all caps. */ if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) diff --git a/security/security.c b/security/security.c index 4ba6d4c..f19f945 100644 --- a/security/security.c +++ b/security/security.c @@ -626,6 +626,7 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } +EXPORT_SYMBOL_GPL(security_file_permission); int security_file_alloc(struct file *file) { diff --git a/security/selinux/.gitignore b/security/selinux/.gitignore deleted file mode 100644 index 2e5040a..0000000 --- a/security/selinux/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -av_permissions.h -flask.h diff --git a/security/smack/smack.h b/security/smack/smack.h index 2b6c6a5..618de1f 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -23,13 +23,19 @@ #include <linux/lsm_audit.h> /* + * Smack labels were limited to 23 characters for a long time. + */ +#define SMK_LABELLEN 24 +#define SMK_LONGLABEL 256 + +/* + * Maximum number of bytes for the levels in a CIPSO IP option. * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is * bigger than can be used, and 24 is the next lower multiple * of 8, and there are too many issues if there isn't space set * aside for the terminating null byte. */ -#define SMK_MAXLEN 23 -#define SMK_LABELLEN (SMK_MAXLEN+1) +#define SMK_CIPSOLEN 24 struct superblock_smack { char *smk_root; @@ -41,9 +47,9 @@ struct superblock_smack { }; struct socket_smack { - char *smk_out; /* outbound label */ - char *smk_in; /* inbound label */ - char smk_packet[SMK_LABELLEN]; /* TCP peer label */ + char *smk_out; /* outbound label */ + char *smk_in; /* inbound label */ + char *smk_packet; /* TCP peer label */ }; /* @@ -66,6 +72,7 @@ struct task_smack { #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ #define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ +#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */ /* * A label access rule. @@ -78,15 +85,6 @@ struct smack_rule { }; /* - * An entry in the table mapping smack values to - * CIPSO level/category-set values. - */ -struct smack_cipso { - int smk_level; - char smk_catset[SMK_LABELLEN]; -}; - -/* * An entry in the table identifying hosts. */ struct smk_netlbladdr { @@ -113,16 +111,19 @@ struct smk_netlbladdr { * interfaces don't. The secid should go away when all of * these components have been repaired. * - * If there is a cipso value associated with the label it - * gets stored here, too. This will most likely be rare as - * the cipso direct mapping in used internally. + * The cipso value associated with the label gets stored here, too. + * + * Keep the access rules for this subject label here so that + * the entire set of rules does not need to be examined every + * time. */ struct smack_known { - struct list_head list; - char smk_known[SMK_LABELLEN]; - u32 smk_secid; - struct smack_cipso *smk_cipso; - spinlock_t smk_cipsolock; /* for changing cipso map */ + struct list_head list; + char *smk_known; + u32 smk_secid; + struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ + struct list_head smk_rules; /* access rules */ + struct mutex smk_rules_lock; /* lock for rules */ }; /* @@ -150,7 +151,6 @@ struct smack_known { /* * smackfs magic number - * smackfs macic number */ #define SMACK_MAGIC 0x43415d53 /* "SMAC" */ @@ -160,6 +160,7 @@ struct smack_known { #define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ #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 */ @@ -176,9 +177,9 @@ struct smack_known { #define MAY_NOT 0 /* - * Number of access types used by Smack (rwxa) + * Number of access types used by Smack (rwxat) */ -#define SMK_NUM_ACCESS_TYPE 4 +#define SMK_NUM_ACCESS_TYPE 5 /* * Smack audit data; is empty if CONFIG_AUDIT not set @@ -200,17 +201,19 @@ struct inode_smack *new_inode_smack(char *); int smk_access_entry(char *, char *, struct list_head *); int smk_access(char *, char *, int, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); -int smack_to_cipso(const char *, struct smack_cipso *); -void smack_from_cipso(u32, char *, char *); char *smack_from_secid(const u32); +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); +struct smack_known *smk_find_entry(const char *); u32 smack_to_secid(const char *); /* * Shared data. */ extern int smack_cipso_direct; +extern int smack_cipso_mapped; extern char *smack_net_ambient; extern char *smack_onlycap; extern const char *smack_cipso_option; @@ -222,25 +225,13 @@ extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; +extern struct mutex smack_known_lock; extern struct list_head smack_known_list; -extern struct list_head smack_rule_list; extern struct list_head smk_netlbladdr_list; extern struct security_operations smack_ops; /* - * Stricly for CIPSO level manipulation. - * Set the category bit number in a smack label sized buffer. - */ -static inline void smack_catset_bit(int cat, char *catsetp) -{ - if (cat > SMK_LABELLEN * 8) - return; - - catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); -} - -/* * Is the directory transmuting? */ static inline int smk_inode_transmutable(const struct inode *isp) @@ -283,6 +274,19 @@ static inline char *smk_of_current(void) } /* + * Is the task privileged and allowed to be privileged + * by the onlycap rule. + */ +static inline int smack_privileged(int cap) +{ + if (!capable(cap)) + return 0; + if (smack_onlycap == NULL || smack_onlycap == smk_of_current()) + return 1; + return 0; +} + +/* * logging functions */ #define SMACK_AUDIT_DENIED 0x1 diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9637e10..425a6a2 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -19,37 +19,31 @@ 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); @@ -77,14 +71,19 @@ 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. * - * 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. + * 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. */ int smk_access_entry(char *subject_label, char *object_label, struct list_head *rule_list) @@ -93,13 +92,10 @@ 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_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; - } + if (srp->smk_object == object_label && + srp->smk_subject == subject_label) { + may = srp->smk_access; + break; } } @@ -117,18 +113,12 @@ int smk_access_entry(char *subject_label, char *object_label, * access rule list and returns 0 if the access is permitted, * non zero otherwise. * - * 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. + * Smack labels are shared on smack_list */ 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; @@ -137,8 +127,7 @@ 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 || - strcmp(subject_label, smack_known_star.smk_known) == 0) { + if (subject_label == smack_known_star.smk_known) { rc = -EACCES; goto out_audit; } @@ -148,33 +137,27 @@ 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 || - strcmp(object_label, smack_known_web.smk_known) == 0 || - strcmp(subject_label, smack_known_web.smk_known) == 0) + subject_label == smack_known_web.smk_known) goto out_audit; /* * A star object can be accessed by any subject. */ - if (object_label == smack_known_star.smk_known || - strcmp(object_label, smack_known_star.smk_known) == 0) + if (object_label == smack_known_star.smk_known) goto out_audit; /* * An object can be accessed in any way by a subject * with the same label. */ - if (subject_label == object_label || - strcmp(subject_label, object_label) == 0) + if (subject_label == object_label) 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 || - strcmp(object_label, smack_known_floor.smk_known) == 0) + if (object_label == smack_known_floor.smk_known) goto out_audit; - if (subject_label == smack_known_hat.smk_known || - strcmp(subject_label, smack_known_hat.smk_known) == 0) + if (subject_label == smack_known_hat.smk_known) goto out_audit; } /* @@ -184,8 +167,9 @@ 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, &smack_rule_list); + may = smk_access_entry(subject_label, object_label, &skp->smk_rules); rcu_read_unlock(); if (may > 0 && (request & may) == request) @@ -236,14 +220,9 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) } /* - * Return if a specific label has been designated as the - * only one that gets privilege and current does not - * have that label. + * Allow for priviliged to override policy. */ - if (smack_onlycap != NULL && smack_onlycap != sp) - goto out_audit; - - if (capable(CAP_MAC_OVERRIDE)) + if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE)) rc = 0; out_audit: @@ -273,6 +252,8 @@ 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'; } /** @@ -341,7 +322,102 @@ void smack_log(char *subject_label, char *object_label, int request, } #endif -static DEFINE_MUTEX(smack_known_lock); +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; +} /** * smk_import_entry - import a label, return the list entry @@ -354,53 +430,59 @@ static DEFINE_MUTEX(smack_known_lock); struct smack_known *smk_import_entry(const char *string, int len) { struct smack_known *skp; - char smack[SMK_LABELLEN]; - int found; - int i; - - 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]; - } + char *smack; + int slen; + int rc; - if (smack[0] == '\0') + smack = smk_parse_smack(string, len); + if (smack == NULL) return NULL; mutex_lock(&smack_known_lock); - 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_find_entry(smack); + if (skp != NULL) + goto freeout; - 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); - } - } + skp = kzalloc(sizeof(*skp), GFP_KERNEL); + if (skp == NULL) + goto freeout; + 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; + } + /* + * smk_netlbl_mls failed. + */ + kfree(skp); + skp = NULL; +freeout: + kfree(smack); +unlockout: mutex_unlock(&smack_known_lock); return skp; @@ -463,85 +545,9 @@ char *smack_from_secid(const u32 secid) */ u32 smack_to_secid(const char *smack) { - struct smack_known *skp; + struct smack_known *skp = smk_find_entry(smack); - 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; + if (skp == NULL) + return 0; + return skp->smk_secid; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 9831a39..c43f73b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -5,12 +5,13 @@ * * Authors: * Casey Schaufler <casey@schaufler-ca.com> - * Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com> + * Jarkko Sakkinen <jarkko.sakkinen@intel.com> * * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com> * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. - * Paul Moore <paul.moore@hp.com> + * Paul Moore <paul@paul-moore.com> * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2011 Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -29,11 +30,11 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/pipe_fs_i.h> -#include <net/netlabel.h> #include <net/cipso_ipv4.h> #include <linux/audit.h> #include <linux/magic.h> #include <linux/dcache.h> +#include <linux/personality.h> #include "smack.h" #define task_security(task) (task_cred_xxx((task), security)) @@ -52,16 +53,23 @@ static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) { int rc; - char in[SMK_LABELLEN]; + char *buffer; + char *result = NULL; if (ip->i_op->getxattr == NULL) return NULL; - rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN); - if (rc < 0) + buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL); + if (buffer == NULL) return NULL; - return smk_import(in, rc); + rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL); + if (rc > 0) + result = smk_import(buffer, rc); + + kfree(buffer); + + return result; } /** @@ -206,7 +214,7 @@ static int smack_syslog(int typefrom_file) int rc = 0; char *sp = smk_of_current(); - if (capable(CAP_MAC_OVERRIDE)) + if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; if (sp != smack_known_floor.smk_known) @@ -441,11 +449,17 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags) * BPRM hooks */ +/** + * smack_bprm_set_creds - set creds for exec + * @bprm: the exec information + * + * Returns 0 if it gets a blob, -ENOMEM otherwise + */ static int smack_bprm_set_creds(struct linux_binprm *bprm) { - struct task_smack *tsp = bprm->cred->security; + struct inode *inode = bprm->file->f_path.dentry->d_inode; + struct task_smack *bsp = bprm->cred->security; struct inode_smack *isp; - struct dentry *dp; int rc; rc = cap_bprm_set_creds(bprm); @@ -455,20 +469,48 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - if (bprm->file == NULL || bprm->file->f_dentry == NULL) + isp = inode->i_security; + if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; - dp = bprm->file->f_dentry; + if (bprm->unsafe) + return -EPERM; - if (dp->d_inode == NULL) - return 0; + bsp->smk_task = isp->smk_task; + bprm->per_clear |= PER_CLEAR_ON_SETID; - isp = dp->d_inode->i_security; + return 0; +} - if (isp->smk_task != NULL) - tsp->smk_task = isp->smk_task; +/** + * smack_bprm_committing_creds - Prepare to install the new credentials + * from bprm. + * + * @bprm: binprm for exec + */ +static void smack_bprm_committing_creds(struct linux_binprm *bprm) +{ + struct task_smack *bsp = bprm->cred->security; - return 0; + if (bsp->smk_task != bsp->smk_forked) + current->pdeath_signal = 0; +} + +/** + * smack_bprm_secureexec - Return the decision to use secureexec. + * @bprm: binprm for exec + * + * Returns 0 on success. + */ +static int smack_bprm_secureexec(struct linux_binprm *bprm) +{ + struct task_smack *tsp = current_security(); + int ret = cap_bprm_secureexec(bprm); + + if (!ret && (tsp->smk_task != tsp->smk_forked)) + ret = 1; + + return ret; } /* @@ -516,6 +558,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, char **name, void **value, size_t *len) { + struct smack_known *skp; + struct inode_smack *issp = inode->i_security; + char *csp = smk_of_current(); char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); int may; @@ -527,18 +572,22 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (value) { + skp = smk_find_entry(csp); rcu_read_lock(); - may = smk_access_entry(smk_of_current(), dsp, &smack_rule_list); + may = smk_access_entry(csp, dsp, &skp->smk_rules); rcu_read_unlock(); /* * If the access rule allows transmutation and * the directory requests transmutation then * by all means transmute. + * Mark the inode as changed. */ if (may > 0 && ((may & MAY_TRANSMUTE) != 0) && - smk_inode_transmutable(dir)) + smk_inode_transmutable(dir)) { isp = dsp; + issp->smk_flags |= SMK_INODE_CHANGED; + } *value = kstrdup(isp, GFP_KERNEL); if (*value == NULL) @@ -684,6 +733,7 @@ static int smack_inode_rename(struct inode *old_inode, * smack_inode_permission - Smack version of permission() * @inode: the inode in question * @mask: the access requested + * @flags: special case * * This is the important Smack hook. * @@ -703,6 +753,7 @@ static int smack_inode_permission(struct inode *inode, int mask, unsigned flags) /* May be droppable after audit */ if (flags & IPERM_FLAG_RCU) return -ECHILD; + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, inode); return smk_curacc(smk_of_inode(inode), mask, &ad); @@ -772,17 +823,17 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) rc = -EPERM; /* * check label validity here so import wont fail on * post_setxattr */ - if (size == 0 || size >= SMK_LABELLEN || + if (size == 0 || size >= SMK_LONGLABEL || smk_import(value, size) == NULL) rc = -EINVAL; } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) rc = -EPERM; if (size != TRANS_TRUE_SIZE || strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0) @@ -840,7 +891,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, return; } -/* +/** * smack_inode_getxattr - Smack check on getxattr * @dentry: the object * @name: unused @@ -857,7 +908,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); } -/* +/** * smack_inode_removexattr - Smack check on removexattr * @dentry: the object * @name: name of the attribute @@ -878,7 +929,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || strcmp(name, XATTR_NAME_SMACKMMAP)) { - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) rc = -EPERM; } else rc = cap_inode_removexattr(dentry, name); @@ -1087,36 +1138,31 @@ static int smack_file_lock(struct file *file, unsigned int cmd) * @cmd: what action to check * @arg: unused * + * Generally these operations are harmless. + * File locking operations present an obvious mechanism + * for passing information, so they require write access. + * * Returns 0 if current has access, error code otherwise */ static int smack_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { struct smk_audit_info ad; - int rc; + int rc = 0; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); - smk_ad_setfield_u_fs_path(&ad, file->f_path); switch (cmd) { - case F_DUPFD: - case F_GETFD: - case F_GETFL: case F_GETLK: - case F_GETOWN: - case F_GETSIG: - rc = smk_curacc(file->f_security, MAY_READ, &ad); - break; - case F_SETFD: - case F_SETFL: case F_SETLK: case F_SETLKW: case F_SETOWN: case F_SETSIG: + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); + smk_ad_setfield_u_fs_path(&ad, file->f_path); rc = smk_curacc(file->f_security, MAY_WRITE, &ad); break; default: - rc = smk_curacc(file->f_security, MAY_READWRITE, &ad); + break; } return rc; @@ -1137,6 +1183,7 @@ static int smack_file_mmap(struct file *file, unsigned long flags, unsigned long addr, unsigned long addr_only) { + struct smack_known *skp; struct smack_rule *srp; struct task_smack *tsp; char *sp; @@ -1169,6 +1216,7 @@ static int smack_file_mmap(struct file *file, tsp = current_security(); sp = smk_of_current(); + skp = smk_find_entry(sp); rc = 0; rcu_read_lock(); @@ -1176,15 +1224,8 @@ static int smack_file_mmap(struct file *file, * For each Smack rule associated with the subject * label verify that the SMACK64MMAP also has access * to that rule's object label. - * - * Because neither of the labels comes - * from the networking code it is sufficient - * to compare pointers. */ - list_for_each_entry_rcu(srp, &smack_rule_list, list) { - if (srp->smk_subject != sp) - continue; - + list_for_each_entry_rcu(srp, &skp->smk_rules, list) { osmack = srp->smk_object; /* * Matching labels always allows access. @@ -1213,7 +1254,8 @@ static int smack_file_mmap(struct file *file, * If there isn't one a SMACK64MMAP subject * can't have as much access as current. */ - mmay = smk_access_entry(msmack, osmack, &smack_rule_list); + skp = smk_find_entry(msmack); + mmay = smk_access_entry(msmack, osmack, &skp->smk_rules); if (mmay == -ENOENT) { rc = -EACCES; break; @@ -1314,6 +1356,24 @@ static int smack_file_receive(struct file *file) return smk_curacc(file->f_security, may, &ad); } +/** + * smack_dentry_open - Smack dentry open processing + * @file: the object + * @cred: unused + * + * Set the security blob in the file structure. + * + * Returns 0 + */ +static int smack_dentry_open(struct file *file, const struct cred *cred) +{ + struct inode_smack *isp = file->f_path.dentry->d_inode->i_security; + + file->f_security = isp->smk_inode; + + return 0; +} + /* * Task hooks */ @@ -1454,15 +1514,17 @@ static int smack_kernel_create_files_as(struct cred *new, /** * smk_curacc_on_task - helper to log task related access * @p: the task object - * @access : the access requested + * @access: the access requested + * @caller: name of the calling function for audit * * Return 0 if access is permitted */ -static int smk_curacc_on_task(struct task_struct *p, int access) +static int smk_curacc_on_task(struct task_struct *p, int access, + const char *caller) { struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); + smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); return smk_curacc(smk_of_task(task_security(p)), access, &ad); } @@ -1476,7 +1538,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access) */ static int smack_task_setpgid(struct task_struct *p, pid_t pgid) { - return smk_curacc_on_task(p, MAY_WRITE); + return smk_curacc_on_task(p, MAY_WRITE, __func__); } /** @@ -1487,7 +1549,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid) */ static int smack_task_getpgid(struct task_struct *p) { - return smk_curacc_on_task(p, MAY_READ); + return smk_curacc_on_task(p, MAY_READ, __func__); } /** @@ -1498,7 +1560,7 @@ static int smack_task_getpgid(struct task_struct *p) */ static int smack_task_getsid(struct task_struct *p) { - return smk_curacc_on_task(p, MAY_READ); + return smk_curacc_on_task(p, MAY_READ, __func__); } /** @@ -1526,7 +1588,7 @@ static int smack_task_setnice(struct task_struct *p, int nice) rc = cap_task_setnice(p, nice); if (rc == 0) - rc = smk_curacc_on_task(p, MAY_WRITE); + rc = smk_curacc_on_task(p, MAY_WRITE, __func__); return rc; } @@ -1543,7 +1605,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio) rc = cap_task_setioprio(p, ioprio); if (rc == 0) - rc = smk_curacc_on_task(p, MAY_WRITE); + rc = smk_curacc_on_task(p, MAY_WRITE, __func__); return rc; } @@ -1555,7 +1617,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio) */ static int smack_task_getioprio(struct task_struct *p) { - return smk_curacc_on_task(p, MAY_READ); + return smk_curacc_on_task(p, MAY_READ, __func__); } /** @@ -1572,7 +1634,7 @@ static int smack_task_setscheduler(struct task_struct *p) rc = cap_task_setscheduler(p); if (rc == 0) - rc = smk_curacc_on_task(p, MAY_WRITE); + rc = smk_curacc_on_task(p, MAY_WRITE, __func__); return rc; } @@ -1584,7 +1646,7 @@ static int smack_task_setscheduler(struct task_struct *p) */ static int smack_task_getscheduler(struct task_struct *p) { - return smk_curacc_on_task(p, MAY_READ); + return smk_curacc_on_task(p, MAY_READ, __func__); } /** @@ -1595,7 +1657,7 @@ static int smack_task_getscheduler(struct task_struct *p) */ static int smack_task_movememory(struct task_struct *p) { - return smk_curacc_on_task(p, MAY_WRITE); + return smk_curacc_on_task(p, MAY_WRITE, __func__); } /** @@ -1662,7 +1724,8 @@ static int smack_task_wait(struct task_struct *p) * state into account in the decision as well as * the smack value. */ - if (capable(CAP_MAC_OVERRIDE) || has_capability(p, CAP_MAC_OVERRIDE)) + if (smack_privileged(CAP_MAC_OVERRIDE) || + has_capability(p, CAP_MAC_OVERRIDE)) rc = 0; /* we log only if we didn't get overriden */ out_log: @@ -1710,7 +1773,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ssp->smk_in = csp; ssp->smk_out = csp; - ssp->smk_packet[0] = '\0'; + ssp->smk_packet = NULL; sk->sk_security = ssp; @@ -1766,65 +1829,6 @@ static char *smack_host_label(struct sockaddr_in *sip) } /** - * smack_set_catset - convert a capset to netlabel mls categories - * @catset: the Smack categories - * @sap: where to put the netlabel categories - * - * Allocates and fills attr.mls.cat - */ -static void smack_set_catset(char *catset, struct netlbl_lsm_secattr *sap) -{ - unsigned char *cp; - unsigned char m; - int cat; - int rc; - int byte; - - if (!catset) - return; - - sap->flags |= NETLBL_SECATTR_MLS_CAT; - sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - sap->attr.mls.cat->startbit = 0; - - for (cat = 1, cp = catset, byte = 0; byte < SMK_LABELLEN; 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); - } -} - -/** - * smack_to_secattr - fill a secattr from a smack value - * @smack: the smack value - * @nlsp: where the result goes - * - * Casey says that CIPSO is good enough for now. - * It can be used to effect. - * It can also be abused to effect when necessary. - * Apologies to the TSIG group in general and GW in particular. - */ -static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) -{ - struct smack_cipso cipso; - int rc; - - nlsp->domain = smack; - nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; - - rc = smack_to_cipso(smack, &cipso); - if (rc == 0) { - nlsp->attr.mls.lvl = cipso.smk_level; - smack_set_catset(cipso.smk_catset, nlsp); - } else { - nlsp->attr.mls.lvl = smack_cipso_direct; - smack_set_catset(smack, nlsp); - } -} - -/** * smack_netlabel - Set the secattr on a socket * @sk: the socket * @labeled: socket label scheme @@ -1836,8 +1840,8 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) */ static int smack_netlabel(struct sock *sk, int labeled) { + struct smack_known *skp; struct socket_smack *ssp = sk->sk_security; - struct netlbl_lsm_secattr secattr; int rc = 0; /* @@ -1855,10 +1859,8 @@ static int smack_netlabel(struct sock *sk, int labeled) labeled == SMACK_UNLABELED_SOCKET) netlbl_sock_delattr(sk); else { - netlbl_secattr_init(&secattr); - smack_to_secattr(ssp->smk_out, &secattr); - rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); - netlbl_secattr_destroy(&secattr); + skp = smk_find_entry(ssp->smk_out); + rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel); } bh_unlock_sock(sk); @@ -1929,7 +1931,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct socket *sock; int rc = 0; - if (value == NULL || size > SMK_LABELLEN || size == 0) + if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EACCES; sp = smk_import(value, size); @@ -2496,6 +2498,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) char *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; + int rc; struct dentry *dp; if (inode == NULL) @@ -2614,17 +2617,38 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ dp = dget(opt_dentry); fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp); - if (fetched != NULL) { + if (fetched != NULL) final = fetched; - if (S_ISDIR(inode->i_mode)) { - trattr[0] = '\0'; - inode->i_op->getxattr(dp, + + /* + * Transmuting directory + */ + if (S_ISDIR(inode->i_mode)) { + /* + * If this is a new directory and the label was + * transmuted when the inode was initialized + * set the transmute attribute on the directory + * and mark the inode. + * + * If there is a transmute attribute on the + * directory mark the inode. + */ + if (isp->smk_flags & SMK_INODE_CHANGED) { + isp->smk_flags &= ~SMK_INODE_CHANGED; + rc = inode->i_op->setxattr(dp, XATTR_NAME_SMACKTRANSMUTE, - trattr, TRANS_TRUE_SIZE); - if (strncmp(trattr, TRANS_TRUE, - TRANS_TRUE_SIZE) == 0) - transflag = SMK_INODE_TRANSMUTE; + TRANS_TRUE, TRANS_TRUE_SIZE, + 0); + } else { + rc = inode->i_op->getxattr(dp, + XATTR_NAME_SMACKTRANSMUTE, trattr, + TRANS_TRUE_SIZE); + if (rc >= 0 && strncmp(trattr, TRANS_TRUE, + TRANS_TRUE_SIZE) != 0) + rc = -EINVAL; } + if (rc >= 0) + transflag = SMK_INODE_TRANSMUTE; } isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); @@ -2700,10 +2724,10 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (p != current) return -EPERM; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (value == NULL || size == 0 || size >= SMK_LABELLEN) + if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; if (strcmp(name, "current") != 0) @@ -2752,15 +2776,24 @@ static int smack_unix_stream_connect(struct sock *sock, { struct socket_smack *ssp = sock->sk_security; struct socket_smack *osp = other->sk_security; + struct socket_smack *nsp = newsk->sk_security; struct smk_audit_info ad; int rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); smk_ad_setfield_u_net_sk(&ad, other); - if (!capable(CAP_MAC_OVERRIDE)) + if (!smack_privileged(CAP_MAC_OVERRIDE)) rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + /* + * Cross reference the peer labels for SO_PEERSEC. + */ + if (rc == 0) { + nsp->smk_packet = ssp->smk_out; + ssp->smk_packet = osp->smk_out; + } + return rc; } @@ -2782,7 +2815,7 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); smk_ad_setfield_u_net_sk(&ad, other->sk); - if (!capable(CAP_MAC_OVERRIDE)) + if (!smack_privileged(CAP_MAC_OVERRIDE)) rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); return rc; @@ -2812,19 +2845,19 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, return smack_netlabel_send(sock->sk, sip); } - /** * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat pair to smack * @sap: netlabel secattr - * @sip: where to put the result + * @ssp: socket security information * - * Copies a smack label into sip + * Returns a pointer to a Smack label found on the label list. */ -static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) +static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, + struct socket_smack *ssp) { - char smack[SMK_LABELLEN]; + struct smack_known *kp; char *sp; - int pcat; + int found = 0; if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { /* @@ -2832,34 +2865,30 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) * If there are flags but no level netlabel isn't * behaving the way we expect it to. * - * Get the categories, if any + * Look it up in the label table * Without guidance regarding the smack value * for the packet fall back on the network * ambient value. */ - memset(smack, '\0', SMK_LABELLEN); - if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) - for (pcat = -1;;) { - pcat = netlbl_secattr_catmap_walk( - sap->attr.mls.cat, pcat + 1); - if (pcat < 0) - break; - smack_catset_bit(pcat, smack); - } - /* - * If it is CIPSO using smack direct mapping - * we are already done. WeeHee. - */ - if (sap->attr.mls.lvl == smack_cipso_direct) { - memcpy(sip, smack, SMK_MAXLEN); - return; + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { + if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl) + continue; + if (memcmp(sap->attr.mls.cat, + kp->smk_netlabel.attr.mls.cat, + SMK_CIPSOLEN) != 0) + continue; + found = 1; + break; } - /* - * Look it up in the supplied table if it is not - * a direct mapping. - */ - smack_from_cipso(sap->attr.mls.lvl, smack, sip); - return; + rcu_read_unlock(); + + if (found) + return kp->smk_known; + + if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) + return smack_known_web.smk_known; + return smack_known_star.smk_known; } if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { /* @@ -2874,16 +2903,14 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) * secid is from a fallback. */ BUG_ON(sp == NULL); - strncpy(sip, sp, SMK_MAXLEN); - return; + return sp; } /* * Without guidance regarding the smack value * for the packet fall back on the network * ambient value. */ - strncpy(sip, smack_net_ambient, SMK_MAXLEN); - return; + return smack_net_ambient; } /** @@ -2897,7 +2924,6 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = sk->sk_security; - char smack[SMK_LABELLEN]; char *csp; int rc; struct smk_audit_info ad; @@ -2910,10 +2936,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); - if (rc == 0) { - smack_from_secattr(&secattr, smack); - csp = smack; - } else + if (rc == 0) + csp = smack_from_secattr(&secattr, ssp); + else csp = smack_net_ambient; netlbl_secattr_destroy(&secattr); @@ -2950,15 +2975,19 @@ static int smack_socket_getpeersec_stream(struct socket *sock, int __user *optlen, unsigned len) { struct socket_smack *ssp; - int slen; + char *rcp = ""; + int slen = 1; int rc = 0; ssp = sock->sk->sk_security; - slen = strlen(ssp->smk_packet) + 1; + if (ssp->smk_packet != NULL) { + rcp = ssp->smk_packet; + slen = strlen(rcp) + 1; + } if (slen > len) rc = -ERANGE; - else if (copy_to_user(optval, ssp->smk_packet, slen) != 0) + else if (copy_to_user(optval, rcp, slen) != 0) rc = -EFAULT; if (put_user(slen, optlen) != 0) @@ -2981,8 +3010,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, { struct netlbl_lsm_secattr secattr; - struct socket_smack *sp; - char smack[SMK_LABELLEN]; + struct socket_smack *ssp = NULL; + char *sp; int family = PF_UNSPEC; u32 s = 0; /* 0 is the invalid secid */ int rc; @@ -2997,17 +3026,19 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, family = sock->sk->sk_family; if (family == PF_UNIX) { - sp = sock->sk->sk_security; - s = smack_to_secid(sp->smk_out); + ssp = sock->sk->sk_security; + s = smack_to_secid(ssp->smk_out); } else if (family == PF_INET || family == PF_INET6) { /* * Translate what netlabel gave us. */ + if (sock != NULL && sock->sk != NULL) + ssp = sock->sk->sk_security; netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) { - smack_from_secattr(&secattr, smack); - s = smack_to_secid(smack); + sp = smack_from_secattr(&secattr, ssp); + s = smack_to_secid(sp); } netlbl_secattr_destroy(&secattr); } @@ -3051,11 +3082,13 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { u16 family = sk->sk_family; + struct smack_known *skp; struct socket_smack *ssp = sk->sk_security; struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; - char smack[SMK_LABELLEN]; + char *sp; + char *hsp; int rc; struct smk_audit_info ad; @@ -3066,9 +3099,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) - smack_from_secattr(&secattr, smack); + sp = smack_from_secattr(&secattr, ssp); else - strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN); + sp = smack_known_huh.smk_known; netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT @@ -3081,7 +3114,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad); if (rc != 0) return rc; @@ -3089,7 +3122,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Save the peer's label in the request_sock so we can later setup * smk_packet in the child socket so that SO_PEERCRED can report it. */ - req->peer_secid = smack_to_secid(smack); + req->peer_secid = smack_to_secid(sp); /* * We need to decide if we want to label the incoming connection here @@ -3099,16 +3132,14 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hdr = ip_hdr(skb); addr.sin_addr.s_addr = hdr->saddr; rcu_read_lock(); - if (smack_host_label(&addr) == NULL) { - rcu_read_unlock(); - netlbl_secattr_init(&secattr); - smack_to_secattr(smack, &secattr); - rc = netlbl_req_setattr(req, &secattr); - netlbl_secattr_destroy(&secattr); - } else { - rcu_read_unlock(); + hsp = smack_host_label(&addr); + rcu_read_unlock(); + + if (hsp == NULL) { + skp = smk_find_entry(sp); + rc = netlbl_req_setattr(req, &skp->smk_netlabel); + } else netlbl_req_delattr(req); - } return rc; } @@ -3124,13 +3155,11 @@ static void smack_inet_csk_clone(struct sock *sk, const struct request_sock *req) { struct socket_smack *ssp = sk->sk_security; - char *smack; - if (req->peer_secid != 0) { - smack = smack_from_secid(req->peer_secid); - strncpy(ssp->smk_packet, smack, SMK_MAXLEN); - } else - ssp->smk_packet[0] = '\0'; + if (req->peer_secid != 0) + ssp->smk_packet = smack_from_secid(req->peer_secid); + else + ssp->smk_packet = NULL; } /* @@ -3408,6 +3437,8 @@ struct security_operations smack_ops = { .sb_umount = smack_sb_umount, .bprm_set_creds = smack_bprm_set_creds, + .bprm_committing_creds = smack_bprm_committing_creds, + .bprm_secureexec = smack_bprm_secureexec, .inode_alloc_security = smack_inode_alloc_security, .inode_free_security = smack_inode_free_security, @@ -3439,6 +3470,8 @@ struct security_operations smack_ops = { .file_send_sigiotask = smack_file_send_sigiotask, .file_receive = smack_file_receive, + .dentry_open = smack_dentry_open, + .cred_alloc_blank = smack_cred_alloc_blank, .cred_free = smack_cred_free, .cred_prepare = smack_cred_prepare, @@ -3528,8 +3561,29 @@ struct security_operations smack_ops = { }; -static __init void init_smack_know_list(void) +static __init void init_smack_known_list(void) { + /* + * Initialize rule list locks + */ + mutex_init(&smack_known_huh.smk_rules_lock); + mutex_init(&smack_known_hat.smk_rules_lock); + mutex_init(&smack_known_floor.smk_rules_lock); + mutex_init(&smack_known_star.smk_rules_lock); + mutex_init(&smack_known_invalid.smk_rules_lock); + mutex_init(&smack_known_web.smk_rules_lock); + /* + * Initialize rule lists + */ + INIT_LIST_HEAD(&smack_known_huh.smk_rules); + INIT_LIST_HEAD(&smack_known_hat.smk_rules); + INIT_LIST_HEAD(&smack_known_star.smk_rules); + INIT_LIST_HEAD(&smack_known_floor.smk_rules); + INIT_LIST_HEAD(&smack_known_invalid.smk_rules); + INIT_LIST_HEAD(&smack_known_web.smk_rules); + /* + * 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); @@ -3564,16 +3618,8 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = tsp; - /* initialize the smack_know_list */ - init_smack_know_list(); - /* - * Initialize locks - */ - spin_lock_init(&smack_known_huh.smk_cipsolock); - spin_lock_init(&smack_known_hat.smk_cipsolock); - spin_lock_init(&smack_known_star.smk_cipsolock); - spin_lock_init(&smack_known_floor.smk_cipsolock); - spin_lock_init(&smack_known_invalid.smk_cipsolock); + /* initialize the smack_known_list */ + init_smack_known_list(); /* * Register with LSM diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index f934601..5a4ab14 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -22,7 +22,6 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <net/net_namespace.h> -#include <net/netlabel.h> #include <net/cipso_ipv4.h> #include <linux/seq_file.h> #include <linux/ctype.h> @@ -44,6 +43,14 @@ enum smk_inos { SMK_ONLYCAP = 9, /* the only "capable" label */ SMK_LOGGING = 10, /* logging */ SMK_LOAD_SELF = 11, /* task specific rules */ + SMK_ACCESSES = 12, /* access policy */ + SMK_MAPPED = 13, /* CIPSO level indicating mapped label */ + SMK_LOAD2 = 14, /* load policy with long labels */ + SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */ + SMK_ACCESS2 = 16, /* make an access check with long labels */ + SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ + SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ + SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ }; /* @@ -59,7 +66,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock); * If it isn't somehow marked, use this. * It can be reset via smackfs/ambient */ -char *smack_net_ambient = smack_known_floor.smk_known; +char *smack_net_ambient; /* * This is the level in a CIPSO header that indicates a @@ -69,6 +76,13 @@ char *smack_net_ambient = smack_known_floor.smk_known; int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; /* + * This is the level in a CIPSO header that indicates a + * secid is contained directly in the category set. + * It can be reset via smackfs/mapped + */ +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 @@ -85,15 +99,22 @@ char *smack_onlycap; */ LIST_HEAD(smk_netlbladdr_list); + +/* + * Rule lists are maintained for each label. + * This master list is just for reading /smack/load and /smack/load2. + */ +struct smack_master_list { + struct list_head list; + struct smack_rule *smk_rule; +}; + LIST_HEAD(smack_rule_list); static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; const char *smack_cipso_option = SMACK_CIPSO_OPTION; - -#define SEQ_READ_FINISHED 1 - /* * Values for parsing cipso rules * SMK_DIGITLEN: Length of a digit field in a rule. @@ -117,6 +138,18 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION; #define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN) #define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) +/* + * Stricly for CIPSO level manipulation. + * Set the category bit number in a smack label sized buffer. + */ +static inline void smack_catset_bit(unsigned int cat, char *catsetp) +{ + if (cat == 0 || cat > (SMK_CIPSOLEN * 8)) + return; + + catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); +} + /** * smk_netlabel_audit_set - fill a netlbl_audit struct * @nap: structure to fill @@ -129,12 +162,10 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) } /* - * Values for parsing single label host rules + * Value for parsing single label host rules * "1.2.3.4 X" - * "192.168.138.129/32 abcdefghijklmnopqrstuvw" */ #define SMK_NETLBLADDRMIN 9 -#define SMK_NETLBLADDRMAX 42 /** * smk_set_access - add a rule to the rule list @@ -159,9 +190,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, mutex_lock(rule_lock); + /* + * Because the object label is less likely to match + * than the subject label check it first + */ list_for_each_entry_rcu(sp, rule_list, list) { - if (sp->smk_subject == srp->smk_subject && - sp->smk_object == srp->smk_object) { + if (sp->smk_object == srp->smk_object && + sp->smk_subject == srp->smk_subject) { found = 1; sp->smk_access = srp->smk_access; break; @@ -176,30 +211,212 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, } /** - * smk_write_load_list - write() for any /smack/load + * smk_perm_from_str - parse smack accesses from a text string + * @string: a text string that contains a Smack accesses code + * + * Returns an integer with respective bits set for specified accesses. + */ +static int smk_perm_from_str(const char *string) +{ + int perm = 0; + const char *cp; + + for (cp = string; ; cp++) + switch (*cp) { + case '-': + break; + case 'r': + case 'R': + perm |= MAY_READ; + break; + case 'w': + case 'W': + perm |= MAY_WRITE; + break; + case 'x': + case 'X': + perm |= MAY_EXEC; + break; + case 'a': + case 'A': + perm |= MAY_APPEND; + break; + case 't': + case 'T': + perm |= MAY_TRANSMUTE; + break; + default: + return perm; + } +} + +/** + * smk_fill_rule - Fill Smack rule from strings + * @subject: subject label string + * @object: object label string + * @access: access string + * @access_remove: string with permissions to be removed + * @rule: Smack rule + * @import: if non-zero, import labels + * + * Returns 0 on success, -1 on failure + */ +static int smk_fill_rule(const char *subject, const char *object, + const char *access, const char *access_remove, + struct smack_rule *rule, int import) +{ + int rc = -1; + const char *cp; + struct smack_known *skp; + + if (import) { + rule->smk_subject = smk_import(subject, 0); + if (rule->smk_subject == NULL) + return -1; + + rule->smk_object = smk_import(object, 0); + if (rule->smk_object == NULL) + return -1; + } else { + cp = smk_parse_smack(subject, 0); + if (cp == NULL) + return -1; + skp = smk_find_entry(cp); + kfree(cp); + if (skp == NULL) + return -1; + rule->smk_subject = skp->smk_known; + + cp = smk_parse_smack(object, 0); + if (cp == NULL) + return -1; + skp = smk_find_entry(cp); + kfree(cp); + if (skp == NULL) + return -1; + rule->smk_object = skp->smk_known; + } + + if (access_remove) { + rule->smk_access |= smk_perm_from_str(access); + rule->smk_access &= ~smk_perm_from_str(access_remove); + } else + rule->smk_access = smk_perm_from_str(access); + + rc = 0; + + return rc; +} + +/** + * smk_parse_rule - parse Smack rule from load string + * @data: string to be parsed whose size is SMK_LOADLEN + * @rule: Smack rule + * @import: if non-zero, import labels + * + * Returns 0 on success, -1 on errors. + */ +static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +{ + int rc; + + rc = smk_fill_rule(data, data + SMK_LABELLEN, + data + SMK_LABELLEN + SMK_LABELLEN, NULL, rule, + import); + return rc; +} + +/** + * smk_parse_long_rule - parse Smack rule from rule string + * @data: string to be parsed, null terminated + * @rule: Smack rule + * @import: if non-zero, import labels + * @change: if non-zero, data is from /smack/change-rule + * + * Returns 0 on success, -1 on failure + */ +static int smk_parse_long_rule(const char *data, struct smack_rule *rule, + int import, int change) +{ + char *subject; + char *object; + char *access; + char *access_remove; + int datalen; + int rc = -1; + + /* + * This is probably inefficient, but safe. + */ + datalen = strlen(data); + subject = kzalloc(datalen, GFP_KERNEL); + if (subject == NULL) + return -1; + object = kzalloc(datalen, GFP_KERNEL); + if (object == NULL) + goto free_out_s; + access = kzalloc(datalen, GFP_KERNEL); + if (access == NULL) + goto free_out_o; + access_remove = kzalloc(datalen, GFP_KERNEL); + if (access_remove == NULL) + goto free_out_a; + + if (change) { + if (sscanf(data, "%s %s %s %s", + subject, object, access, access_remove) == 4) + rc = smk_fill_rule(subject, object, access, + access_remove, rule, import); + } else { + if (sscanf(data, "%s %s %s", subject, object, access) == 3) + rc = smk_fill_rule(subject, object, access, + NULL, rule, import); + } + + kfree(access_remove); +free_out_a: + kfree(access); +free_out_o: + kfree(object); +free_out_s: + kfree(subject); + return rc; +} + +#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ +#define SMK_LONG_FMT 1 /* Variable long label format */ +#define SMK_CHANGE_FMT 2 /* Rule modification format */ +/** + * smk_write_rules_list - write() for any /smack rule file * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start - must be 0 * @rule_list: the list of rules to write to * @rule_lock: lock for the rule list + * @format: /smack/load or /smack/load2 or /smack/change-rule format. * * Get one smack access rule from above. - * The format is exactly: - * char subject[SMK_LABELLEN] - * char object[SMK_LABELLEN] - * char access[SMK_ACCESSLEN] - * - * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes. + * The format for SMK_LONG_FMT is: + * "subject<whitespace>object<whitespace>access[<whitespace>...]" + * The format for SMK_FIXED24_FMT is exactly: + * "subject object rwxat" + * The format for SMK_CHANGE_FMT is: + * "subject<whitespace>object<whitespace> + * acc_enable<whitespace>acc_disable[<whitespace>...]" */ -static ssize_t smk_write_load_list(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, - struct list_head *rule_list, - struct mutex *rule_lock) +static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, + struct list_head *rule_list, + struct mutex *rule_lock, int format) { + struct smack_master_list *smlp; + struct smack_known *skp; struct smack_rule *rule; char *data; + int datalen; int rc = -EINVAL; + int load = 0; /* * No partial writes. @@ -207,13 +424,18 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, */ if (*ppos != 0) return -EINVAL; - /* - * Minor hack for backward compatibility - */ - if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN) - return -EINVAL; - data = kzalloc(SMK_LOADLEN, GFP_KERNEL); + if (format == SMK_FIXED24_FMT) { + /* + * Minor hack for backward compatibility + */ + if (count != SMK_OLOADLEN && count != SMK_LOADLEN) + return -EINVAL; + datalen = SMK_LOADLEN; + } else + datalen = count + 1; + + data = kzalloc(datalen, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -222,90 +444,59 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, goto out; } - /* - * More on the minor hack for backward compatibility - */ - if (count == (SMK_OLOADLEN)) - data[SMK_OLOADLEN] = '-'; - rule = kzalloc(sizeof(*rule), GFP_KERNEL); if (rule == NULL) { rc = -ENOMEM; goto out; } - rule->smk_subject = smk_import(data, 0); - if (rule->smk_subject == NULL) - goto out_free_rule; - - 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; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { - case '-': - break; - case 'w': - case 'W': - rule->smk_access |= MAY_WRITE; - break; - default: - goto out_free_rule; - } - - switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { - case '-': - break; - case 'x': - case 'X': - rule->smk_access |= MAY_EXEC; - break; - default: - goto out_free_rule; + 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; } - switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { - case '-': - break; - case 'a': - case 'A': - rule->smk_access |= MAY_APPEND; - break; - default: - goto out_free_rule; - } - switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { - case '-': - break; - case 't': - case 'T': - rule->smk_access |= MAY_TRANSMUTE; - break; - default: - goto out_free_rule; + if (rule_list == NULL) { + load = 1; + skp = smk_find_entry(rule->smk_subject); + rule_list = &skp->smk_rules; + rule_lock = &skp->smk_rules_lock; } rc = count; /* + * If this is a global as opposed to self and a new rule + * it needs to get added for reporting. * smk_set_access returns true if there was already a rule * for the subject/object pair, and false if it was new. */ - if (!smk_set_access(rule, rule_list, rule_lock)) + if (!smk_set_access(rule, rule_list, rule_lock)) { + if (load) { + smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); + if (smlp != NULL) { + smlp->smk_rule = rule; + list_add_rcu(&smlp->list, &smack_rule_list); + } else + rc = -ENOMEM; + } goto out; + } out_free_rule: kfree(rule); @@ -314,39 +505,66 @@ out: return rc; } - /* - * Seq_file read operations for /smack/load + * Core logic for smackfs seq list operations. */ -static void *load_seq_start(struct seq_file *s, loff_t *pos) +static void *smk_seq_start(struct seq_file *s, loff_t *pos, + struct list_head *head) { - if (*pos == SEQ_READ_FINISHED) + struct list_head *list; + + /* + * This is 0 the first time through. + */ + if (s->index == 0) + s->private = head; + + if (s->private == NULL) return NULL; - if (list_empty(&smack_rule_list)) + + list = s->private; + if (list_empty(list)) return NULL; - return smack_rule_list.next; + + if (s->index == 0) + return list->next; + return list; } -static void *load_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, + struct list_head *head) { struct list_head *list = v; - if (list_is_last(list, &smack_rule_list)) { - *pos = SEQ_READ_FINISHED; + if (list_is_last(list, head)) { + s->private = NULL; return NULL; } + s->private = list->next; return list->next; } -static int load_seq_show(struct seq_file *s, void *v) +static void smk_seq_stop(struct seq_file *s, void *v) { - struct list_head *list = v; - struct smack_rule *srp = - list_entry(list, struct smack_rule, list); + /* No-op */ +} - seq_printf(s, "%s %s", (char *)srp->smk_subject, - (char *)srp->smk_object); +static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) +{ + /* + * Don't show any rules with label names too long for + * interface file (/smack/load or /smack/load2) + * because you should expect to be able to write + * anything you read back. + */ + if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max) + return; + + if (srp->smk_access == 0) + return; + + seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object); seq_putc(s, ' '); @@ -360,24 +578,40 @@ static int load_seq_show(struct seq_file *s, void *v) seq_putc(s, 'a'); if (srp->smk_access & MAY_TRANSMUTE) seq_putc(s, 't'); - if (srp->smk_access == 0) - seq_putc(s, '-'); seq_putc(s, '\n'); +} - return 0; +/* + * Seq_file read operations for /smack/load + */ + +static void *load2_seq_start(struct seq_file *s, loff_t *pos) +{ + return smk_seq_start(s, pos, &smack_rule_list); } -static void load_seq_stop(struct seq_file *s, void *v) +static void *load2_seq_next(struct seq_file *s, void *v, loff_t *pos) { - /* No-op */ + return smk_seq_next(s, v, pos, &smack_rule_list); +} + +static int load_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_master_list *smlp = + list_entry(list, struct smack_master_list, list); + + smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN); + + return 0; } static const struct seq_operations load_seq_ops = { - .start = load_seq_start, - .next = load_seq_next, + .start = load2_seq_start, + .next = load2_seq_next, .show = load_seq_show, - .stop = load_seq_stop, + .stop = smk_seq_stop, }; /** @@ -403,17 +637,16 @@ static int smk_open_load(struct inode *inode, struct file *file) static ssize_t smk_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - /* * Must have privilege. * No partial writes. * Enough data must be present. */ - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - return smk_write_load_list(file, buf, count, ppos, &smack_rule_list, - &smack_list_lock); + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_FIXED24_FMT); } static const struct file_operations smk_load_ops = { @@ -483,6 +716,8 @@ static void smk_unlbl_ambient(char *oldambient) printk(KERN_WARNING "%s:%d remove rc = %d\n", __func__, __LINE__, rc); } + if (smack_net_ambient == NULL) + smack_net_ambient = smack_known_floor.smk_known; rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, NULL, NULL, &nai); @@ -497,28 +732,12 @@ static void smk_unlbl_ambient(char *oldambient) 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_list.next; + return smk_seq_start(s, pos, &smack_known_list); } static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct list_head *list = v; - - /* - * labels with no associated cipso value wont be printed - * in cipso_seq_show - */ - if (list_is_last(list, &smack_known_list)) { - *pos = SEQ_READ_FINISHED; - return NULL; - } - - return list->next; + return smk_seq_next(s, v, pos, &smack_known_list); } /* @@ -530,43 +749,39 @@ static int cipso_seq_show(struct seq_file *s, void *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; + struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; - int cat = 1; int i; - unsigned char m; - if (scp == NULL) + /* + * Don't show a label that could not have been set using + * /smack/cipso. This is in support of the notion that + * anything read from /smack/cipso ought to be writeable + * to /smack/cipso. + * + * /smack/cipso2 should be used instead. + */ + if (strlen(skp->smk_known) >= SMK_LABELLEN) return 0; - seq_printf(s, "%s %3d", (char *)&skp->smk_known, scp->smk_level); + seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - cbp = scp->smk_catset; - for (i = 0; i < SMK_LABELLEN; i++) - for (m = 0x80; m != 0; m >>= 1) { - if (m & cbp[i]) { - seq_printf(s, "%c%d", sep, cat); - sep = ','; - } - cat++; - } + for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; + i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + seq_printf(s, "%c%d", sep, i); + sep = ','; + } seq_putc(s, '\n'); return 0; } -static void cipso_seq_stop(struct seq_file *s, void *v) -{ - /* No-op */ -} - static const struct seq_operations cipso_seq_ops = { .start = cipso_seq_start, - .stop = cipso_seq_stop, .next = cipso_seq_next, .show = cipso_seq_show, + .stop = smk_seq_stop, }; /** @@ -583,23 +798,24 @@ static int smk_open_cipso(struct inode *inode, struct file *file) } /** - * smk_write_cipso - write() for /smack/cipso + * smk_set_cipso - do the work for write() for cipso and cipso2 * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start + * @format: /smack/cipso or /smack/cipso2 * * Accepts only one cipso rule per write call. * Returns number of bytes written or error code, as appropriate */ -static ssize_t smk_write_cipso(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t smk_set_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, int format) { struct smack_known *skp; - struct smack_cipso *scp = NULL; - char mapcatset[SMK_LABELLEN]; + struct netlbl_lsm_secattr ncats; + char mapcatset[SMK_CIPSOLEN]; int maplevel; - int cat; + unsigned int cat; int catlen; ssize_t rc = -EINVAL; char *data = NULL; @@ -612,11 +828,12 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, * No partial writes. * Enough data must be present. */ - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; if (*ppos != 0) return -EINVAL; - if (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX) + if (format == SMK_FIXED24_FMT && + (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX)) return -EINVAL; data = kzalloc(count + 1, GFP_KERNEL); @@ -628,11 +845,6 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, goto unlockedout; } - /* labels cannot begin with a '-' */ - if (data[0] == '-') { - rc = -EINVAL; - goto unlockedout; - } data[count] = '\0'; rule = data; /* @@ -645,7 +857,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, if (skp == NULL) goto out; - rule += SMK_LABELLEN; + if (format == SMK_FIXED24_FMT) + rule += SMK_LABELLEN; + else + rule += strlen(skp->smk_known); + ret = sscanf(rule, "%d", &maplevel); if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL) goto out; @@ -655,41 +871,29 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM) goto out; - if (count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN)) + if (format == SMK_FIXED24_FMT && + count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN)) goto out; memset(mapcatset, 0, sizeof(mapcatset)); for (i = 0; i < catlen; i++) { rule += SMK_DIGITLEN; - ret = sscanf(rule, "%d", &cat); + ret = sscanf(rule, "%u", &cat); if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL) goto out; smack_catset_bit(cat, mapcatset); } - if (skp->smk_cipso == NULL) { - scp = kzalloc(sizeof(struct smack_cipso), GFP_KERNEL); - if (scp == NULL) { - rc = -ENOMEM; - goto out; - } + rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN); + if (rc >= 0) { + netlbl_secattr_catmap_free(skp->smk_netlabel.attr.mls.cat); + skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat; + skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl; + rc = count; } - spin_lock_bh(&skp->smk_cipsolock); - - if (scp == NULL) - scp = skp->smk_cipso; - else - skp->smk_cipso = scp; - - scp->smk_level = maplevel; - memcpy(scp->smk_catset, mapcatset, sizeof(mapcatset)); - - spin_unlock_bh(&skp->smk_cipsolock); - - rc = count; out: mutex_unlock(&smack_cipso_lock); unlockedout: @@ -697,6 +901,22 @@ unlockedout: return rc; } +/** + * smk_write_cipso - write() for /smack/cipso + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Accepts only one cipso rule per write call. + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_set_cipso(file, buf, count, ppos, SMK_FIXED24_FMT); +} + static const struct file_operations smk_cipso_ops = { .open = smk_open_cipso, .read = seq_read, @@ -706,28 +926,91 @@ static const struct file_operations smk_cipso_ops = { }; /* + * Seq_file read operations for /smack/cipso2 + */ + +/* + * Print cipso labels in format: + * label level[/cat[,cat]] + */ +static int cipso2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_known *skp = + list_entry(list, struct smack_known, list); + struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + char sep = '/'; + int i; + + seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); + + for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; + i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + seq_printf(s, "%c%d", sep, i); + sep = ','; + } + + seq_putc(s, '\n'); + + return 0; +} + +static const struct seq_operations cipso2_seq_ops = { + .start = cipso_seq_start, + .next = cipso_seq_next, + .show = cipso2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_cipso2 - open() for /smack/cipso2 + * @inode: inode structure representing file + * @file: "cipso2" file pointer + * + * Connect our cipso_seq_* operations with /smack/cipso2 + * file_operations + */ +static int smk_open_cipso2(struct inode *inode, struct file *file) +{ + return seq_open(file, &cipso2_seq_ops); +} + +/** + * smk_write_cipso2 - write() for /smack/cipso2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Accepts only one cipso rule per write call. + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_cipso2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_set_cipso(file, buf, count, ppos, SMK_LONG_FMT); +} + +static const struct file_operations smk_cipso2_ops = { + .open = smk_open_cipso2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_cipso2, + .release = seq_release, +}; + +/* * Seq_file read operations for /smack/netlabel */ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) { - if (*pos == SEQ_READ_FINISHED) - return NULL; - if (list_empty(&smk_netlbladdr_list)) - return NULL; - return smk_netlbladdr_list.next; + return smk_seq_start(s, pos, &smk_netlbladdr_list); } static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct list_head *list = v; - - if (list_is_last(list, &smk_netlbladdr_list)) { - *pos = SEQ_READ_FINISHED; - return NULL; - } - - return list->next; + return smk_seq_next(s, v, pos, &smk_netlbladdr_list); } #define BEBITS (sizeof(__be32) * 8) @@ -751,16 +1034,11 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v) return 0; } -static void netlbladdr_seq_stop(struct seq_file *s, void *v) -{ - /* No-op */ -} - static const struct seq_operations netlbladdr_seq_ops = { .start = netlbladdr_seq_start, - .stop = netlbladdr_seq_stop, .next = netlbladdr_seq_next, .show = netlbladdr_seq_show, + .stop = smk_seq_stop, }; /** @@ -833,9 +1111,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, { struct smk_netlbladdr *skp; struct sockaddr_in newname; - char smack[SMK_LABELLEN]; + char *smack; char *sp; - char data[SMK_NETLBLADDRMAX + 1]; + char *data; char *host = (char *)&newname.sin_addr.s_addr; int rc; struct netlbl_audit audit_info; @@ -853,14 +1131,27 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, * "<addr/mask, as a.b.c.d/e><space><label>" * "<addr, as a.b.c.d><space><label>" */ - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; if (*ppos != 0) return -EINVAL; - if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX) + if (count < SMK_NETLBLADDRMIN) return -EINVAL; - if (copy_from_user(data, buf, count) != 0) - return -EFAULT; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto free_data_out; + } + + smack = kzalloc(count + 1, GFP_KERNEL); + if (smack == NULL) { + rc = -ENOMEM; + goto free_data_out; + } data[count] = '\0'; @@ -869,24 +1160,34 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, if (rc != 6) { rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", &host[0], &host[1], &host[2], &host[3], smack); - if (rc != 5) - return -EINVAL; + if (rc != 5) { + rc = -EINVAL; + goto free_out; + } m = BEBITS; } - if (m > BEBITS) - return -EINVAL; + if (m > BEBITS) { + rc = -EINVAL; + goto free_out; + } - /* if smack begins with '-', its an option, don't import it */ + /* + * If smack begins with '-', it is an option, don't import it + */ if (smack[0] != '-') { sp = smk_import(smack, 0); - if (sp == NULL) - return -EINVAL; + if (sp == NULL) { + rc = -EINVAL; + goto free_out; + } } else { /* check known options */ if (strcmp(smack, smack_cipso_option) == 0) sp = (char *)smack_cipso_option; - else - return -EINVAL; + else { + rc = -EINVAL; + goto free_out; + } } for (temp_mask = 0; m > 0; m--) { @@ -952,6 +1253,11 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, mutex_unlock(&smk_netlbladdr_lock); +free_out: + kfree(smack); +free_data_out: + kfree(data); + return rc; } @@ -1002,7 +1308,7 @@ static ssize_t smk_write_doi(struct file *file, const char __user *buf, char temp[80]; int i; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -1065,10 +1371,11 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf, static ssize_t smk_write_direct(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct smack_known *skp; char temp[80]; int i; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -1082,7 +1389,20 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf, if (sscanf(temp, "%d", &i) != 1) return -EINVAL; - smack_cipso_direct = i; + /* + * Don't do anything if the value hasn't actually changed. + * If it is changing reset the level on entries that were + * set up to be direct when they were created. + */ + if (smack_cipso_direct != i) { + mutex_lock(&smack_known_lock); + list_for_each_entry_rcu(skp, &smack_known_list, list) + if (skp->smk_netlabel.attr.mls.lvl == + smack_cipso_direct) + skp->smk_netlabel.attr.mls.lvl = i; + smack_cipso_direct = i; + mutex_unlock(&smack_known_lock); + } return count; } @@ -1094,6 +1414,84 @@ static const struct file_operations smk_direct_ops = { }; /** + * smk_read_mapped - read() for /smack/mapped + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_mapped(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + ssize_t rc; + + if (*ppos != 0) + return 0; + + sprintf(temp, "%d", smack_cipso_mapped); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + + return rc; +} + +/** + * smk_write_mapped - write() for /smack/mapped + * @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_mapped(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_known *skp; + char temp[80]; + int i; + + if (!smack_privileged(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; + + /* + * Don't do anything if the value hasn't actually changed. + * If it is changing reset the level on entries that were + * set up to be mapped when they were created. + */ + if (smack_cipso_mapped != i) { + mutex_lock(&smack_known_lock); + list_for_each_entry_rcu(skp, &smack_known_list, list) + if (skp->smk_netlabel.attr.mls.lvl == + smack_cipso_mapped) + skp->smk_netlabel.attr.mls.lvl = i; + smack_cipso_mapped = i; + mutex_unlock(&smack_known_lock); + } + + return count; +} + +static const struct file_operations smk_mapped_ops = { + .read = smk_read_mapped, + .write = smk_write_mapped, + .llseek = default_llseek, +}; + +/** * smk_read_ambient - read() for /smack/ambient * @filp: file pointer, not actually used * @buf: where to put the result @@ -1141,22 +1539,28 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, static ssize_t smk_write_ambient(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char in[SMK_LABELLEN]; char *oldambient; - char *smack; + char *smack = NULL; + char *data; + int rc = count; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (count >= SMK_LABELLEN) - return -EINVAL; + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; - if (copy_from_user(in, buf, count) != 0) - return -EFAULT; + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto out; + } - smack = smk_import(in, count); - if (smack == NULL) - return -EINVAL; + smack = smk_import(data, count); + if (smack == NULL) { + rc = -EINVAL; + goto out; + } mutex_lock(&smack_ambient_lock); @@ -1166,7 +1570,9 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, mutex_unlock(&smack_ambient_lock); - return count; +out: + kfree(data); + return rc; } static const struct file_operations smk_ambient_ops = { @@ -1217,10 +1623,11 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char in[SMK_LABELLEN]; + char *data; char *sp = smk_of_task(current->cred->security); + int rc = count; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; /* @@ -1231,11 +1638,9 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (smack_onlycap != NULL && smack_onlycap != sp) return -EPERM; - if (count >= SMK_LABELLEN) - return -EINVAL; - - if (copy_from_user(in, buf, count) != 0) - return -EFAULT; + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; /* * Should the null string be passed in unset the onlycap value. @@ -1243,10 +1648,17 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * 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. + * + * smk_import will also reject a label beginning with '-', + * so "-usecapabilities" will also work. */ - smack_onlycap = smk_import(in, count); + if (copy_from_user(data, buf, count) != 0) + rc = -EFAULT; + else + smack_onlycap = smk_import(data, count); - return count; + kfree(data); + return rc; } static const struct file_operations smk_onlycap_ops = { @@ -1293,7 +1705,7 @@ static ssize_t smk_write_logging(struct file *file, const char __user *buf, char temp[32]; int i; - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; if (count >= sizeof(temp) || count == 0) @@ -1328,23 +1740,14 @@ static void *load_self_seq_start(struct seq_file *s, loff_t *pos) { struct task_smack *tsp = current_security(); - if (*pos == SEQ_READ_FINISHED) - return NULL; - if (list_empty(&tsp->smk_rules)) - return NULL; - return tsp->smk_rules.next; + return smk_seq_start(s, pos, &tsp->smk_rules); } static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct task_smack *tsp = current_security(); - struct list_head *list = v; - if (list_is_last(list, &tsp->smk_rules)) { - *pos = SEQ_READ_FINISHED; - return NULL; - } - return list->next; + return smk_seq_next(s, v, pos, &tsp->smk_rules); } static int load_self_seq_show(struct seq_file *s, void *v) @@ -1353,44 +1756,21 @@ static int load_self_seq_show(struct seq_file *s, void *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); - - seq_putc(s, ' '); - - if (srp->smk_access & MAY_READ) - seq_putc(s, 'r'); - if (srp->smk_access & MAY_WRITE) - seq_putc(s, 'w'); - if (srp->smk_access & MAY_EXEC) - seq_putc(s, 'x'); - if (srp->smk_access & MAY_APPEND) - seq_putc(s, 'a'); - if (srp->smk_access & MAY_TRANSMUTE) - seq_putc(s, 't'); - if (srp->smk_access == 0) - seq_putc(s, '-'); - - seq_putc(s, '\n'); + smk_rule_show(s, srp, SMK_LABELLEN); return 0; } -static void load_self_seq_stop(struct seq_file *s, void *v) -{ - /* No-op */ -} - static const struct seq_operations load_self_seq_ops = { .start = load_self_seq_start, .next = load_self_seq_next, .show = load_self_seq_show, - .stop = load_self_seq_stop, + .stop = smk_seq_stop, }; /** - * smk_open_load_self - open() for /smack/load-self + * smk_open_load_self - open() for /smack/load-self2 * @inode: inode structure representing file * @file: "load" file pointer * @@ -1414,8 +1794,8 @@ static ssize_t smk_write_load_self(struct file *file, const char __user *buf, { struct task_smack *tsp = current_security(); - return smk_write_load_list(file, buf, count, ppos, &tsp->smk_rules, - &tsp->smk_rules_lock); + return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, + &tsp->smk_rules_lock, SMK_FIXED24_FMT); } static const struct file_operations smk_load_self_ops = { @@ -1425,6 +1805,333 @@ static const struct file_operations smk_load_self_ops = { .write = smk_write_load_self, .release = seq_release, }; + +/** + * smk_user_access - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_user_access(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, int format) +{ + struct smack_rule rule; + char *data; + char *cod; + int res; + + data = simple_transaction_get(file, buf, count); + if (IS_ERR(data)) + return PTR_ERR(data); + + if (format == SMK_FIXED24_FMT) { + if (count < SMK_LOADLEN) + return -EINVAL; + res = smk_parse_rule(data, &rule, 0); + } else { + /* + * Copy the data to make sure the string is terminated. + */ + 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); + } + + if (res) + return -EINVAL; + + res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, + NULL); + data[0] = res == 0 ? '1' : '0'; + data[1] = '\0'; + + simple_transaction_set(file, 2); + + if (format == SMK_FIXED24_FMT) + return SMK_LOADLEN; + return count; +} + +/** + * smk_write_access - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_access(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_user_access(file, buf, count, ppos, SMK_FIXED24_FMT); +} + +static const struct file_operations smk_access_ops = { + .write = smk_write_access, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + + +/* + * Seq_file read operations for /smack/load2 + */ + +static int load2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_master_list *smlp = + list_entry(list, struct smack_master_list, list); + + smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL); + + return 0; +} + +static const struct seq_operations load2_seq_ops = { + .start = load2_seq_start, + .next = load2_seq_next, + .show = load2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_load2 - open() for /smack/load2 + * @inode: inode structure representing file + * @file: "load2" file pointer + * + * For reading, use load2_seq_* seq_file reading operations. + */ +static int smk_open_load2(struct inode *inode, struct file *file) +{ + return seq_open(file, &load2_seq_ops); +} + +/** + * smk_write_load2 - write() for /smack/load2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* + * Must have privilege. + */ + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_LONG_FMT); +} + +static const struct file_operations smk_load2_ops = { + .open = smk_open_load2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_load2, + .release = seq_release, +}; + +/* + * Seq_file read operations for /smack/load-self2 + */ + +static void *load_self2_seq_start(struct seq_file *s, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_start(s, pos, &tsp->smk_rules); +} + +static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_next(s, v, pos, &tsp->smk_rules); +} + +static int load_self2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); + + smk_rule_show(s, srp, SMK_LONGLABEL); + + return 0; +} + +static const struct seq_operations load_self2_seq_ops = { + .start = load_self2_seq_start, + .next = load_self2_seq_next, + .show = load_self2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_load_self2 - open() for /smack/load-self2 + * @inode: inode structure representing file + * @file: "load" file pointer + * + * For reading, use load_seq_* seq_file reading operations. + */ +static int smk_open_load_self2(struct inode *inode, struct file *file) +{ + return seq_open(file, &load_self2_seq_ops); +} + +/** + * smk_write_load_self2 - write() for /smack/load-self2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load_self2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current_security(); + + return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, + &tsp->smk_rules_lock, SMK_LONG_FMT); +} + +static const struct file_operations smk_load_self2_ops = { + .open = smk_open_load_self2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_load_self2, + .release = seq_release, +}; + +/** + * smk_write_access2 - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_access2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_user_access(file, buf, count, ppos, SMK_LONG_FMT); +} + +static const struct file_operations smk_access2_ops = { + .write = smk_write_access2, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + +/** + * smk_write_revoke_subj - write() for /smack/revoke-subject + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data = NULL; + const char *cp = NULL; + struct smack_known *skp; + struct smack_rule *sp; + struct list_head *rule_list; + struct mutex *rule_lock; + int rc = count; + + if (*ppos != 0) + return -EINVAL; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + if (count == 0 || count > SMK_LONGLABEL) + return -EINVAL; + + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto free_out; + } + + cp = smk_parse_smack(data, count); + if (cp == NULL) { + rc = -EINVAL; + goto free_out; + } + + skp = smk_find_entry(cp); + if (skp == NULL) { + rc = -EINVAL; + goto free_out; + } + + rule_list = &skp->smk_rules; + rule_lock = &skp->smk_rules_lock; + + mutex_lock(rule_lock); + + list_for_each_entry_rcu(sp, rule_list, list) + sp->smk_access = 0; + + mutex_unlock(rule_lock); + +free_out: + kfree(data); + kfree(cp); + return rc; +} + +static const struct file_operations smk_revoke_subj_ops = { + .write = smk_write_revoke_subj, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + +/** + * smk_write_change_rule - write() for /smack/change-rule + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_change_rule(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* + * Must have privilege. + */ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_CHANGE_FMT); +} + +static const struct file_operations smk_change_rule_ops = { + .write = smk_write_change_rule, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -1459,6 +2166,23 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "logging", &smk_logging_ops, S_IRUGO|S_IWUSR}, [SMK_LOAD_SELF] = { "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, + [SMK_ACCESSES] = { + "access", &smk_access_ops, S_IRUGO|S_IWUGO}, + [SMK_MAPPED] = { + "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR}, + [SMK_LOAD2] = { + "load2", &smk_load2_ops, S_IRUGO|S_IWUSR}, + [SMK_LOAD_SELF2] = { + "load-self2", &smk_load_self2_ops, S_IRUGO|S_IWUGO}, + [SMK_ACCESS2] = { + "access2", &smk_access2_ops, S_IRUGO|S_IWUGO}, + [SMK_CIPSO2] = { + "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR}, + [SMK_REVOKE_SUBJ] = { + "revoke-subject", &smk_revoke_subj_ops, + S_IRUGO|S_IWUSR}, + [SMK_CHANGE_RULE] = { + "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; @@ -1501,6 +2225,15 @@ static struct file_system_type smk_fs_type = { static struct vfsmount *smackfs_mount; +static int __init smk_preset_netlabel(struct smack_known *skp) +{ + skp->smk_netlabel.domain = skp->smk_known; + skp->smk_netlabel.flags = + NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; + return smk_netlbl_mls(smack_cipso_direct, skp->smk_known, + &skp->smk_netlabel, strlen(skp->smk_known)); +} + /** * init_smk_fs - get the smackfs superblock * @@ -1517,6 +2250,7 @@ static struct vfsmount *smackfs_mount; static int __init init_smk_fs(void) { int err; + int rc; if (!security_module_enable(&smack_ops)) return 0; @@ -1534,6 +2268,25 @@ static int __init init_smk_fs(void) smk_cipso_doi(); smk_unlbl_ambient(NULL); + rc = smk_preset_netlabel(&smack_known_floor); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_hat); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_huh); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_invalid); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_star); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_web); + if (err == 0 && rc < 0) + err = rc; + return err; } |