/* * This routine is used to compare two credentials to determine if * they refer to the same "user". If the pointers are equal, then * they must refer to the same user. Otherwise, the contents of * the credentials are compared to see whether they are equivalent. * * This routine returns 0 if the credentials refer to the same user, * 1 if they do not. */ int crcmp(const cred_t *cr1, const cred_t *cr2) { credgrp_t *grp1, *grp2; if (cr1 == cr2) return (0); if (cr1->cr_uid == cr2->cr_uid && cr1->cr_gid == cr2->cr_gid && cr1->cr_ruid == cr2->cr_ruid && cr1->cr_rgid == cr2->cr_rgid && cr1->cr_zone == cr2->cr_zone && ((grp1 = cr1->cr_grps) == (grp2 = cr2->cr_grps) || (grp1 != NULL && grp2 != NULL && grp1->crg_ngroups == grp2->crg_ngroups && bcmp(grp1->crg_groups, grp2->crg_groups, grp1->crg_ngroups * sizeof (gid_t)) == 0))) { return (!priv_isequalset(&CR_OEPRIV(cr1), &CR_OEPRIV(cr2))); } return (1); }
/* * Return the nth privilege set */ const priv_set_t * priv_getset(const cred_t *cr, int set) { ASSERT(PRIV_VALIDSET(set)); if ((CR_FLAGS(cr) & PRIV_AWARE) == 0) switch (set) { case PRIV_EFFECTIVE: return (&CR_OEPRIV(cr)); case PRIV_PERMITTED: return (&CR_OPPRIV(cr)); } return (&CR_PRIVS(cr)->crprivs[set]); }
/* * Register the klpd. * If the pid_t passed in is positive, update the registration for * the specific process; that is only possible if the process already * has a registration on it. This change of registration will affect * all processes which share common ancestry. * * MY_PID (pid 0) can be used to create or change the context for * the current process, typically done after fork(). * * A negative value can be used to register a klpd globally. * * The per-credential klpd needs to be cleaned up when entering * a zone or unsetting the flag. */ int klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) { cred_t *cr = CRED(); door_handle_t dh; klpd_reg_t *kpd; priv_set_t pset; door_info_t di; credklpd_t *ckp = NULL; pid_t pid = -1; projid_t proj = -1; kproject_t *kpp = NULL; if (CR_FLAGS(cr) & PRIV_XPOLICY) return (set_errno(EINVAL)); if (copyin(psetbuf, &pset, sizeof (priv_set_t))) return (set_errno(EFAULT)); if (!priv_issubset(&pset, &CR_OEPRIV(cr))) return (set_errno(EPERM)); switch (type) { case P_PID: pid = (pid_t)id; if (pid == P_MYPID) pid = curproc->p_pid; if (pid == curproc->p_pid) ckp = crklpd_alloc(); break; case P_PROJID: proj = (projid_t)id; kpp = project_hold_by_id(proj, crgetzone(cr), PROJECT_HOLD_FIND); if (kpp == NULL) return (set_errno(ESRCH)); break; default: return (set_errno(ENOTSUP)); } /* * Verify the door passed in; it must be a door and we won't * allow processes to be called on their own behalf. */ dh = door_ki_lookup(did); if (dh == NULL || door_ki_info(dh, &di) != 0) { if (ckp != NULL) crklpd_rele(ckp); if (kpp != NULL) project_rele(kpp); return (set_errno(EBADF)); } if (type == P_PID && pid == di.di_target) { if (ckp != NULL) crklpd_rele(ckp); ASSERT(kpp == NULL); return (set_errno(EINVAL)); } kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); crhold(kpd->klpd_cred = cr); kpd->klpd_door = dh; kpd->klpd_door_pid = di.di_target; kpd->klpd_ref = 1; kpd->klpd_pset = pset; if (kpp != NULL) { mutex_enter(&klpd_mutex); kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); mutex_exit(&klpd_mutex); if (kpd != NULL) klpd_rele(kpd); project_rele(kpp); } else if ((int)pid < 0) { /* Global daemon */ mutex_enter(&klpd_mutex); (void) klpd_link(kpd, &klpd_list, B_FALSE); mutex_exit(&klpd_mutex); } else if (pid == curproc->p_pid) { proc_t *p = curproc; cred_t *newcr = cralloc(); /* No need to lock, sole reference to ckp */ kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); if (kpd != NULL) klpd_rele(kpd); mutex_enter(&p->p_crlock); cr = p->p_cred; crdup_to(cr, newcr); crsetcrklpd(newcr, ckp); p->p_cred = newcr; /* Already held for p_cred */ crhold(newcr); /* Hold once for the current thread */ mutex_exit(&p->p_crlock); crfree(cr); /* One for the p_cred */ crset(p, newcr); } else { proc_t *p; cred_t *pcr; mutex_enter(&pidlock); p = prfind(pid); if (p == NULL || !prochasprocperm(p, curproc, CRED())) { mutex_exit(&pidlock); klpd_rele(kpd); return (set_errno(p == NULL ? ESRCH : EPERM)); } mutex_enter(&p->p_crlock); crhold(pcr = p->p_cred); mutex_exit(&pidlock); mutex_exit(&p->p_crlock); /* * We're going to update the credential's ckp in place; * this requires that it exists. */ ckp = crgetcrklpd(pcr); if (ckp == NULL) { crfree(pcr); klpd_rele(kpd); return (set_errno(EINVAL)); } crklpd_setreg(ckp, kpd); crfree(pcr); } 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); }