Index: head/sys/kern/kern_exec.c =================================================================== --- head/sys/kern/kern_exec.c +++ head/sys/kern/kern_exec.c @@ -356,7 +356,7 @@ { struct proc *p = td->td_proc; struct nameidata nd; - struct ucred *newcred = NULL, *oldcred; + struct ucred *oldcred; struct uidinfo *euip = NULL; register_t *stack_base; int error, i; @@ -404,6 +404,7 @@ imgp->proc = p; imgp->attr = &attr; imgp->args = args; + oldcred = p->p_ucred; #ifdef MAC error = mac_execve_enter(imgp, mac_p); @@ -485,6 +486,87 @@ goto exec_fail_dealloc; imgp->proc->p_osrel = 0; + + /* + * Implement image setuid/setgid. + * + * Determine new credentials before attempting image activators + * so that it can be used by process_exec handlers to determine + * credential/setid changes. + * + * Don't honor setuid/setgid if the filesystem prohibits it or if + * the process is being traced. + * + * We disable setuid/setgid/etc in capability mode on the basis + * that most setugid applications are not written with that + * environment in mind, and will therefore almost certainly operate + * incorrectly. In principle there's no reason that setugid + * applications might not be useful in capability mode, so we may want + * to reconsider this conservative design choice in the future. + * + * XXXMAC: For the time being, use NOSUID to also prohibit + * transitions on the file system. + */ + credential_changing = 0; + credential_changing |= (attr.va_mode & S_ISUID) && + oldcred->cr_uid != attr.va_uid; + credential_changing |= (attr.va_mode & S_ISGID) && + oldcred->cr_gid != attr.va_gid; +#ifdef MAC + will_transition = mac_vnode_execve_will_transition(oldcred, imgp->vp, + interpvplabel, imgp); + credential_changing |= will_transition; +#endif + + if (credential_changing && +#ifdef CAPABILITY_MODE + ((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) && +#endif + (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 && + (p->p_flag & P_TRACED) == 0) { + imgp->credential_setid = true; + VOP_UNLOCK(imgp->vp, 0); + imgp->newcred = crdup(oldcred); + if (attr.va_mode & S_ISUID) { + euip = uifind(attr.va_uid); + change_euid(imgp->newcred, euip); + } + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); + if (attr.va_mode & S_ISGID) + change_egid(imgp->newcred, attr.va_gid); + /* + * Implement correct POSIX saved-id behavior. + * + * XXXMAC: Note that the current logic will save the + * uid and gid if a MAC domain transition occurs, even + * though maybe it shouldn't. + */ + change_svuid(imgp->newcred, imgp->newcred->cr_uid); + change_svgid(imgp->newcred, imgp->newcred->cr_gid); + } else { + /* + * Implement correct POSIX saved-id behavior. + * + * XXX: It's not clear that the existing behavior is + * POSIX-compliant. A number of sources indicate that the + * saved uid/gid should only be updated if the new ruid is + * not equal to the old ruid, or the new euid is not equal + * to the old euid and the new euid is not equal to the old + * ruid. The FreeBSD code always updates the saved uid/gid. + * Also, this code uses the new (replaced) euid and egid as + * the source, which may or may not be the right ones to use. + */ + if (oldcred->cr_svuid != oldcred->cr_uid || + oldcred->cr_svgid != oldcred->cr_gid) { + VOP_UNLOCK(imgp->vp, 0); + imgp->newcred = crdup(oldcred); + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); + change_svuid(imgp->newcred, imgp->newcred->cr_uid); + change_svgid(imgp->newcred, imgp->newcred->cr_gid); + } + } + /* The new credentials are installed into the process later. */ + /* * If the current process has a special image activator it * wants to try first, call it. For example, emulating shell @@ -542,6 +624,11 @@ vput(newtextvp); vm_object_deallocate(imgp->object); imgp->object = NULL; + imgp->credential_setid = false; + if (imgp->newcred != NULL) { + crfree(imgp->newcred); + imgp->newcred = NULL; + } /* set new name to that of the interpreter */ NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME, UIO_SYSSPACE, imgp->interpreter_name, td); @@ -639,7 +726,6 @@ PROC_LOCK(p); if (oldsigacts) p->p_sigacts = newsigacts; - oldcred = p->p_ucred; /* Stop profiling */ stopprofclock(p); @@ -671,38 +757,9 @@ } /* - * Implement image setuid/setgid. - * - * Don't honor setuid/setgid if the filesystem prohibits it or if - * the process is being traced. - * - * We disable setuid/setgid/etc in capability mode on the basis - * that most setugid applications are not written with that - * environment in mind, and will therefore almost certainly operate - * incorrectly. In principle there's no reason that setugid - * applications might not be useful in capability mode, so we may want - * to reconsider this conservative design choice in the future. - * - * XXXMAC: For the time being, use NOSUID to also prohibit - * transitions on the file system. + * Implement image setuid/setgid installation. */ - credential_changing = 0; - credential_changing |= (attr.va_mode & S_ISUID) && oldcred->cr_uid != - attr.va_uid; - credential_changing |= (attr.va_mode & S_ISGID) && oldcred->cr_gid != - attr.va_gid; -#ifdef MAC - will_transition = mac_vnode_execve_will_transition(oldcred, imgp->vp, - interpvplabel, imgp); - credential_changing |= will_transition; -#endif - - if (credential_changing && -#ifdef CAPABILITY_MODE - ((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) && -#endif - (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 && - (p->p_flag & P_TRACED) == 0) { + if (imgp->credential_setid) { /* * Turn off syscall tracing for set-id programs, except for * root. Record any set-id flags first to make sure that @@ -728,61 +785,24 @@ error = fdcheckstd(td); if (error != 0) goto done1; - newcred = crdup(oldcred); - euip = uifind(attr.va_uid); vn_lock(imgp->vp, LK_SHARED | LK_RETRY); PROC_LOCK(p); - /* - * Set the new credentials. - */ - if (attr.va_mode & S_ISUID) - change_euid(newcred, euip); - if (attr.va_mode & S_ISGID) - change_egid(newcred, attr.va_gid); #ifdef MAC if (will_transition) { - mac_vnode_execve_transition(oldcred, newcred, imgp->vp, - interpvplabel, imgp); + mac_vnode_execve_transition(oldcred, imgp->newcred, + imgp->vp, interpvplabel, imgp); } #endif - /* - * Implement correct POSIX saved-id behavior. - * - * XXXMAC: Note that the current logic will save the - * uid and gid if a MAC domain transition occurs, even - * though maybe it shouldn't. - */ - change_svuid(newcred, newcred->cr_uid); - change_svgid(newcred, newcred->cr_gid); - proc_set_cred(p, newcred); } else { if (oldcred->cr_uid == oldcred->cr_ruid && oldcred->cr_gid == oldcred->cr_rgid) p->p_flag &= ~P_SUGID; - /* - * Implement correct POSIX saved-id behavior. - * - * XXX: It's not clear that the existing behavior is - * POSIX-compliant. A number of sources indicate that the - * saved uid/gid should only be updated if the new ruid is - * not equal to the old ruid, or the new euid is not equal - * to the old euid and the new euid is not equal to the old - * ruid. The FreeBSD code always updates the saved uid/gid. - * Also, this code uses the new (replaced) euid and egid as - * the source, which may or may not be the right ones to use. - */ - if (oldcred->cr_svuid != oldcred->cr_uid || - oldcred->cr_svgid != oldcred->cr_gid) { - PROC_UNLOCK(p); - VOP_UNLOCK(imgp->vp, 0); - newcred = crdup(oldcred); - vn_lock(imgp->vp, LK_SHARED | LK_RETRY); - PROC_LOCK(p); - change_svuid(newcred, newcred->cr_uid); - change_svgid(newcred, newcred->cr_gid); - proc_set_cred(p, newcred); - } } + /* + * Set the new credentials. + */ + if (imgp->newcred != NULL) + proc_set_cred(p, imgp->newcred); /* * Store the vp for use in procfs. This vnode was referenced by namei @@ -856,14 +876,6 @@ VOP_UNLOCK(imgp->vp, 0); done1: /* - * Free any resources malloc'd earlier that we didn't use. - */ - if (euip != NULL) - uifree(euip); - if (newcred != NULL) - crfree(oldcred); - - /* * Handle deferred decrement of ref counts. */ if (oldtextvp != NULL) @@ -881,10 +893,12 @@ sigacts_free(oldsigacts); exec_fail_dealloc: - /* * free various allocated resources */ + if (euip != NULL) + uifree(euip); + if (imgp->firstpage != NULL) exec_unmap_first_page(imgp); @@ -926,6 +940,8 @@ SDT_PROBE1(proc, , , exec__failure, error); done2: + if (imgp->newcred != NULL) + crfree(oldcred); #ifdef MAC mac_execve_exit(imgp); mac_execve_interpreter_exit(interpvplabel); Index: head/sys/sys/imgact.h =================================================================== --- head/sys/sys/imgact.h +++ head/sys/sys/imgact.h @@ -38,6 +38,8 @@ #define MAXSHELLCMDLEN PAGE_SIZE +struct ucred; + struct image_args { char *buf; /* pointer to string buffer */ char *begin_argv; /* beginning of argv in buf */ @@ -82,6 +84,8 @@ int pagesizeslen; vm_prot_t stack_prot; u_long stack_sz; + struct ucred *newcred; /* new credentials if changing */ + bool credential_setid; /* true if becoming setid */ }; #ifdef _KERNEL