]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - security/smack/smack_lsm.c
Smack: limited capability for changing process label
[karo-tx-linux.git] / security / smack / smack_lsm.c
index c2d66ca1127ab63d9130084087cd85dc1f1f9cb3..ff81026f6ddbae7de368a1ae4bfbfeb1a66a9ae6 100644 (file)
@@ -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;
 }
@@ -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;