diff options
Diffstat (limited to 'security/smack/smack_lsm.c')
-rw-r--r-- | security/smack/smack_lsm.c | 58 |
1 files changed, 56 insertions, 2 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c2d66ca1127a..ff81026f6ddb 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -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; @@ -361,6 +362,35 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, } /** + * 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; } @@ -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 @@ -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) @@ -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; |