This makes it possible for a root-task to pass capabilities to nonroot-task across execve. The root-task needs to change it's cap_inheritable mask and set prctl(PR_SET_KEEPCAPS, 1) to pass on capabilities. At execve time the capabilities will be passed on to the new nonroot-task and any non-inheritable effective and permitted capabilities will be masked out. The effective capability of the new nonroot-task will be set to the maximum permitted. >From here on the inheritable mask will be passed on unchanged to the new tasks children unless told otherwise (effectively the complete capability state is passed on). With a small insert of prctl(PR_SET_KEEPCAPS, 1) into pam_cap.c at the correct place this makes pam_cap work as expected. I'm also attaching a test-case that tests capabilities across setuid => execve (makes the new task inherit CAP_CHOWN). Signed-off-by: Alexander Nyberg ===== security/commoncap.c 1.15 vs edited ===== --- 1.15/security/commoncap.c 2005-01-11 02:29:23 +01:00 +++ edited/security/commoncap.c 2005-03-12 12:04:34 +01:00 @@ -111,13 +111,19 @@ void cap_capset_set (struct task_struct int cap_bprm_set_security (struct linux_binprm *bprm) { - /* Copied from fs/exec.c:prepare_binprm. */ - - /* We don't have VFS support for capabilities yet */ - cap_clear (bprm->cap_inheritable); - cap_clear (bprm->cap_permitted); - cap_clear (bprm->cap_effective); + struct task_struct *p = current; + /* + * Mask out the non-inheritable capabilities. + * Note: init has a zero mask of cap_inheritable, so a root-task will not + * pass on any capabilities unless explicitly told to do so. If a non-zero + * inheritable mask is passed to a positive uid task it will then pass on + * its inheritable mask to all children unless told otherwise. + */ + bprm->cap_permitted = cap_intersect(p->cap_permitted, p->cap_inheritable); + bprm->cap_effective = cap_intersect(p->cap_effective, p->cap_inheritable); + bprm->cap_inheritable = p->cap_inheritable; + /* To support inheritance of root-permissions and suid-root * executables under compatibility mode, we raise all three * capability sets for the file. @@ -127,7 +133,7 @@ int cap_bprm_set_security (struct linux_ */ if (!issecure (SECURE_NOROOT)) { - if (bprm->e_uid == 0 || current->uid == 0) { + if (bprm->e_uid == 0 || p->uid == 0) { cap_set_full (bprm->cap_inheritable); cap_set_full (bprm->cap_permitted); } @@ -139,13 +145,9 @@ int cap_bprm_set_security (struct linux_ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { - /* Derived from fs/exec.c:compute_creds. */ - kernel_cap_t new_permitted, working; + kernel_cap_t new_permitted; 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)) { @@ -166,14 +168,9 @@ void cap_bprm_apply_creds (struct linux_ 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); - } + current->cap_permitted = new_permitted; + current->cap_effective = + cap_intersect (new_permitted, bprm->cap_effective); /* AUD: Audit candidate if current->cap_effective is set */ @@ -249,9 +246,9 @@ static inline void cap_emulate_setxuid ( cap_clear (current->cap_permitted); cap_clear (current->cap_effective); } - if (old_euid == 0 && current->euid != 0) { + if (old_euid == 0 && current->euid != 0 && !current->keep_capabilities) cap_clear (current->cap_effective); - } + if (old_euid != 0 && current->euid == 0) { current->cap_effective = current->cap_permitted; }