int setgid(gid_t gid) { proc_t *p; int error; int do_nocd = 0; cred_t *cr, *newcr; ksid_t ksid, *ksp; zone_t *zone = crgetzone(CRED()); if (!VALID_GID(gid, zone)) return (set_errno(EINVAL)); if (gid > MAXUID) { if (ksid_lookupbygid(zone, gid, &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 cannot hold the mutex across the * secpolicy functions. */ newcr = cralloc_ksid(); p = ttoproc(curthread); mutex_enter(&p->p_crlock); retry: cr = p->p_cred; crhold(cr); mutex_exit(&p->p_crlock); if ((gid == cr->cr_rgid || gid == cr->cr_sgid) && secpolicy_allow_setid(cr, -1, B_TRUE) != 0) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry; error = 0; crcopy_to(cr, newcr); p->p_cred = newcr; newcr->cr_gid = gid; crsetsid(newcr, ksp, KSID_GROUP); mutex_exit(&p->p_crlock); } else if ((error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) { mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry; /* * A privileged process that makes itself look like a * set-gid process must be marked to produce no core dump. */ if (cr->cr_gid != gid || cr->cr_rgid != gid || cr->cr_sgid != gid) do_nocd = 1; crcopy_to(cr, newcr); p->p_cred = newcr; newcr->cr_gid = gid; newcr->cr_rgid = gid; newcr->cr_sgid = gid; crsetsid(newcr, ksp, KSID_GROUP); mutex_exit(&p->p_crlock); } else { crfree(newcr); crfree(cr); if (ksp != NULL) ksid_rele(ksp); } 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)); }
/* * Buy-back from SunOS 4.x * * Like setgid() and setegid() combined -except- that non-root users * can change cr_rgid to cr_gid, and the semantics of cr_sgid are * subtly different. */ int setregid(gid_t rgid, gid_t egid) { proc_t *p; int error = EPERM; int do_nocd = 0; cred_t *cr, *newcr; ksid_t ksid, *ksp; zone_t *zone = crgetzone(CRED()); if ((rgid != -1 && !VALID_GID(rgid, zone)) || (egid != -1 && !VALID_GID(egid, zone))) return (set_errno(EINVAL)); if (egid != -1 && egid > MAXUID) { if (ksid_lookupbygid(zone, egid, &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); mutex_enter(&p->p_crlock); cr = p->p_cred; if ((rgid == -1 || rgid == cr->cr_rgid || rgid == cr->cr_gid || rgid == cr->cr_sgid) && (egid == -1 || egid == cr->cr_rgid || egid == cr->cr_gid || egid == cr->cr_sgid) || (error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) { crhold(cr); crcopy_to(cr, newcr); p->p_cred = newcr; if (egid != -1) { newcr->cr_gid = egid; crsetsid(newcr, ksp, KSID_GROUP); } if (rgid != -1) newcr->cr_rgid = rgid; /* * "If the real gid is being changed, or the effective gid is * being changed to a value not equal to the real gid, the * saved gid is set to the new effective gid." */ if (rgid != -1 || (egid != -1 && newcr->cr_gid != newcr->cr_rgid)) newcr->cr_sgid = newcr->cr_gid; /* * A privileged process that makes itself look like a * set-gid process must be marked to produce no core dump. */ if ((cr->cr_gid != newcr->cr_gid || cr->cr_rgid != newcr->cr_rgid || cr->cr_sgid != newcr->cr_sgid) && error == 0) do_nocd = 1; error = 0; crfree(cr); } mutex_exit(&p->p_crlock); 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)); }
int setgroups(int gidsetsize, gid_t *gidset) { proc_t *p; cred_t *cr, *newcr; int i; int n = gidsetsize; int error; int scnt = 0; ksidlist_t *ksl = NULL; zone_t *zone; struct credgrp *grps = NULL; /* Perform the cheapest tests before grabbing p_crlock */ if (n > ngroups_max || n < 0) return (set_errno(EINVAL)); zone = crgetzone(CRED()); if (n != 0) { const gid_t *groups; grps = crgrpcopyin(n, gidset); if (grps == NULL) return (set_errno(EFAULT)); groups = crgetggroups(grps); for (i = 0; i < n; i++) { if (!VALID_GID(groups[i], zone)) { crgrprele(grps); return (set_errno(EINVAL)); } if (groups[i] > MAXUID) scnt++; } if (scnt > 0) { ksl = kcrsid_gidstosids(zone, n, (gid_t *)groups); if (ksl == NULL) { crgrprele(grps); return (set_errno(EINVAL)); } } } /* * Need to pre-allocate the new cred structure before acquiring * the p_crlock mutex. */ newcr = cralloc_ksid(); p = ttoproc(curthread); mutex_enter(&p->p_crlock); retry: cr = p->p_cred; crhold(cr); mutex_exit(&p->p_crlock); if ((error = secpolicy_allow_setid(cr, -1, B_FALSE)) != 0) { if (grps != NULL) crgrprele(grps); if (ksl != NULL) ksidlist_rele(ksl); crfree(newcr); crfree(cr); return (set_errno(error)); } mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry; crdup_to(cr, newcr); crsetsidlist(newcr, ksl); crsetcredgrp(newcr, grps); p->p_cred = newcr; crhold(newcr); /* hold for the current thread */ crfree(cr); /* free the old one */ mutex_exit(&p->p_crlock); /* * Broadcast new cred to process threads (including the current one). */ crset(p, newcr); 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)); }
/* * 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)); }
int seteuid(uid_t uid) { proc_t *p; int error = EPERM; int do_nocd = 0; cred_t *cr, *newcr; 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. */ newcr = cralloc_ksid(); p = ttoproc(curthread); mutex_enter(&p->p_crlock); retry: crhold(cr = p->p_cred); mutex_exit(&p->p_crlock); if (uid == cr->cr_ruid || uid == cr->cr_uid || uid == cr->cr_suid || (error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) { /* * A privileged process that makes itself look like a * set-uid process must be marked to produce no core dump, * if the effective uid did changed. */ mutex_enter(&p->p_crlock); crfree(cr); if (cr != p->p_cred) goto retry; if (cr->cr_uid != uid && error == 0) do_nocd = 1; error = 0; crcopy_to(cr, newcr); p->p_cred = newcr; newcr->cr_uid = uid; crsetsid(newcr, ksp, KSID_USER); priv_reset_PA(newcr, B_FALSE); mutex_exit(&p->p_crlock); 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); crfree(cr); if (ksp != NULL) ksid_rele(ksp); return (set_errno(error)); }