/* * Interface to effectively set the PRIV_ALL for * a credential; this interface does no security checks and is * intended for kernel (file)servers to extend the user credentials * to be ALL, like either kcred or zcred. */ void crset_zone_privall(cred_t *cr) { zone_t *zone = crgetzone(cr); priv_fillset(&CR_LPRIV(cr)); CR_EPRIV(cr) = CR_PPRIV(cr) = CR_IPRIV(cr) = CR_LPRIV(cr); priv_intersect(zone->zone_privset, &CR_LPRIV(cr)); priv_intersect(zone->zone_privset, &CR_EPRIV(cr)); priv_intersect(zone->zone_privset, &CR_IPRIV(cr)); priv_intersect(zone->zone_privset, &CR_PPRIV(cr)); }
int klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) { klpd_reg_t *p; int rv = -1; credklpd_t *ckp; zone_t *ckzone; /* * These locks must not be held when this code is called; * callbacks to userland with these locks held will result * in issues. That said, the code at the call sides was * restructured not to call with any of the locks held and * no policies operate by default on most processes. */ if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || mutex_owned(&curproc->p_crlock)) { atomic_inc_32(&klpd_bad_locks); return (-1); } /* * Enforce the limit set for the call process (still). */ if (!priv_issubset(req, &CR_LPRIV(cr))) return (-1); /* Try 1: get the credential specific klpd */ if ((ckp = crgetcrklpd(cr)) != NULL) { mutex_enter(&ckp->crkl_lock); if ((p = ckp->crkl_reg) != NULL && p->klpd_indel == 0 && priv_issubset(req, &p->klpd_pset)) { klpd_hold(p); mutex_exit(&ckp->crkl_lock); rv = klpd_do_call(p, req, ap); mutex_enter(&ckp->crkl_lock); klpd_rele(p); mutex_exit(&ckp->crkl_lock); if (rv != -1) return (rv == 0 ? 0 : -1); } else { mutex_exit(&ckp->crkl_lock); } } /* Try 2: get the project specific klpd */ mutex_enter(&klpd_mutex); if ((p = curproj->kpj_klpd) != NULL) { klpd_hold(p); mutex_exit(&klpd_mutex); if (p->klpd_indel == 0 && priv_issubset(req, &p->klpd_pset)) { rv = klpd_do_call(p, req, ap); } mutex_enter(&klpd_mutex); klpd_rele(p); mutex_exit(&klpd_mutex); if (rv != -1) return (rv == 0 ? 0 : -1); } else { mutex_exit(&klpd_mutex); } /* Try 3: get the global klpd list */ ckzone = crgetzone(cr); mutex_enter(&klpd_mutex); for (p = klpd_list; p != NULL; ) { zone_t *kkzone = crgetzone(p->klpd_cred); if ((kkzone == &zone0 || kkzone == ckzone) && p->klpd_indel == 0 && priv_issubset(req, &p->klpd_pset)) { klpd_hold(p); mutex_exit(&klpd_mutex); rv = klpd_do_call(p, req, ap); mutex_enter(&klpd_mutex); p = klpd_rele_next(p); if (rv != -1) break; } else { p = p->klpd_next; } } mutex_exit(&klpd_mutex); return (rv == 0 ? 0 : -1); }
/* * setppriv (priv_op_t, priv_ptype_t, priv_set_t) */ static int setppriv(priv_op_t op, priv_ptype_t type, priv_set_t *in_pset) { priv_set_t pset, *target; cred_t *cr, *pcr; proc_t *p; boolean_t donocd = B_FALSE; if (!PRIV_VALIDSET(type) || !PRIV_VALIDOP(op)) return (set_errno(EINVAL)); if (copyin(in_pset, &pset, sizeof (priv_set_t))) return (set_errno(EFAULT)); p = ttoproc(curthread); cr = cralloc(); mutex_enter(&p->p_crlock); retry: pcr = p->p_cred; if (AU_AUDITING()) audit_setppriv(op, type, &pset, pcr); /* * Filter out unallowed request (bad op and bad type) */ switch (op) { case PRIV_ON: case PRIV_SET: /* * Turning on privileges; the limit set cannot grow, * other sets can but only as long as they remain subsets * of P. Only immediately after exec holds that P <= L. */ if (type == PRIV_LIMIT && !priv_issubset(&pset, &CR_LPRIV(pcr))) { mutex_exit(&p->p_crlock); crfree(cr); return (set_errno(EPERM)); } if (!priv_issubset(&pset, &CR_OPPRIV(pcr)) && !priv_issubset(&pset, priv_getset(pcr, type))) { mutex_exit(&p->p_crlock); /* Policy override should not grow beyond L either */ if (type != PRIV_INHERITABLE || !priv_issubset(&pset, &CR_LPRIV(pcr)) || secpolicy_require_privs(CRED(), &pset) != 0) { crfree(cr); return (set_errno(EPERM)); } mutex_enter(&p->p_crlock); if (pcr != p->p_cred) goto retry; donocd = B_TRUE; } break; case PRIV_OFF: /* PRIV_OFF is always allowed */ break; } /* * OK! everything is cool. * Do cred COW. */ crcopy_to(pcr, cr); /* * If we change the effective, permitted or limit set, we attain * "privilege awareness". */ if (type != PRIV_INHERITABLE) priv_set_PA(cr); target = &(CR_PRIVS(cr)->crprivs[type]); switch (op) { case PRIV_ON: priv_union(&pset, target); break; case PRIV_OFF: priv_inverse(&pset); priv_intersect(target, &pset); /* * Fall-thru to set target and change other process * privilege sets. */ /*FALLTHRU*/ case PRIV_SET: *target = pset; /* * Take privileges no longer permitted out * of other effective sets as well. * Limit set is enforced at exec() time. */ if (type == PRIV_PERMITTED) priv_intersect(&pset, &CR_EPRIV(cr)); break; } /* * When we give up privileges not in the inheritable set, * set SNOCD if not already set; first we compute the * privileges removed from P using Diff = (~P') & P * and then we check whether the removed privileges are * a subset of I. If we retain uid 0, all privileges * are required anyway so don't set SNOCD. */ if (type == PRIV_PERMITTED && (p->p_flag & SNOCD) == 0 && cr->cr_uid != 0 && cr->cr_ruid != 0 && cr->cr_suid != 0) { priv_set_t diff = CR_OPPRIV(cr); priv_inverse(&diff); priv_intersect(&CR_OPPRIV(pcr), &diff); donocd = !priv_issubset(&diff, &CR_IPRIV(cr)); } p->p_cred = cr; mutex_exit(&p->p_crlock); if (donocd) { mutex_enter(&p->p_lock); p->p_flag |= SNOCD; mutex_exit(&p->p_lock); } /* * The basic_test privilege should not be removed from E; * if that has happened, then some programmer typically set the E/P to * empty. That is not portable. */ if ((type == PRIV_EFFECTIVE || type == PRIV_PERMITTED) && priv_basic_test >= 0 && !PRIV_ISASSERT(target, priv_basic_test)) { proc_t *p = curproc; pid_t pid = p->p_pid; char *fn = PTOU(p)->u_comm; cmn_err(CE_WARN, "%s[%d]: setppriv: basic_test privilege " "removed from E/P", fn, pid); } crset(p, cr); /* broadcast to process threads */ return (0); }
/* * Guts of pr_spriv: * * Set the privileges of a process. * * In order to set the privileges, the setting process will need to * have those privileges in its effective set in order to prevent * specially privileged processes to easily gain additional privileges. * Pre-existing privileges can be retained. To change any privileges, * PRIV_PROC_OWNER needs to be asserted. * * In formula: * * S' <= S || S' <= S + Ea * * the new set must either be subset of the old set or a subset of * the oldset merged with the effective set of the acting process; or just: * * S' <= S + Ea * * It's not legal to grow the limit set this way. * */ int priv_pr_spriv(proc_t *p, prpriv_t *prpriv, const cred_t *cr) { cred_t *oldcred; cred_t *newcred; int i; int err = EPERM; cred_priv_t *cp, *ocp; priv_set_t eset; ASSERT(MUTEX_HELD(&p->p_lock)); /* * Set must have proper dimension; infosize must be absent * or properly sized. */ if (prpriv->pr_nsets != PRIV_NSET || prpriv->pr_setsize != PRIV_SETSIZE || (prpriv->pr_infosize & (sizeof (uint32_t) - 1)) != 0 || prpriv->pr_infosize > priv_info->priv_infosize || prpriv->pr_infosize < 0) return (EINVAL); mutex_exit(&p->p_lock); if (priv_proc_cred_perm(cr, p, &oldcred, VWRITE) != 0) { mutex_enter(&p->p_lock); return (EPERM); } newcred = crdup(oldcred); /* Copy the privilege sets from prpriv to newcred */ bcopy(prpriv->pr_sets, CR_PRIVSETS(newcred), PRIV_SETBYTES); cp = &newcred->cr_priv; ocp = &oldcred->cr_priv; eset = CR_OEPRIV(cr); priv_intersect(&CR_LPRIV(oldcred), &eset); /* * Verify the constraints laid out: * for the limit set, we require that the new set is a subset * of the old limit set. * for all other sets, we require that the new set is either a * subset of the old set or a subset of the intersection of * the old limit set and the effective set of the acting process. */ for (i = 0; i < PRIV_NSET; i++) if (!priv_issubset(&cp->crprivs[i], &ocp->crprivs[i]) && (i == PRIV_LIMIT || !priv_issubset(&cp->crprivs[i], &eset))) break; crfree(oldcred); if (i < PRIV_NSET || !priv_valid(newcred)) goto err; /* Load the settable privilege information */ if (prpriv->pr_infosize > 0) { char *x = (char *)prpriv + PRIV_PRPRIV_INFO_OFFSET(prpriv); char *lastx = x + prpriv->pr_infosize; while (x < lastx) { priv_info_t *pi = (priv_info_t *)x; priv_info_uint_t *pii; switch (pi->priv_info_type) { case PRIV_INFO_FLAGS: pii = (priv_info_uint_t *)x; if (pii->info.priv_info_size != sizeof (*pii)) { err = EINVAL; goto err; } CR_FLAGS(newcred) &= ~PRIV_USER; CR_FLAGS(newcred) |= (pii->val & PRIV_USER); break; default: err = EINVAL; goto err; } /* Guarantee alignment and forward progress */ if ((pi->priv_info_size & (sizeof (uint32_t) - 1)) || pi->priv_info_size < sizeof (*pi) || lastx - x > pi->priv_info_size) { err = EINVAL; goto err; } x += pi->priv_info_size; } } /* * We'll try to copy the privilege aware flag; but since the * privileges sets are all individually set, they are set * as if we're privilege aware. If PRIV_AWARE wasn't set * or was explicitely unset, we need to set the flag and then * try to get rid of it. */ if ((CR_FLAGS(newcred) & PRIV_AWARE) == 0) { CR_FLAGS(newcred) |= PRIV_AWARE; priv_adjust_PA(newcred); } mutex_enter(&p->p_crlock); oldcred = p->p_cred; p->p_cred = newcred; mutex_exit(&p->p_crlock); crfree(oldcred); mutex_enter(&p->p_lock); return (0); err: crfree(newcred); mutex_enter(&p->p_lock); return (err); }
void cred_init(void) { priv_init(); crsize = sizeof (cred_t); if (get_c2audit_load() > 0) { #ifdef _LP64 /* assure audit context is 64-bit aligned */ audoff = (crsize + sizeof (int64_t) - 1) & ~(sizeof (int64_t) - 1); #else /* _LP64 */ audoff = crsize; #endif /* _LP64 */ crsize = audoff + sizeof (auditinfo_addr_t); crsize = (crsize + sizeof (int) - 1) & ~(sizeof (int) - 1); } cred_cache = kmem_cache_create("cred_cache", crsize, 0, NULL, NULL, NULL, NULL, NULL, 0); /* * dummycr is used to copy initial state for creds. */ dummycr = cralloc(); bzero(dummycr, crsize); dummycr->cr_ref = 1; dummycr->cr_uid = (uid_t)-1; dummycr->cr_gid = (gid_t)-1; dummycr->cr_ruid = (uid_t)-1; dummycr->cr_rgid = (gid_t)-1; dummycr->cr_suid = (uid_t)-1; dummycr->cr_sgid = (gid_t)-1; /* * kcred is used by anything that needs all privileges; it's * also the template used for crget as it has all the compatible * sets filled in. */ kcred = cralloc(); bzero(kcred, crsize); kcred->cr_ref = 1; /* kcred is never freed, so we don't need zone_cred_hold here */ kcred->cr_zone = &zone0; priv_fillset(&CR_LPRIV(kcred)); CR_IPRIV(kcred) = *priv_basic; /* Not a basic privilege, if chown is not restricted add it to I0 */ if (!rstchown) priv_addset(&CR_IPRIV(kcred), PRIV_FILE_CHOWN_SELF); /* Basic privilege, if link is restricted remove it from I0 */ if (rstlink) priv_delset(&CR_IPRIV(kcred), PRIV_FILE_LINK_ANY); CR_EPRIV(kcred) = CR_PPRIV(kcred) = CR_IPRIV(kcred); CR_FLAGS(kcred) = NET_MAC_AWARE; /* * Set up credentials of p0. */ ttoproc(curthread)->p_cred = kcred; curthread->t_cred = kcred; ucredsize = UCRED_SIZE; mutex_init(&ephemeral_zone_mutex, NULL, MUTEX_DEFAULT, NULL); zone_key_create(&ephemeral_zone_key, NULL, NULL, destroy_ephemeral_zsd); }