Smack: check permissions from user space (v2)
Adds a new file into SmackFS called 'access'. Wanted Smack permission is written into /smack/access. After that result can be read from the opened file. If access applies result contains 1 and otherwise 0. File access is protected from race conditions by using simple_transaction_get()/set() API. Fixes from the previous version: - Removed smack.h changes, refactoring left-over from previous version. - Removed #include <linux/smack.h>, refactoring left-over from previous version. Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com> Signed-off-by: Casey Schaufler <cschaufler@cschaufler-intel.(none)>
This commit is contained in:
		
				
					committed by
					
						 Casey Schaufler
						Casey Schaufler
					
				
			
			
				
	
			
			
			
						parent
						
							545a726034
						
					
				
				
					commit
					828716c28f
				
			| @@ -44,6 +44,7 @@ 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 */ | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -175,6 +176,81 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, | ||||
| 	return found; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * smk_parse_rule - parse subject, object and access type | ||||
|  * @data: string to be parsed whose size is SMK_LOADLEN | ||||
|  * @rule: parsed entities are stored in here | ||||
|  */ | ||||
| static int smk_parse_rule(const char *data, struct smack_rule *rule) | ||||
| { | ||||
| 	rule->smk_subject = smk_import(data, 0); | ||||
| 	if (rule->smk_subject == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	rule->smk_object = smk_import(data + SMK_LABELLEN, 0); | ||||
| 	if (rule->smk_object == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	rule->smk_access = 0; | ||||
|  | ||||
| 	switch (data[SMK_LABELLEN + SMK_LABELLEN]) { | ||||
| 	case '-': | ||||
| 		break; | ||||
| 	case 'r': | ||||
| 	case 'R': | ||||
| 		rule->smk_access |= MAY_READ; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { | ||||
| 	case '-': | ||||
| 		break; | ||||
| 	case 'w': | ||||
| 	case 'W': | ||||
| 		rule->smk_access |= MAY_WRITE; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { | ||||
| 	case '-': | ||||
| 		break; | ||||
| 	case 'x': | ||||
| 	case 'X': | ||||
| 		rule->smk_access |= MAY_EXEC; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { | ||||
| 	case '-': | ||||
| 		break; | ||||
| 	case 'a': | ||||
| 	case 'A': | ||||
| 		rule->smk_access |= MAY_APPEND; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { | ||||
| 	case '-': | ||||
| 		break; | ||||
| 	case 't': | ||||
| 	case 'T': | ||||
| 		rule->smk_access |= MAY_TRANSMUTE; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * smk_write_load_list - write() for any /smack/load | ||||
|  * @file: file pointer, not actually used | ||||
| @@ -234,71 +310,9 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	rule->smk_subject = smk_import(data, 0); | ||||
| 	if (rule->smk_subject == NULL) | ||||
| 	if (smk_parse_rule(data, rule)) | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	rc = count; | ||||
| 	/* | ||||
| 	 * smk_set_access returns true if there was already a rule | ||||
| @@ -1425,6 +1439,44 @@ static const struct file_operations smk_load_self_ops = { | ||||
| 	.write		= smk_write_load_self, | ||||
| 	.release        = seq_release, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * 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) | ||||
| { | ||||
| 	struct smack_rule rule; | ||||
| 	char *data; | ||||
|  | ||||
| 	if (!capable(CAP_MAC_ADMIN)) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	data = simple_transaction_get(file, buf, count); | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
|  | ||||
| 	if (count < SMK_LOADLEN || smk_parse_rule(data, &rule)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	data[0] = smk_access(rule.smk_subject, rule.smk_object, | ||||
| 			     rule.smk_access, NULL) == 0; | ||||
|  | ||||
| 	simple_transaction_set(file, 1); | ||||
| 	return SMK_LOADLEN; | ||||
| } | ||||
|  | ||||
| static const struct file_operations smk_access_ops = { | ||||
| 	.write		= smk_write_access, | ||||
| 	.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 +1511,8 @@ 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_IWUSR}, | ||||
| 		/* last one */ | ||||
| 			{""} | ||||
| 	}; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user