int cap_capset_check (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { /* Derived from kernel/capability.c:sys_capset. */ /* verify restrictions on target's new Inheritable set */ if (!cap_issubset (*inheritable, cap_combine (target->cap_inheritable, current->cap_permitted))) { return -EPERM; } /* verify restrictions on target's new Permitted set */ if (!cap_issubset (*permitted, cap_combine (target->cap_permitted, current->cap_permitted))) { return -EPERM; } /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ if (!cap_issubset (*effective, *permitted)) { return -EPERM; } return 0; }
int cap_ptrace (struct task_struct *parent, struct task_struct *child) { /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ if (!cap_issubset(child->cap_permitted, parent->cap_permitted) && !__capable(parent, CAP_SYS_PTRACE)) return -EPERM; return 0; }
/** * cap_ptrace_traceme - Determine whether another process may trace the current * @parent: The task proposed to be the tracer * * 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; rcu_read_lock(); if (!cap_issubset(current_cred()->cap_permitted, __task_cred(parent)->cap_permitted) && !has_capability(parent, CAP_SYS_PTRACE)) ret = -EPERM; rcu_read_unlock(); return ret; }
/** * cap_ptrace_access_check - Determine whether the current process may access * another * @child: The process to be accessed * @mode: The mode of attachment. * * 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; rcu_read_lock(); if (!cap_issubset(__task_cred(child)->cap_permitted, current_cred()->cap_permitted) && !capable(CAP_SYS_PTRACE)) ret = -EPERM; rcu_read_unlock(); return ret; }
int ptrace_attach(struct task_struct *task) { task_lock(task); if (task->pid <= 1) goto bad; if (task == current) goto bad; if (!task->mm) goto bad; if(((current->uid != task->euid) || (current->uid != task->suid) || (current->uid != task->uid) || (current->gid != task->egid) || (current->gid != task->sgid) || (!cap_issubset(task->cap_permitted, current->cap_permitted)) || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) goto bad; rmb(); if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) goto bad; /* the same process cannot be attached many times */ if (task->ptrace & PT_PTRACED) goto bad; /* Go */ task->ptrace |= PT_PTRACED; if (capable(CAP_SYS_PTRACE)) task->ptrace |= PT_PTRACE_CAP; task_unlock(task); write_lock_irq(&tasklist_lock); if (task->p_pptr != current) { REMOVE_LINKS(task); task->p_pptr = current; SET_LINKS(task); } write_unlock_irq(&tasklist_lock); send_sig(SIGSTOP, task, 1); return 0; bad: task_unlock(task); return -EPERM; }
/** * 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(); 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; }
/** * cap_ptrace_access_check - Determine whether the current process may access * another * @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(); 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; }
void cap_bprm_compute_creds (struct linux_binprm *bprm) { /* Derived from fs/exec.c:compute_creds. */ kernel_cap_t new_permitted, working; int do_unlock = 0; new_permitted = cap_intersect (bprm->cap_permitted, cap_bset); working = cap_intersect (bprm->cap_inheritable, current->cap_inheritable); new_permitted = cap_combine (new_permitted, working); if (!cap_issubset (new_permitted, current->cap_permitted)) { current->mm->dumpable = 0; lock_kernel (); if (must_not_trace_exec (current) || atomic_read (¤t->fs->count) > 1 || atomic_read (¤t->files->count) > 1 || atomic_read (¤t->sig->count) > 1) { if (!capable (CAP_SETPCAP)) { new_permitted = cap_intersect (new_permitted, current-> cap_permitted); } } do_unlock = 1; } /* For init, we want to retain the capabilities set * in the init_task struct. Thus we skip the usual * capability rules */ if (current->pid != 1) { current->cap_permitted = new_permitted; current->cap_effective = cap_intersect (new_permitted, bprm->cap_effective); } /* AUD: Audit candidate if current->cap_effective is set */ if (do_unlock) unlock_kernel (); current->keep_capabilities = 0; }
void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { /* Derived from fs/exec.c:compute_creds. */ kernel_cap_t new_permitted, working; new_permitted = cap_intersect (bprm->cap_permitted, cap_bset); working = cap_intersect (bprm->cap_inheritable, current->cap_inheritable); new_permitted = cap_combine (new_permitted, working); if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || !cap_issubset (new_permitted, current->cap_permitted)) { current->mm->dumpable = 0; if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { if (!capable(CAP_SETUID)) { bprm->e_uid = current->uid; bprm->e_gid = current->gid; } if (!capable (CAP_SETPCAP)) { new_permitted = cap_intersect (new_permitted, current->cap_permitted); } } } current->suid = current->euid = current->fsuid = bprm->e_uid; current->sgid = current->egid = current->fsgid = bprm->e_gid; /* For init, we want to retain the capabilities set * in the init_task struct. Thus we skip the usual * capability rules */ if (current->pid != 1) { current->cap_permitted = new_permitted; current->cap_effective = cap_intersect (new_permitted, bprm->cap_effective); } /* AUD: Audit candidate if current->cap_effective is set */ current->keep_capabilities = 0; }
/** * cap_ptrace_access_check - Determine whether the current process may access * another * @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; const kernel_cap_t *caller_caps; rcu_read_lock(); cred = current_cred(); child_cred = __task_cred(child); if (mode & PTRACE_MODE_FSCREDS) caller_caps = &cred->cap_effective; else caller_caps = &cred->cap_permitted; if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, *caller_caps)) goto out; if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) goto out; ret = -EPERM; out: rcu_read_unlock(); return ret; }
asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) { kernel_cap_t inheritable, permitted, effective; __u32 version; struct task_struct *target; int error, pid; if (get_user(version, &header->version)) return -EFAULT; if (version != _LINUX_CAPABILITY_VERSION) { version = _LINUX_CAPABILITY_VERSION; if (put_user(version, &header->version)) return -EFAULT; return -EINVAL; } if (get_user(pid, &header->pid)) return -EFAULT; if (pid && !capable(CAP_SETPCAP)) return -EPERM; if (copy_from_user(&effective, &data->effective, sizeof(effective)) || copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) || copy_from_user(&permitted, &data->permitted, sizeof(permitted))) return -EFAULT; error = -EPERM; spin_lock(&task_capability_lock); if (pid > 0 && pid != current->pid) { read_lock(&tasklist_lock); target = find_task_by_pid(pid); /* identify target of query */ if (!target) { error = -ESRCH; goto out; } } else { target = current; } /* verify restrictions on target's new Inheritable set */ if (!cap_issubset(inheritable, cap_combine(target->cap_inheritable, current->cap_permitted))) { goto out; } /* verify restrictions on target's new Permitted set */ if (!cap_issubset(permitted, cap_combine(target->cap_permitted, current->cap_permitted))) { goto out; } /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ if (!cap_issubset(effective, permitted)) { goto out; } /* having verified that the proposed changes are legal, we now put them into effect. */ error = 0; if (pid < 0) { if (pid == -1) /* all procs other than current and init */ cap_set_all(&effective, &inheritable, &permitted); else /* all procs in process group */ cap_set_pg(-pid, &effective, &inheritable, &permitted); goto spin_out; } else { /* FIXME: do we need to have a write lock here..? */ target->cap_effective = effective; target->cap_inheritable = inheritable; target->cap_permitted = permitted; } out: if (target != current) { read_unlock(&tasklist_lock); } spin_out: spin_unlock(&task_capability_lock); return error; }
long sys_ptrace(long request, pid_t pid, long addr, long data) { struct task_struct *child; long ret; lock_kernel(); ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* no messing around with init! */ goto out_tsk; if (request == PTRACE_ATTACH) { if (child == current) goto out_tsk; if ((!child->dumpable || (current->uid != child->euid) || (current->uid != child->suid) || (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out_tsk; /* the same process cannot be attached many times */ if (child->ptrace & PT_PTRACED) goto out_tsk; child->ptrace |= PT_PTRACED; if (child->p_pptr != current) { unsigned long flags; write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); write_unlock_irqrestore(&tasklist_lock, flags); } send_sig(SIGSTOP, child, 1); ret = 0; goto out_tsk; } ret = -ESRCH; if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; switch (request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; int copied; copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); ret = -EIO; if (copied != sizeof(tmp)) goto out_tsk; ret = put_user(tmp,(unsigned long *) data); goto out_tsk; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) goto out_tsk; ret = -EIO; goto out_tsk; /* Read the word at location addr in the USER area. This will need to change when the kernel no longer saves all regs on a syscall. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) goto out_tsk; tmp = *(unsigned long *) ((char *) task_regs(child) + addr); ret = put_user(tmp, (unsigned long *) data); goto out_tsk; } /* Write the word at location addr in the USER area. This will need to change when the kernel no longer saves all regs on a syscall. FIXME. There is a problem at the moment in that r3-r18 are only saved if the process is ptraced on syscall entry, and even then those values are overwritten by actual register values on syscall exit. */ case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) goto out_tsk; /* XXX This test probably needs adjusting. We probably want to * allow writes to some bits of PSW, and may want to block writes * to (some) space registers. Some register values written here * may be ignored in entry.S:syscall_restore_rfi; e.g. iaoq is * written with r31/r31+4, and not with the values in pt_regs. */ /* Allow writing of gr1-gr31, fr*, sr*, iasq*, iaoq*, sar */ if (addr == PT_PSW || (addr > PT_IAOQ1 && addr != PT_SAR)) goto out_tsk; *(unsigned long *) ((char *) task_regs(child) + addr) = data; ret = 0; goto out_tsk; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP); if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; goto out_wake_notrap; case PTRACE_KILL: /* * make the child exit. Best I can do is send it a * sigkill. perhaps it should be put in the status * that it wants to exit. */ if (child->state == TASK_ZOMBIE) /* already dead */ goto out_tsk; child->exit_code = SIGKILL; goto out_wake_notrap; case PTRACE_SINGLEBLOCK: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_TRACESYS|PT_SINGLESTEP); child->ptrace |= PT_BLOCKSTEP; child->exit_code = data; /* Enable taken branch trap. */ pa_psw(child)->r = 0; pa_psw(child)->t = 1; pa_psw(child)->h = 0; pa_psw(child)->l = 0; goto out_wake; case PTRACE_SINGLESTEP: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_TRACESYS|PT_BLOCKSTEP); child->ptrace |= PT_SINGLESTEP; child->exit_code = data; if (pa_psw(child)->n) { struct siginfo si; /* Nullified, just crank over the queue. */ task_regs(child)->iaoq[0] = task_regs(child)->iaoq[1]; task_regs(child)->iasq[0] = task_regs(child)->iasq[1]; task_regs(child)->iaoq[1] = task_regs(child)->iaoq[0] + 4; pa_psw(child)->n = 0; pa_psw(child)->x = 0; pa_psw(child)->y = 0; pa_psw(child)->z = 0; pa_psw(child)->b = 0; pa_psw(child)->r = 0; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; /* Don't wake up the child, but let the parent know something happened. */ si.si_code = TRAP_TRACE; si.si_addr = (void *) (task_regs(child)->iaoq[0] & ~3); si.si_signo = SIGTRAP; si.si_errno = 0; force_sig_info(SIGTRAP, &si, child); //notify_parent(child, SIGCHLD); //ret = 0; goto out_wake; } /* Enable recovery counter traps. The recovery counter * itself will be set to zero on a task switch. If the * task is suspended on a syscall then the syscall return * path will overwrite the recovery counter with a suitable * value such that it traps once back in user space. We * disable interrupts in the childs PSW here also, to avoid * interrupts while the recovery counter is decrementing. */ pa_psw(child)->r = 1; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; /* give it a chance to run. */ goto out_wake; case PTRACE_DETACH: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_PTRACED|PT_TRACESYS|PT_SINGLESTEP|PT_BLOCKSTEP); child->exit_code = data; write_lock_irq(&tasklist_lock); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); write_unlock_irq(&tasklist_lock); goto out_wake_notrap; default: ret = -EIO; goto out_tsk; } out_wake_notrap: /* make sure the trap bits are not set */ pa_psw(child)->r = 0; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; out_wake: wake_up_process(child); ret = 0; out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
static int krg_set_cap(struct task_struct *tsk, const kernel_krg_cap_t *requested_cap) { kernel_krg_cap_t *caps = &tsk->krg_caps; kernel_cap_t tmp_cap; struct nsproxy *nsp; int res; int i; res = 0; rcu_read_lock(); nsp = rcu_dereference(tsk->nsproxy); if (!nsp || !nsp->krg_ns) res = -EPERM; rcu_read_unlock(); if (res) goto out; res = -EINVAL; if (!cap_issubset(requested_cap->effective, requested_cap->permitted) || !cap_issubset(requested_cap->inheritable_permitted, requested_cap->permitted) || !cap_issubset(requested_cap->inheritable_effective, requested_cap->inheritable_permitted)) goto out; res = -ENOSYS; tmp_cap = KRG_CAP_SUPPORTED; if (!cap_issubset(requested_cap->permitted, tmp_cap)) goto out; res = -EPERM; if (!permissions_ok(tsk)) goto out; task_lock(tsk); if (!cap_raised(caps->effective, CAP_CHANGE_KERRIGHED_CAP)) goto out_unlock; res = -EBUSY; for (i = 0; i < CAP_SIZE; i++) if (atomic_read(&tsk->krg_cap_used[i]) && !cap_raised(requested_cap->effective, i)) goto out_unlock; tmp_cap = cap_intersect(caps->permitted, requested_cap->permitted); caps->permitted = tmp_cap; tmp_cap = cap_intersect(caps->permitted, requested_cap->effective); caps->effective = tmp_cap; tmp_cap = cap_intersect(caps->permitted, requested_cap->inheritable_effective); caps->inheritable_effective = tmp_cap; tmp_cap = cap_intersect(caps->permitted, requested_cap->inheritable_permitted); caps->inheritable_permitted = tmp_cap; res = 0; out_unlock: task_unlock(tsk); out: return res; }