int crsetugid(cred_t *cr, uid_t uid, gid_t gid) { zone_t *zone = crgetzone(cr); ASSERT(cr->cr_ref <= 2); if (!VALID_UID(uid, zone) || !VALID_GID(gid, zone)) return (-1); cr->cr_uid = cr->cr_ruid = cr->cr_suid = uid; cr->cr_gid = cr->cr_rgid = cr->cr_sgid = gid; return (0); }
/* * Change ownership of file. */ int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag) { struct vattr vattr; int error; struct zone *zone = crgetzone(CRED()); if (uid != (uid_t)-1 && !VALID_UID(uid, zone) || gid != (gid_t)-1 && !VALID_GID(gid, zone)) { return (set_errno(EINVAL)); } vattr.va_uid = uid; vattr.va_gid = gid; vattr.va_mask = 0; if (vattr.va_uid != -1) vattr.va_mask |= AT_UID; if (vattr.va_gid != -1) vattr.va_mask |= AT_GID; error = fsetattrat(fd, path, flag, &vattr); if (error) return (set_errno(error)); return (0); }
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); }