Skip to content

Commit

Permalink
Smack: limited capability for changing process label
Browse files Browse the repository at this point in the history
This feature introduces new kernel interface:

- <smack_fs>/relabel-self - for setting transition labels list

This list is used to control smack label transition mechanism.
List is set by, and per process. Process can transit to new label only if
label is on the list. Only process with CAP_MAC_ADMIN capability can add
labels to this list. With this list, process can change it's label without
CAP_MAC_ADMIN but only once. After label changing, list is unset.

Changes in v2:
* use list_for_each_entry instead of _rcu during label write
* added missing description in security/Smack.txt

Changes in v3:
* squashed into one commit

Changes in v4:
* switch from global list to per-task list
* since the per-task list is accessed only by the task itself
  there is no need to use synchronization mechanisms on it

Changes in v5:
* change smackfs interface of relabel-self to the one used for onlycap
  multiple labels are accepted, separated by space, which
  replace the previous list upon write

Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
  • Loading branch information
penszo authored and cschaufler committed Oct 19, 2015
1 parent 8da4aba commit 38416e5
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 41 deletions.
10 changes: 10 additions & 0 deletions Documentation/security/Smack.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 3 additions & 1 deletion security/smack/smack.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions security/smack/smack_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
58 changes: 56 additions & 2 deletions security/smack/smack_lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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_*
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -3552,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
Expand All @@ -3563,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)
Expand All @@ -3582,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;
Expand Down
Loading

0 comments on commit 38416e5

Please sign in to comment.