static ephemeral_zsd_t * get_ephemeral_zsd(zone_t *zone) { ephemeral_zsd_t *eph_zsd; eph_zsd = zone_getspecific(ephemeral_zone_key, zone); if (eph_zsd != NULL) { return (eph_zsd); } mutex_enter(&ephemeral_zone_mutex); eph_zsd = zone_getspecific(ephemeral_zone_key, zone); if (eph_zsd == NULL) { eph_zsd = kmem_zalloc(sizeof (ephemeral_zsd_t), KM_SLEEP); eph_zsd->min_uid = MAXUID; eph_zsd->last_uid = IDMAP_WK__MAX_UID; eph_zsd->min_gid = MAXUID; eph_zsd->last_gid = IDMAP_WK__MAX_GID; mutex_init(&eph_zsd->eph_lock, NULL, MUTEX_DEFAULT, NULL); /* * nobody is used to map SID containing CRs. */ eph_zsd->eph_nobody = crdup(zone->zone_kcred); (void) crsetugid(eph_zsd->eph_nobody, UID_NOBODY, GID_NOBODY); CR_FLAGS(eph_zsd->eph_nobody) = 0; eph_zsd->eph_nobody->cr_zone = zone; (void) zone_setspecific(ephemeral_zone_key, zone, eph_zsd); } mutex_exit(&ephemeral_zone_mutex); return (eph_zsd); }
/*ARGSUSED*/ void priv_getinfo(const cred_t *cr, void *buf) { struct priv_info_uint *ii; ii = buf; ii->val = CR_FLAGS(cr); ii->info.priv_info_size = (uint32_t)sizeof (*ii); ii->info.priv_info_type = PRIV_INFO_FLAGS; }
/* * Getpflags. Currently only implements single bit flags. */ uint_t getpflags(uint_t flag, const cred_t *cr) { if (flag != PRIV_DEBUG && flag != PRIV_AWARE && flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT && flag != PRIV_XPOLICY && flag != PRIV_PFEXEC && flag != PRIV_AWARE_RESET) return ((uint_t)-1); return ((CR_FLAGS(cr) & flag) != 0); }
/* * 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); }
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); }
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); }