/* * This function is based on setreuid in common/syscall/uid.c and exists * because Solaris does not have a way to explicitly set the saved uid (suid) * from any other system call. */ long lx_setresuid(l_uid_t ruid, l_uid_t euid, l_uid_t suid) { proc_t *p; int error = 0; int do_nocd = 0; int uidchge = 0; uid_t oldruid = ruid; cred_t *cr, *newcr; zoneid_t zoneid = getzoneid(); if ((ruid != -1 && (ruid > MAXUID)) || (euid != -1 && (euid > MAXUID)) || (suid != -1 && (suid > MAXUID))) { error = EINVAL; goto done; } /* * Need to pre-allocate the new cred structure before grabbing * the p_crlock mutex. */ newcr = cralloc(); p = ttoproc(curthread); retry: mutex_enter(&p->p_crlock); cr = p->p_cred; if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid && ruid != cr->cr_suid && secpolicy_allow_setid(cr, ruid, B_FALSE)) { error = EPERM; } else if (euid != -1 && euid != cr->cr_ruid && euid != cr->cr_uid && euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) { error = EPERM; } else if (suid != -1 && suid != cr->cr_ruid && suid != cr->cr_uid && suid != cr->cr_suid && secpolicy_allow_setid(cr, suid, B_FALSE)) { error = EPERM; } else { if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) { /* * The ruid of the process is going to change. In order * to avoid a race condition involving the * process count associated with the newly given ruid, * we increment the count before assigning the * credential to the process. * To do that, we'll have to take pidlock, so we first * release p_crlock. */ mutex_exit(&p->p_crlock); uidchge = 1; mutex_enter(&pidlock); upcount_inc(ruid, zoneid); mutex_exit(&pidlock); /* * As we released p_crlock we can't rely on the cr * we read. So retry the whole thing. */ goto retry; } crhold(cr); crcopy_to(cr, newcr); p->p_cred = newcr; if (euid != -1) newcr->cr_uid = euid; if (suid != -1) newcr->cr_suid = suid; if (ruid != -1) { oldruid = newcr->cr_ruid; newcr->cr_ruid = ruid; ASSERT(ruid != oldruid ? uidchge : 1); } /* * A process that gives up its privilege * must be marked to produce no core dump. */ if ((cr->cr_uid != newcr->cr_uid || cr->cr_ruid != newcr->cr_ruid || cr->cr_suid != newcr->cr_suid)) do_nocd = 1; crfree(cr); } mutex_exit(&p->p_crlock); /* * We decrement the number of processes associated with the oldruid * to match the increment above, even if the ruid of the process * did not change or an error occurred (oldruid == uid). */ if (uidchge) { ASSERT(oldruid != -1 && ruid != -1); mutex_enter(&pidlock); upcount_dec(oldruid, zoneid); mutex_exit(&pidlock); } if (error == 0) { if (do_nocd) { mutex_enter(&p->p_lock); p->p_flag |= SNOCD; mutex_exit(&p->p_lock); } crset(p, newcr); /* broadcast to process threads */ goto done; } crfree(newcr); done: if (error) return (set_errno(error)); else return (0); }
int setuid(uid_t uid) { proc_t *p; int error; int do_nocd = 0; int uidchge = 0; cred_t *cr, *newcr; uid_t oldruid = uid; zoneid_t zoneid = getzoneid(); ksid_t ksid, *ksp; zone_t *zone = crgetzone(CRED()); if (!VALID_UID(uid, zone)) return (set_errno(EINVAL)); if (uid > MAXUID) { if (ksid_lookupbyuid(zone, uid, &ksid) != 0) return (set_errno(EINVAL)); ksp = &ksid; } else { ksp = NULL; } /* * Need to pre-allocate the new cred structure before grabbing * the p_crlock mutex. We can't hold on to the p_crlock for most * if this though, now that we allow kernel upcalls from the * policy routines. */ newcr = cralloc_ksid(); p = ttoproc(curthread); retry: mutex_enter(&p->p_crlock); retry_locked: cr = p->p_cred; crhold(cr); mutex_exit(&p->p_crlock); if ((uid == cr->cr_ruid || uid == cr->cr_suid) && secpolicy_allow_setid(cr, uid, B_TRUE) != 0) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry_locked; error = 0; crcopy_to(cr, newcr); p->p_cred = newcr; newcr->cr_uid = uid; crsetsid(newcr, ksp, KSID_USER); mutex_exit(&p->p_crlock); } else if ((error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry_locked; if (!uidchge && uid != cr->cr_ruid) { /* * The ruid of the process is going to change. In order * to avoid a race condition involving the * process-count associated with the newly given ruid, * we increment the count before assigning the * credential to the process. * To do that, we'll have to take pidlock, so we first * release p_crlock. */ mutex_exit(&p->p_crlock); uidchge = 1; mutex_enter(&pidlock); upcount_inc(uid, zoneid); mutex_exit(&pidlock); /* * As we released p_crlock we can't rely on the cr * we read. So retry the whole thing. */ goto retry; } /* * A privileged process that gives up its privilege * must be marked to produce no core dump. */ if (cr->cr_uid != uid || cr->cr_ruid != uid || cr->cr_suid != uid) do_nocd = 1; oldruid = cr->cr_ruid; crcopy_to(cr, newcr); p->p_cred = newcr; newcr->cr_ruid = uid; newcr->cr_suid = uid; newcr->cr_uid = uid; crsetsid(newcr, ksp, KSID_USER); priv_reset_PA(newcr, B_TRUE); ASSERT(uid != oldruid ? uidchge : 1); mutex_exit(&p->p_crlock); } else { crfree(newcr); crfree(cr); if (ksp != NULL) ksid_rele(ksp); } /* * We decrement the number of processes associated with the oldruid * to match the increment above, even if the ruid of the process * did not change or an error occurred (oldruid == uid). */ if (uidchge) { mutex_enter(&pidlock); upcount_dec(oldruid, zoneid); mutex_exit(&pidlock); } if (error == 0) { if (do_nocd) { mutex_enter(&p->p_lock); p->p_flag |= SNOCD; mutex_exit(&p->p_lock); } crset(p, newcr); /* broadcast to process threads */ return (0); } return (set_errno(error)); }
/* * Remove zombie children from the process table. */ void freeproc(proc_t *p) { proc_t *q; task_t *tk; ASSERT(p->p_stat == SZOMB); ASSERT(p->p_tlist == NULL); ASSERT(MUTEX_HELD(&pidlock)); sigdelq(p, NULL, 0); if (p->p_killsqp) { siginfofree(p->p_killsqp); p->p_killsqp = NULL; } prfree(p); /* inform /proc */ /* * Don't free the init processes. * Other dying processes will access it. */ if (p == proc_init) return; /* * We wait until now to free the cred structure because a * zombie process's credentials may be examined by /proc. * No cred locking needed because there are no threads at this point. */ upcount_dec(crgetruid(p->p_cred), crgetzoneid(p->p_cred)); crfree(p->p_cred); if (p->p_corefile != NULL) { corectl_path_rele(p->p_corefile); p->p_corefile = NULL; } if (p->p_content != NULL) { corectl_content_rele(p->p_content); p->p_content = NULL; } if (p->p_nextofkin && !((p->p_nextofkin->p_flag & SNOWAIT) || (PTOU(p->p_nextofkin)->u_signal[SIGCLD - 1] == SIG_IGN))) { /* * This should still do the right thing since p_utime/stime * get set to the correct value on process exit, so it * should get properly updated */ p->p_nextofkin->p_cutime += p->p_utime; p->p_nextofkin->p_cstime += p->p_stime; p->p_nextofkin->p_cacct[LMS_USER] += p->p_acct[LMS_USER]; p->p_nextofkin->p_cacct[LMS_SYSTEM] += p->p_acct[LMS_SYSTEM]; p->p_nextofkin->p_cacct[LMS_TRAP] += p->p_acct[LMS_TRAP]; p->p_nextofkin->p_cacct[LMS_TFAULT] += p->p_acct[LMS_TFAULT]; p->p_nextofkin->p_cacct[LMS_DFAULT] += p->p_acct[LMS_DFAULT]; p->p_nextofkin->p_cacct[LMS_KFAULT] += p->p_acct[LMS_KFAULT]; p->p_nextofkin->p_cacct[LMS_USER_LOCK] += p->p_acct[LMS_USER_LOCK]; p->p_nextofkin->p_cacct[LMS_SLEEP] += p->p_acct[LMS_SLEEP]; p->p_nextofkin->p_cacct[LMS_WAIT_CPU] += p->p_acct[LMS_WAIT_CPU]; p->p_nextofkin->p_cacct[LMS_STOPPED] += p->p_acct[LMS_STOPPED]; p->p_nextofkin->p_cru.minflt += p->p_ru.minflt; p->p_nextofkin->p_cru.majflt += p->p_ru.majflt; p->p_nextofkin->p_cru.nswap += p->p_ru.nswap; p->p_nextofkin->p_cru.inblock += p->p_ru.inblock; p->p_nextofkin->p_cru.oublock += p->p_ru.oublock; p->p_nextofkin->p_cru.msgsnd += p->p_ru.msgsnd; p->p_nextofkin->p_cru.msgrcv += p->p_ru.msgrcv; p->p_nextofkin->p_cru.nsignals += p->p_ru.nsignals; p->p_nextofkin->p_cru.nvcsw += p->p_ru.nvcsw; p->p_nextofkin->p_cru.nivcsw += p->p_ru.nivcsw; p->p_nextofkin->p_cru.sysc += p->p_ru.sysc; p->p_nextofkin->p_cru.ioch += p->p_ru.ioch; } q = p->p_nextofkin; if (q && q->p_orphan == p) q->p_orphan = p->p_nextorph; else if (q) { for (q = q->p_orphan; q; q = q->p_nextorph) if (q->p_nextorph == p) break; ASSERT(q && q->p_nextorph == p); q->p_nextorph = p->p_nextorph; } /* * The process table slot is being freed, so it is now safe to give up * task and project membership. */ mutex_enter(&p->p_lock); tk = p->p_task; task_detach(p); mutex_exit(&p->p_lock); proc_detach(p); pid_exit(p, tk); /* frees pid and proc structure */ task_rele(tk); }
/* * Buy-back from SunOS 4.x * * Like setuid() and seteuid() combined -except- that non-root users * can change cr_ruid to cr_uid, and the semantics of cr_suid are * subtly different. */ int setreuid(uid_t ruid, uid_t euid) { proc_t *p; int error = 0; int do_nocd = 0; int uidchge = 0; uid_t oldruid = ruid; cred_t *cr, *newcr; zoneid_t zoneid = getzoneid(); ksid_t ksid, *ksp; zone_t *zone = crgetzone(CRED()); if ((ruid != -1 && !VALID_UID(ruid, zone)) || (euid != -1 && !VALID_UID(euid, zone))) return (set_errno(EINVAL)); if (euid != -1 && euid > MAXUID) { if (ksid_lookupbyuid(zone, euid, &ksid) != 0) return (set_errno(EINVAL)); ksp = &ksid; } else { ksp = NULL; } /* * Need to pre-allocate the new cred structure before grabbing * the p_crlock mutex. */ newcr = cralloc_ksid(); p = ttoproc(curthread); retry: mutex_enter(&p->p_crlock); retry_locked: crhold(cr = p->p_cred); mutex_exit(&p->p_crlock); if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid && secpolicy_allow_setid(cr, ruid, B_FALSE) != 0) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry_locked; error = EPERM; } else if (euid != -1 && euid != cr->cr_ruid && euid != cr->cr_uid && euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry_locked; error = EPERM; } else { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry_locked; if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) { /* * The ruid of the process is going to change. In order * to avoid a race condition involving the * process-count associated with the newly given ruid, * we increment the count before assigning the * credential to the process. * To do that, we'll have to take pidlock, so we first * release p_crlock. */ mutex_exit(&p->p_crlock); uidchge = 1; mutex_enter(&pidlock); upcount_inc(ruid, zoneid); mutex_exit(&pidlock); /* * As we released p_crlock we can't rely on the cr * we read. So retry the whole thing. */ goto retry; } crhold(cr); crcopy_to(cr, newcr); p->p_cred = newcr; if (euid != -1) { newcr->cr_uid = euid; crsetsid(newcr, ksp, KSID_USER); } if (ruid != -1) { oldruid = newcr->cr_ruid; newcr->cr_ruid = ruid; ASSERT(ruid != oldruid ? uidchge : 1); } /* * "If the real uid is being changed, or the effective uid is * being changed to a value not equal to the real uid, the * saved uid is set to the new effective uid." */ if (ruid != -1 || (euid != -1 && newcr->cr_uid != newcr->cr_ruid)) newcr->cr_suid = newcr->cr_uid; /* * A process that gives up its privilege * must be marked to produce no core dump. */ if ((cr->cr_uid != newcr->cr_uid || cr->cr_ruid != newcr->cr_ruid || cr->cr_suid != newcr->cr_suid)) do_nocd = 1; priv_reset_PA(newcr, ruid != -1 && euid != -1 && ruid == euid); crfree(cr); } mutex_exit(&p->p_crlock); /* * We decrement the number of processes associated with the oldruid * to match the increment above, even if the ruid of the process * did not change or an error occurred (oldruid == uid). */ if (uidchge) { ASSERT(oldruid != -1 && ruid != -1); mutex_enter(&pidlock); upcount_dec(oldruid, zoneid); mutex_exit(&pidlock); } if (error == 0) { if (do_nocd) { mutex_enter(&p->p_lock); p->p_flag |= SNOCD; mutex_exit(&p->p_lock); } crset(p, newcr); /* broadcast to process threads */ return (0); } crfree(newcr); if (ksp != NULL) ksid_rele(ksp); return (set_errno(error)); }