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:
parent
545a726034
commit
828716c28f
|
@ -44,6 +44,7 @@ enum smk_inos {
|
||||||
SMK_ONLYCAP = 9, /* the only "capable" label */
|
SMK_ONLYCAP = 9, /* the only "capable" label */
|
||||||
SMK_LOGGING = 10, /* logging */
|
SMK_LOGGING = 10, /* logging */
|
||||||
SMK_LOAD_SELF = 11, /* task specific rules */
|
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;
|
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
|
* smk_write_load_list - write() for any /smack/load
|
||||||
* @file: file pointer, not actually used
|
* @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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rule->smk_subject = smk_import(data, 0);
|
if (smk_parse_rule(data, rule))
|
||||||
if (rule->smk_subject == NULL)
|
|
||||||
goto out_free_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;
|
rc = count;
|
||||||
/*
|
/*
|
||||||
* smk_set_access returns true if there was already a rule
|
* 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,
|
.write = smk_write_load_self,
|
||||||
.release = seq_release,
|
.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
|
* smk_fill_super - fill the /smackfs superblock
|
||||||
* @sb: the empty 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},
|
"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
|
||||||
[SMK_LOAD_SELF] = {
|
[SMK_LOAD_SELF] = {
|
||||||
"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
|
"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
|
||||||
|
[SMK_ACCESSES] = {
|
||||||
|
"access", &smk_access_ops, S_IRUGO|S_IWUSR},
|
||||||
/* last one */
|
/* last one */
|
||||||
{""}
|
{""}
|
||||||
};
|
};
|
||||||
|
|
Reference in New Issue