forked from Minki/linux
Merge branch 'smack-for-4.4' of https://github.com/cschaufler/smack-next into next
This commit is contained in:
commit
09302fd19e
@ -255,6 +255,16 @@ unconfined
|
||||
the access permitted if it wouldn't be otherwise. Note that this
|
||||
is dangerous and can ruin the proper labeling of your system.
|
||||
It should never be used in production.
|
||||
relabel-self
|
||||
This interface contains a list of labels to which the process can
|
||||
transition to, by writing to /proc/self/attr/current.
|
||||
Normally a process can change its own label to any legal value, but only
|
||||
if it has CAP_MAC_ADMIN. This interface allows a process without
|
||||
CAP_MAC_ADMIN to relabel itself to one of labels from predefined list.
|
||||
A process without CAP_MAC_ADMIN can change its label only once. When it
|
||||
does, this list will be cleared.
|
||||
The values are set by writing the desired labels, separated
|
||||
by spaces, to the file or cleared by writing "-" to the file.
|
||||
|
||||
If you are using the smackload utility
|
||||
you can add access rules in /etc/smack/accesses. They take the form:
|
||||
|
@ -115,6 +115,7 @@ struct task_smack {
|
||||
struct smack_known *smk_forked; /* label when forked */
|
||||
struct list_head smk_rules; /* per task access rules */
|
||||
struct mutex smk_rules_lock; /* lock for the rules */
|
||||
struct list_head smk_relabel; /* transit allowed labels */
|
||||
};
|
||||
|
||||
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
|
||||
@ -169,7 +170,7 @@ struct smk_port_label {
|
||||
};
|
||||
#endif /* SMACK_IPV6_PORT_LABELING */
|
||||
|
||||
struct smack_onlycap {
|
||||
struct smack_known_list_elem {
|
||||
struct list_head list;
|
||||
struct smack_known *smk_label;
|
||||
};
|
||||
@ -301,6 +302,7 @@ struct smack_known *smk_import_entry(const char *, int);
|
||||
void smk_insert_entry(struct smack_known *skp);
|
||||
struct smack_known *smk_find_entry(const char *);
|
||||
int smack_privileged(int cap);
|
||||
void smk_destroy_label_list(struct list_head *list);
|
||||
|
||||
/*
|
||||
* Shared data.
|
||||
|
@ -637,7 +637,7 @@ DEFINE_MUTEX(smack_onlycap_lock);
|
||||
int smack_privileged(int cap)
|
||||
{
|
||||
struct smack_known *skp = smk_of_current();
|
||||
struct smack_onlycap *sop;
|
||||
struct smack_known_list_elem *sklep;
|
||||
|
||||
/*
|
||||
* All kernel tasks are privileged
|
||||
@ -654,8 +654,8 @@ int smack_privileged(int cap)
|
||||
return 1;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(sop, &smack_onlycap_list, list) {
|
||||
if (sop->smk_label == skp) {
|
||||
list_for_each_entry_rcu(sklep, &smack_onlycap_list, list) {
|
||||
if (sklep->smk_label == skp) {
|
||||
rcu_read_unlock();
|
||||
return 1;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
#define SMK_SENDING 2
|
||||
|
||||
#ifdef SMACK_IPV6_PORT_LABELING
|
||||
LIST_HEAD(smk_ipv6_port_list);
|
||||
static LIST_HEAD(smk_ipv6_port_list);
|
||||
#endif
|
||||
static struct kmem_cache *smack_inode_cache;
|
||||
int smack_enabled;
|
||||
@ -326,6 +326,7 @@ static struct task_smack *new_task_smack(struct smack_known *task,
|
||||
tsp->smk_task = task;
|
||||
tsp->smk_forked = forked;
|
||||
INIT_LIST_HEAD(&tsp->smk_rules);
|
||||
INIT_LIST_HEAD(&tsp->smk_relabel);
|
||||
mutex_init(&tsp->smk_rules_lock);
|
||||
|
||||
return tsp;
|
||||
@ -360,6 +361,35 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_copy_relabel - copy smk_relabel labels list
|
||||
* @nhead: new rules header pointer
|
||||
* @ohead: old rules header pointer
|
||||
* @gfp: type of the memory for the allocation
|
||||
*
|
||||
* Returns 0 on success, -ENOMEM on error
|
||||
*/
|
||||
static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct smack_known_list_elem *nklep;
|
||||
struct smack_known_list_elem *oklep;
|
||||
|
||||
INIT_LIST_HEAD(nhead);
|
||||
|
||||
list_for_each_entry(oklep, ohead, list) {
|
||||
nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp);
|
||||
if (nklep == NULL) {
|
||||
smk_destroy_label_list(nhead);
|
||||
return -ENOMEM;
|
||||
}
|
||||
nklep->smk_label = oklep->smk_label;
|
||||
list_add(&nklep->list, nhead);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_*
|
||||
* @mode - input mode in form of PTRACE_MODE_*
|
||||
@ -1922,6 +1952,8 @@ static void smack_cred_free(struct cred *cred)
|
||||
return;
|
||||
cred->security = NULL;
|
||||
|
||||
smk_destroy_label_list(&tsp->smk_relabel);
|
||||
|
||||
list_for_each_safe(l, n, &tsp->smk_rules) {
|
||||
rp = list_entry(l, struct smack_rule, list);
|
||||
list_del(&rp->list);
|
||||
@ -1953,6 +1985,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
|
||||
gfp);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
new->security = new_tsp;
|
||||
return 0;
|
||||
}
|
||||
@ -3354,6 +3391,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
|
||||
*/
|
||||
isp->smk_inode = smk_of_current();
|
||||
break;
|
||||
case PIPEFS_MAGIC:
|
||||
isp->smk_inode = smk_of_current();
|
||||
break;
|
||||
default:
|
||||
isp->smk_inode = sbsp->smk_root;
|
||||
break;
|
||||
@ -3549,9 +3589,11 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
|
||||
static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
struct task_smack *tsp;
|
||||
struct task_smack *tsp = current_security();
|
||||
struct cred *new;
|
||||
struct smack_known *skp;
|
||||
struct smack_known_list_elem *sklep;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Changing another process' Smack value is too dangerous
|
||||
@ -3560,7 +3602,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
if (p != current)
|
||||
return -EPERM;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
|
||||
return -EPERM;
|
||||
|
||||
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
|
||||
@ -3579,12 +3621,27 @@ static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
if (skp == &smack_known_web)
|
||||
return -EPERM;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN)) {
|
||||
rc = -EPERM;
|
||||
list_for_each_entry(sklep, &tsp->smk_relabel, list)
|
||||
if (sklep->smk_label == skp) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
new = prepare_creds();
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tsp = new->security;
|
||||
tsp->smk_task = skp;
|
||||
/*
|
||||
* process can change its label only once
|
||||
*/
|
||||
smk_destroy_label_list(&tsp->smk_relabel);
|
||||
|
||||
commit_creds(new);
|
||||
return size;
|
||||
@ -4708,8 +4765,6 @@ static __init int smack_init(void)
|
||||
if (!security_module_enable("smack"))
|
||||
return 0;
|
||||
|
||||
smack_enabled = 1;
|
||||
|
||||
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
|
||||
if (!smack_inode_cache)
|
||||
return -ENOMEM;
|
||||
@ -4721,6 +4776,8 @@ static __init int smack_init(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
smack_enabled = 1;
|
||||
|
||||
pr_info("Smack: Initializing.\n");
|
||||
#ifdef CONFIG_SECURITY_SMACK_NETFILTER
|
||||
pr_info("Smack: Netfilter enabled.\n");
|
||||
|
@ -61,6 +61,7 @@ enum smk_inos {
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
SMK_NET6ADDR = 23, /* single label IPv6 hosts */
|
||||
#endif /* CONFIG_IPV6 */
|
||||
SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1501,8 +1502,8 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
|
||||
*/
|
||||
if (smack[0] != '-') {
|
||||
skp = smk_import_entry(smack, 0);
|
||||
if (skp == NULL) {
|
||||
rc = -EINVAL;
|
||||
if (IS_ERR(skp)) {
|
||||
rc = PTR_ERR(skp);
|
||||
goto free_out;
|
||||
}
|
||||
} else {
|
||||
@ -1914,10 +1915,10 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
static int onlycap_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct list_head *list = v;
|
||||
struct smack_onlycap *sop =
|
||||
list_entry_rcu(list, struct smack_onlycap, list);
|
||||
struct smack_known_list_elem *sklep =
|
||||
list_entry_rcu(list, struct smack_known_list_elem, list);
|
||||
|
||||
seq_puts(s, sop->smk_label->smk_known);
|
||||
seq_puts(s, sklep->smk_label->smk_known);
|
||||
seq_putc(s, ' ');
|
||||
|
||||
return 0;
|
||||
@ -1973,6 +1974,54 @@ static void smk_list_swap_rcu(struct list_head *public,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_parse_label_list - parse list of Smack labels, separated by spaces
|
||||
*
|
||||
* @data: the string to parse
|
||||
* @private: destination list
|
||||
*
|
||||
* Returns zero on success or error code, as appropriate
|
||||
*/
|
||||
static int smk_parse_label_list(char *data, struct list_head *list)
|
||||
{
|
||||
char *tok;
|
||||
struct smack_known *skp;
|
||||
struct smack_known_list_elem *sklep;
|
||||
|
||||
while ((tok = strsep(&data, " ")) != NULL) {
|
||||
if (!*tok)
|
||||
continue;
|
||||
|
||||
skp = smk_import_entry(tok, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
|
||||
sklep = kzalloc(sizeof(*sklep), GFP_KERNEL);
|
||||
if (sklep == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
sklep->smk_label = skp;
|
||||
list_add(&sklep->list, list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_destroy_label_list - destroy a list of smack_known_list_elem
|
||||
* @head: header pointer of the list to destroy
|
||||
*/
|
||||
void smk_destroy_label_list(struct list_head *list)
|
||||
{
|
||||
struct smack_known_list_elem *sklep;
|
||||
struct smack_known_list_elem *sklep2;
|
||||
|
||||
list_for_each_entry_safe(sklep, sklep2, list, list)
|
||||
kfree(sklep);
|
||||
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_write_onlycap - write() for smackfs/onlycap
|
||||
* @file: file pointer, not actually used
|
||||
@ -1986,13 +2035,8 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *data;
|
||||
char *data_parse;
|
||||
char *tok;
|
||||
struct smack_known *skp;
|
||||
struct smack_onlycap *sop;
|
||||
struct smack_onlycap *sop2;
|
||||
LIST_HEAD(list_tmp);
|
||||
int rc = count;
|
||||
int rc;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
@ -2006,26 +2050,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
data_parse = data;
|
||||
while ((tok = strsep(&data_parse, " ")) != NULL) {
|
||||
if (!*tok)
|
||||
continue;
|
||||
|
||||
skp = smk_import_entry(tok, 0);
|
||||
if (IS_ERR(skp)) {
|
||||
rc = PTR_ERR(skp);
|
||||
break;
|
||||
}
|
||||
|
||||
sop = kzalloc(sizeof(*sop), GFP_KERNEL);
|
||||
if (sop == NULL) {
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
sop->smk_label = skp;
|
||||
list_add_rcu(&sop->list, &list_tmp);
|
||||
}
|
||||
rc = smk_parse_label_list(data, &list_tmp);
|
||||
kfree(data);
|
||||
|
||||
/*
|
||||
@ -2038,17 +2063,14 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
||||
* But do so only on invalid label, not on system errors.
|
||||
* The invalid label must be first to count as clearing attempt.
|
||||
*/
|
||||
if (rc == -EINVAL && list_empty(&list_tmp))
|
||||
rc = count;
|
||||
|
||||
if (rc >= 0) {
|
||||
if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
|
||||
mutex_lock(&smack_onlycap_lock);
|
||||
smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
|
||||
mutex_unlock(&smack_onlycap_lock);
|
||||
rc = count;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(sop, sop2, &list_tmp, list)
|
||||
kfree(sop);
|
||||
smk_destroy_label_list(&list_tmp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -2698,6 +2720,113 @@ static const struct file_operations smk_syslog_ops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* Seq_file read operations for /smack/relabel-self
|
||||
*/
|
||||
|
||||
static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
|
||||
return smk_seq_start(s, pos, &tsp->smk_relabel);
|
||||
}
|
||||
|
||||
static void *relabel_self_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_relabel);
|
||||
}
|
||||
|
||||
static int relabel_self_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct list_head *list = v;
|
||||
struct smack_known_list_elem *sklep =
|
||||
list_entry(list, struct smack_known_list_elem, list);
|
||||
|
||||
seq_puts(s, sklep->smk_label->smk_known);
|
||||
seq_putc(s, ' ');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations relabel_self_seq_ops = {
|
||||
.start = relabel_self_seq_start,
|
||||
.next = relabel_self_seq_next,
|
||||
.show = relabel_self_seq_show,
|
||||
.stop = smk_seq_stop,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_open_relabel_self - open() for /smack/relabel-self
|
||||
* @inode: inode structure representing file
|
||||
* @file: "relabel-self" file pointer
|
||||
*
|
||||
* Connect our relabel_self_seq_* operations with /smack/relabel-self
|
||||
* file_operations
|
||||
*/
|
||||
static int smk_open_relabel_self(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &relabel_self_seq_ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_write_relabel_self - write() for /smack/relabel-self
|
||||
* @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_relabel_self(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
char *data;
|
||||
int rc;
|
||||
LIST_HEAD(list_tmp);
|
||||
|
||||
/*
|
||||
* Must have privilege.
|
||||
*/
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* Enough data must be present.
|
||||
*/
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(count + 1, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, buf, count) != 0) {
|
||||
kfree(data);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
rc = smk_parse_label_list(data, &list_tmp);
|
||||
kfree(data);
|
||||
|
||||
if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
|
||||
smk_destroy_label_list(&tsp->smk_relabel);
|
||||
list_splice(&list_tmp, &tsp->smk_relabel);
|
||||
return count;
|
||||
}
|
||||
|
||||
smk_destroy_label_list(&list_tmp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_relabel_self_ops = {
|
||||
.open = smk_open_relabel_self,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = smk_write_relabel_self,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_read_ptrace - read() for /smack/ptrace
|
||||
@ -2824,6 +2953,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
||||
[SMK_NET6ADDR] = {
|
||||
"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
|
||||
#endif /* CONFIG_IPV6 */
|
||||
[SMK_RELABEL_SELF] = {
|
||||
"relabel-self", &smk_relabel_self_ops,
|
||||
S_IRUGO|S_IWUGO},
|
||||
/* last one */
|
||||
{""}
|
||||
};
|
||||
@ -2892,7 +3024,7 @@ static int __init init_smk_fs(void)
|
||||
int err;
|
||||
int rc;
|
||||
|
||||
if (!security_module_enable("smack"))
|
||||
if (smack_enabled == 0)
|
||||
return 0;
|
||||
|
||||
err = smk_init_sysfs();
|
||||
|
Loading…
Reference in New Issue
Block a user