TOMOYO: Add ACL group support.
[linux-2.6.git] / security / commoncap.c
index a83e607..a93b3b7 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/sched.h>
 #include <linux/prctl.h>
 #include <linux/securebits.h>
+#include <linux/user_namespace.h>
 
 /*
  * If a non-root user executes a setuid-root binary in
@@ -67,6 +68,7 @@ EXPORT_SYMBOL(cap_netlink_recv);
  * cap_capable - Determine whether a task has a particular effective capability
  * @tsk: The task to query
  * @cred: The credentials to use
+ * @ns:  The user namespace in which we need the capability
  * @cap: The capability to check for
  * @audit: Whether to write an audit message or not
  *
@@ -78,10 +80,30 @@ EXPORT_SYMBOL(cap_netlink_recv);
  * cap_has_capability() returns 0 when a task has a capability, but the
  * kernel's capable() and has_capability() returns 1 for this case.
  */
-int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
-               int audit)
+int cap_capable(struct task_struct *tsk, const struct cred *cred,
+               struct user_namespace *targ_ns, int cap, int audit)
 {
-       return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+       for (;;) {
+               /* The creator of the user namespace has all caps. */
+               if (targ_ns != &init_user_ns && targ_ns->creator == cred->user)
+                       return 0;
+
+               /* Do we have the necessary capabilities? */
+               if (targ_ns == cred->user->user_ns)
+                       return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
+
+               /* Have we tried all of the parent namespaces? */
+               if (targ_ns == &init_user_ns)
+                       return -EPERM;
+
+               /*
+                *If you have a capability in a parent user ns, then you have
+                * it over all children user namespaces as well.
+                */
+               targ_ns = targ_ns->creator->user_ns;
+       }
+
+       /* We never get here */
 }
 
 /**
@@ -92,7 +114,7 @@ int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap,
  * Determine whether the current process may set the system clock and timezone
  * information, returning 0 if permission granted, -ve if denied.
  */
-int cap_settime(struct timespec *ts, struct timezone *tz)
+int cap_settime(const struct timespec *ts, const struct timezone *tz)
 {
        if (!capable(CAP_SYS_TIME))
                return -EPERM;
@@ -105,18 +127,30 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
  * @child: The process to be accessed
  * @mode: The mode of attachment.
  *
+ * If we are in the same or an ancestor user_ns and have all the target
+ * task's capabilities, then ptrace access is allowed.
+ * If we have the ptrace capability to the target user_ns, then ptrace
+ * access is allowed.
+ * Else denied.
+ *
  * Determine whether a process may access another, returning 0 if permission
  * granted, -ve if denied.
  */
 int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
        int ret = 0;
+       const struct cred *cred, *child_cred;
 
        rcu_read_lock();
-       if (!cap_issubset(__task_cred(child)->cap_permitted,
-                         current_cred()->cap_permitted) &&
-           !capable(CAP_SYS_PTRACE))
-               ret = -EPERM;
+       cred = current_cred();
+       child_cred = __task_cred(child);
+       if (cred->user->user_ns == child_cred->user->user_ns &&
+           cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
+               goto out;
+       if (ns_capable(child_cred->user->user_ns, CAP_SYS_PTRACE))
+               goto out;
+       ret = -EPERM;
+out:
        rcu_read_unlock();
        return ret;
 }
@@ -125,18 +159,30 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
  * cap_ptrace_traceme - Determine whether another process may trace the current
  * @parent: The task proposed to be the tracer
  *
+ * If parent is in the same or an ancestor user_ns and has all current's
+ * capabilities, then ptrace access is allowed.
+ * If parent has the ptrace capability to current's user_ns, then ptrace
+ * access is allowed.
+ * Else denied.
+ *
  * Determine whether the nominated task is permitted to trace the current
  * process, returning 0 if permission is granted, -ve if denied.
  */
 int cap_ptrace_traceme(struct task_struct *parent)
 {
        int ret = 0;
+       const struct cred *cred, *child_cred;
 
        rcu_read_lock();
-       if (!cap_issubset(current_cred()->cap_permitted,
-                         __task_cred(parent)->cap_permitted) &&
-           !has_capability(parent, CAP_SYS_PTRACE))
-               ret = -EPERM;
+       cred = __task_cred(parent);
+       child_cred = current_cred();
+       if (cred->user->user_ns == child_cred->user->user_ns &&
+           cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
+               goto out;
+       if (has_ns_capability(parent, child_cred->user->user_ns, CAP_SYS_PTRACE))
+               goto out;
+       ret = -EPERM;
+out:
        rcu_read_unlock();
        return ret;
 }
@@ -176,7 +222,8 @@ static inline int cap_inh_is_capped(void)
        /* they are so limited unless the current task has the CAP_SETPCAP
         * capability
         */
-       if (cap_capable(current, current_cred(), CAP_SETPCAP,
+       if (cap_capable(current, current_cred(),
+                       current_cred()->user->user_ns, CAP_SETPCAP,
                        SECURITY_CAP_AUDIT) == 0)
                return 0;
        return 1;
@@ -482,15 +529,10 @@ skip:
        new->suid = new->fsuid = new->euid;
        new->sgid = new->fsgid = new->egid;
 
-       /* For init, we want to retain the capabilities set in the initial
-        * task.  Thus we skip the usual capability rules
-        */
-       if (!is_global_init(current)) {
-               if (effective)
-                       new->cap_effective = new->cap_permitted;
-               else
-                       cap_clear(new->cap_effective);
-       }
+       if (effective)
+               new->cap_effective = new->cap_permitted;
+       else
+               cap_clear(new->cap_effective);
        bprm->cap_effective = effective;
 
        /*
@@ -828,7 +870,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                     & (new->securebits ^ arg2))                        /*[1]*/
                    || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))   /*[2]*/
                    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))   /*[3]*/
-                   || (cap_capable(current, current_cred(), CAP_SETPCAP,
+                   || (cap_capable(current, current_cred(),
+                                   current_cred()->user->user_ns, CAP_SETPCAP,
                                    SECURITY_CAP_AUDIT) != 0)           /*[4]*/
                        /*
                         * [1] no changing of bits that are locked
@@ -893,7 +936,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
 {
        int cap_sys_admin = 0;
 
-       if (cap_capable(current, current_cred(), CAP_SYS_ADMIN,
+       if (cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_ADMIN,
                        SECURITY_CAP_NOAUDIT) == 0)
                cap_sys_admin = 1;
        return __vm_enough_memory(mm, pages, cap_sys_admin);
@@ -920,7 +963,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,
        int ret = 0;
 
        if (addr < dac_mmap_min_addr) {
-               ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO,
+               ret = cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_RAWIO,
                                  SECURITY_CAP_AUDIT);
                /* set PF_SUPERPRIV if it turns out we allow the low mmap */
                if (ret == 0)