/* * Interface to set the effective and permitted privileges for * a credential; this interface does no security checks and is * intended for kernel (file)servers creating credentials with * specific privileges. */ int crsetpriv(cred_t *cr, ...) { va_list ap; const char *privnm; ASSERT(cr->cr_ref <= 2); priv_set_PA(cr); va_start(ap, cr); while ((privnm = va_arg(ap, const char *)) != NULL) { int priv = priv_getbyname(privnm, 0); if (priv < 0) return (-1); priv_addset(&CR_PPRIV(cr), priv); priv_addset(&CR_EPRIV(cr), priv); } priv_adjust_PA(cr); va_end(ap); return (0); }
int setpflags(uint_t flag, uint_t val, cred_t *tcr) { cred_t *cr, *pcr; proc_t *p = curproc; uint_t newflags; boolean_t use_curcred = (tcr == NULL); if (val > 1 || (flag != PRIV_DEBUG && flag != PRIV_AWARE && flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT && flag != __PROC_PROTECT && flag != PRIV_XPOLICY && flag != PRIV_AWARE_RESET && flag != PRIV_PFEXEC)) { return (EINVAL); } if (flag == __PROC_PROTECT) { mutex_enter(&p->p_lock); if (val == 0) p->p_flag &= ~SNOCD; else p->p_flag |= SNOCD; mutex_exit(&p->p_lock); return (0); } if (use_curcred) { cr = cralloc(); mutex_enter(&p->p_crlock); pcr = p->p_cred; } else { cr = pcr = tcr; } newflags = CR_FLAGS(pcr); if (val != 0) { if (flag == PRIV_AWARE) newflags &= ~PRIV_AWARE_RESET; newflags |= flag; } else { newflags &= ~flag; } /* No change */ if (CR_FLAGS(pcr) == newflags) { if (use_curcred) { mutex_exit(&p->p_crlock); crfree(cr); } return (0); } /* * Setting either the NET_MAC_AWARE or NET_MAC_AWARE_INHERIT * flags is a restricted operation. * * When invoked via the PRIVSYS_SETPFLAGS syscall * we require that the current cred has the net_mac_aware * privilege in its effective set. * * When called from within the kernel by label-aware * services such as NFS, we don't require a privilege check. * */ if ((flag == NET_MAC_AWARE || flag == NET_MAC_AWARE_INHERIT) && (val == 1) && use_curcred) { if (secpolicy_net_mac_aware(pcr) != 0) { mutex_exit(&p->p_crlock); crfree(cr); return (EPERM); } } /* Trying to unset PA; if we can't, return an error */ if (flag == PRIV_AWARE && val == 0 && !priv_can_clear_PA(pcr)) { if (use_curcred) { mutex_exit(&p->p_crlock); crfree(cr); } return (EPERM); } /* Committed to changing the flag */ if (use_curcred) crcopy_to(pcr, cr); if (flag == PRIV_AWARE) { if (val != 0) priv_set_PA(cr); else priv_adjust_PA(cr); } else { CR_FLAGS(cr) = newflags; } /* * Unsetting the flag has as side effect getting rid of * the per-credential policy. */ if (flag == PRIV_XPOLICY && val == 0) crsetcrklpd(cr, NULL); if (use_curcred) { p->p_cred = cr; mutex_exit(&p->p_crlock); crset(p, cr); } 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); }