16134Scasper /* 26134Scasper * CDDL HEADER START 36134Scasper * 46134Scasper * The contents of this file are subject to the terms of the 56134Scasper * Common Development and Distribution License (the "License"). 66134Scasper * You may not use this file except in compliance with the License. 76134Scasper * 86134Scasper * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96134Scasper * or http://www.opensolaris.org/os/licensing. 106134Scasper * See the License for the specific language governing permissions 116134Scasper * and limitations under the License. 126134Scasper * 136134Scasper * When distributing Covered Code, include this CDDL HEADER in each 146134Scasper * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156134Scasper * If applicable, add the following below this CDDL HEADER, with the 166134Scasper * fields enclosed by brackets "[]" replaced with your own identifying 176134Scasper * information: Portions Copyright [yyyy] [name of copyright owner] 186134Scasper * 196134Scasper * CDDL HEADER END 206134Scasper */ 216134Scasper 226134Scasper /* 236134Scasper * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 246134Scasper * Use is subject to license terms. 256134Scasper */ 266134Scasper 276134Scasper #pragma ident "%Z%%M% %I% %E% SMI" 286134Scasper 296134Scasper #include <sys/atomic.h> 306134Scasper #include <sys/door.h> 316134Scasper #include <sys/proc.h> 326134Scasper #include <sys/cred_impl.h> 336134Scasper #include <sys/policy.h> 346134Scasper #include <sys/priv.h> 356134Scasper #include <sys/klpd.h> 366134Scasper #include <sys/errno.h> 376134Scasper #include <sys/kmem.h> 386134Scasper #include <sys/project.h> 396134Scasper #include <sys/systm.h> 406134Scasper #include <sys/sysmacros.h> 416134Scasper #include <sys/pathname.h> 426134Scasper #include <sys/varargs.h> 436134Scasper #include <sys/zone.h> 446134Scasper #include <netinet/in.h> 456134Scasper 466134Scasper #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1)) 476134Scasper 486134Scasper static kmutex_t klpd_mutex; 496134Scasper 506134Scasper typedef struct klpd_reg { 516134Scasper struct klpd_reg *klpd_next; 526134Scasper struct klpd_reg **klpd_refp; 536134Scasper door_handle_t klpd_door; 546134Scasper pid_t klpd_door_pid; 556134Scasper priv_set_t klpd_pset; 566134Scasper cred_t *klpd_cred; 576134Scasper int klpd_indel; /* Disabled */ 586134Scasper uint32_t klpd_ref; 596134Scasper } klpd_reg_t; 606134Scasper 616134Scasper 626134Scasper /* 636134Scasper * This data structure hangs off the credential of a process; the 646134Scasper * credential is finalized and cannot be changed; but this structure 656134Scasper * can be changed when a new door server for the particular group 666134Scasper * needs to be registered. It is refcounted and shared between 676134Scasper * processes with common ancestry. 686134Scasper * 696134Scasper * The reference count is atomically updated. 706134Scasper * 716134Scasper * But the registration probably needs to be updated under a lock. 726134Scasper */ 736134Scasper typedef struct credklpd { 746134Scasper kmutex_t crkl_lock; 756134Scasper klpd_reg_t *crkl_reg; 766134Scasper uint32_t crkl_ref; 776134Scasper } credklpd_t; 786134Scasper 796134Scasper klpd_reg_t *klpd_list; 806134Scasper 816134Scasper static void klpd_unlink(klpd_reg_t *); 826134Scasper static int klpd_unreg_dh(door_handle_t); 836134Scasper 846134Scasper static credklpd_t *crklpd_alloc(void); 856134Scasper 866134Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *); 876134Scasper 886134Scasper extern size_t max_vnode_path; 896134Scasper 906134Scasper void 916134Scasper klpd_rele(klpd_reg_t *p) 926134Scasper { 936134Scasper if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) { 946134Scasper if (p->klpd_refp != NULL) 956134Scasper klpd_unlink(p); 966134Scasper if (p->klpd_cred != NULL) 976134Scasper crfree(p->klpd_cred); 986134Scasper door_ki_rele(p->klpd_door); 996134Scasper kmem_free(p, sizeof (*p)); 1006134Scasper } 1016134Scasper } 1026134Scasper 1036134Scasper /* 1046134Scasper * In order to be able to walk the lists, we can't unlink the entry 1056134Scasper * until the reference count drops to 0. If we remove it too soon, 1066134Scasper * list walkers will terminate when they happen to call a now orphaned 1076134Scasper * entry. 1086134Scasper */ 1096134Scasper static klpd_reg_t * 1106134Scasper klpd_rele_next(klpd_reg_t *p) 1116134Scasper { 1126134Scasper klpd_reg_t *r = p->klpd_next; 1136134Scasper 1146134Scasper klpd_rele(p); 1156134Scasper return (r); 1166134Scasper } 1176134Scasper 1186134Scasper 1196134Scasper static void 1206134Scasper klpd_hold(klpd_reg_t *p) 1216134Scasper { 1226134Scasper atomic_add_32(&p->klpd_ref, 1); 1236134Scasper } 1246134Scasper 1256134Scasper /* 1266134Scasper * Remove registration from where it is registered. Returns next in list. 1276134Scasper */ 1286134Scasper static void 1296134Scasper klpd_unlink(klpd_reg_t *p) 1306134Scasper { 1316134Scasper ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p); 1326134Scasper 1336134Scasper if (p->klpd_refp != NULL) 1346134Scasper *p->klpd_refp = p->klpd_next; 1356134Scasper 1366134Scasper if (p->klpd_next != NULL) 1376134Scasper p->klpd_next->klpd_refp = p->klpd_refp; 1386134Scasper p->klpd_refp = NULL; 1396134Scasper } 1406134Scasper 1416134Scasper /* 1426134Scasper * Remove the head of the klpd list and decrement its refcnt. 1436134Scasper * The lock guarding the list should be held; this function is 1446134Scasper * called when we are sure we want to remove the entry from the 1456134Scasper * list but not so sure that the reference count has dropped back to 1466134Scasper * 1 and is specifically intended to remove the non-list variants. 1476134Scasper */ 1486134Scasper void 1496134Scasper klpd_remove(klpd_reg_t **pp) 1506134Scasper { 1516134Scasper klpd_reg_t *p = *pp; 1526134Scasper if (p == NULL) 1536134Scasper return; 1546134Scasper ASSERT(p->klpd_next == NULL); 1556134Scasper klpd_unlink(p); 1566134Scasper klpd_rele(p); 1576134Scasper } 1586134Scasper 1596134Scasper /* 1606134Scasper * Link new entry in list. The Boolean argument specifies whether this 1616134Scasper * list can contain only a single item or multiple items. 1626134Scasper * Returns the entry which needs to be released if single is B_TRUE. 1636134Scasper */ 1646134Scasper static klpd_reg_t * 1656134Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single) 1666134Scasper { 1676134Scasper klpd_reg_t *old = *listp; 1686134Scasper 1696134Scasper ASSERT(p->klpd_ref == 1); 1706134Scasper 1716134Scasper ASSERT(old == NULL || *old->klpd_refp == old); 1726134Scasper p->klpd_refp = listp; 1736134Scasper p->klpd_next = single ? NULL : old; 1746134Scasper *listp = p; 1756134Scasper if (old != NULL) { 1766134Scasper if (single) { 1776134Scasper ASSERT(old->klpd_next == NULL); 1786134Scasper old->klpd_refp = NULL; 1796134Scasper return (old); 1806134Scasper } else 1816134Scasper old->klpd_refp = &p->klpd_next; 1826134Scasper } 1836134Scasper return (NULL); 1846134Scasper } 1856134Scasper 1866134Scasper /* 1876134Scasper * The typical call consists of: 1886134Scasper * - priv_set_t 1896134Scasper * - some integer data (type, value) 1906134Scasper * for now, it's just one bit. 1916134Scasper */ 1926134Scasper static klpd_head_t * 1936134Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap) 1946134Scasper { 1956134Scasper char *comp; 1966134Scasper uint_t type; 1976134Scasper vnode_t *vp; 1986134Scasper size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t); 1996134Scasper size_t plen, clen; 2006134Scasper int proto; 2016134Scasper 2026134Scasper klpd_arg_t *kap = NULL; 2036134Scasper klpd_head_t *khp; 2046134Scasper 2056134Scasper type = va_arg(ap, uint_t); 2066134Scasper switch (type) { 2076134Scasper case KLPDARG_NOMORE: 2086134Scasper khp = kmem_zalloc(len, KM_SLEEP); 2096134Scasper khp->klh_argoff = 0; 2106134Scasper break; 2116134Scasper case KLPDARG_VNODE: 2126134Scasper len += offsetof(klpd_arg_t, kla_str); 2136134Scasper vp = va_arg(ap, vnode_t *); 2146134Scasper if (vp == NULL) 2156134Scasper return (NULL); 2166134Scasper 2176134Scasper comp = va_arg(ap, char *); 2186134Scasper 2196134Scasper if (comp != NULL && *comp != '\0') 2206134Scasper clen = strlen(comp) + 1; 2216134Scasper else 2226134Scasper clen = 0; 2236134Scasper 2246134Scasper len += ROUNDUP(MAXPATHLEN, sizeof (uint_t)); 2256134Scasper khp = kmem_zalloc(len, KM_SLEEP); 2266134Scasper 2276134Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 2286134Scasper kap = KLH_ARG(khp); 2296134Scasper 2306134Scasper if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp, 2316134Scasper vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) { 2326134Scasper kmem_free(khp, len); 2336134Scasper return (NULL); 2346134Scasper } 2356134Scasper if (clen != 0) { 2366134Scasper plen = strlen(kap->kla_str); 2376134Scasper if (plen + clen + 1 >= MAXPATHLEN) { 2386134Scasper kmem_free(khp, len); 2396134Scasper return (NULL); 2406134Scasper } 2416134Scasper /* Don't make root into a double "/" */ 2426134Scasper if (plen <= 2) 2436134Scasper plen = 0; 2446134Scasper kap->kla_str[plen] = '/'; 2456134Scasper bcopy(comp, &kap->kla_str[plen + 1], clen); 2466134Scasper } 2476134Scasper break; 2486134Scasper case KLPDARG_PORT: 2496134Scasper proto = va_arg(ap, int); 2506134Scasper switch (proto) { 2516134Scasper case IPPROTO_TCP: type = KLPDARG_TCPPORT; 2526134Scasper break; 2536134Scasper case IPPROTO_UDP: type = KLPDARG_UDPPORT; 2546134Scasper break; 2556134Scasper case IPPROTO_SCTP: type = KLPDARG_SCTPPORT; 2566134Scasper break; 2576134Scasper case PROTO_SDP: type = KLPDARG_SDPPORT; 2586134Scasper break; 2596134Scasper } 2606134Scasper /* FALLTHROUGH */ 2616134Scasper case KLPDARG_INT: 2626134Scasper case KLPDARG_TCPPORT: 2636134Scasper case KLPDARG_UDPPORT: 2646134Scasper case KLPDARG_SCTPPORT: 2656134Scasper case KLPDARG_SDPPORT: 2666134Scasper len += sizeof (*kap); 2676134Scasper khp = kmem_zalloc(len, KM_SLEEP); 2686134Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 2696134Scasper kap = KLH_ARG(khp); 2706134Scasper kap->kla_int = va_arg(ap, int); 2716134Scasper break; 2726134Scasper default: 2736134Scasper return (NULL); 2746134Scasper } 2756134Scasper khp->klh_vers = KLPDCALL_VERS; 2766134Scasper khp->klh_len = len; 2776134Scasper khp->klh_privoff = sizeof (*khp); 2786134Scasper *KLH_PRIVSET(khp) = *rq; 2796134Scasper if (kap != NULL) { 2806134Scasper kap->kla_type = type; 2816134Scasper kap->kla_dlen = len - khp->klh_argoff; 2826134Scasper } 2836134Scasper return (khp); 2846134Scasper } 2856134Scasper 2866134Scasper static int 2876134Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap) 2886134Scasper { 2896134Scasper door_arg_t da; 2906134Scasper int res; 2916134Scasper int dres; 2926134Scasper klpd_head_t *klh; 2936134Scasper 2946134Scasper if (p->klpd_door_pid == curproc->p_pid) 2956134Scasper return (-1); 2966134Scasper 2976134Scasper klh = klpd_marshall(p, req, ap); 2986134Scasper 2996134Scasper if (klh == NULL) 3006134Scasper return (-1); 3016134Scasper 3026134Scasper da.data_ptr = (char *)klh; 3036134Scasper da.data_size = klh->klh_len; 3046134Scasper da.desc_ptr = NULL; 3056134Scasper da.desc_num = 0; 3066134Scasper da.rbuf = (char *)&res; 3076134Scasper da.rsize = sizeof (res); 3086134Scasper 309*6997Sjwadams while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL, 310*6997Sjwadams SIZE_MAX, 0)) != 0) { 3116134Scasper switch (dres) { 3126134Scasper case EAGAIN: 3136134Scasper delay(1); 3146134Scasper continue; 3156134Scasper case EINVAL: 3166134Scasper case EBADF: 3176134Scasper /* Bad door, don't call it again. */ 3186134Scasper (void) klpd_unreg_dh(p->klpd_door); 3196134Scasper /* FALLTHROUGH */ 3206134Scasper case EINTR: 3216134Scasper /* Pending signal, nothing we can do. */ 3226134Scasper /* FALLTHROUGH */ 3236134Scasper default: 3246134Scasper kmem_free(klh, klh->klh_len); 3256134Scasper return (-1); 3266134Scasper } 3276134Scasper } 3286134Scasper kmem_free(klh, klh->klh_len); 3296134Scasper /* Bogus return value, must be a failure */ 3306134Scasper if (da.rbuf != (char *)&res) { 3316134Scasper kmem_free(da.rbuf, da.rsize); 3326134Scasper return (-1); 3336134Scasper } 3346134Scasper return (res); 3356134Scasper } 3366134Scasper 3376134Scasper uint32_t klpd_bad_locks; 3386134Scasper 3396134Scasper int 3406134Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) 3416134Scasper { 3426134Scasper klpd_reg_t *p; 3436134Scasper int rv = -1; 3446134Scasper credklpd_t *ckp; 3456134Scasper zone_t *ckzone; 3466134Scasper 3476134Scasper /* 3486134Scasper * These locks must not be held when this code is called; 3496134Scasper * callbacks to userland with these locks held will result 3506134Scasper * in issues. That said, the code at the call sides was 3516134Scasper * restructured not to call with any of the locks held and 3526134Scasper * no policies operate by default on most processes. 3536134Scasper */ 3546134Scasper if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || 3556134Scasper mutex_owned(&curproc->p_crlock)) { 3566134Scasper atomic_add_32(&klpd_bad_locks, 1); 3576134Scasper return (-1); 3586134Scasper } 3596134Scasper 3606134Scasper /* 3616134Scasper * Enforce the limit set for the call process (still). 3626134Scasper */ 3636134Scasper if (!priv_issubset(req, &CR_LPRIV(cr))) 3646134Scasper return (-1); 3656134Scasper 3666134Scasper /* Try 1: get the credential specific klpd */ 3676134Scasper if ((ckp = crgetcrklpd(cr)) != NULL) { 3686134Scasper mutex_enter(&ckp->crkl_lock); 3696134Scasper if ((p = ckp->crkl_reg) != NULL && 3706134Scasper p->klpd_indel == 0 && 3716134Scasper priv_issubset(req, &p->klpd_pset)) { 3726134Scasper klpd_hold(p); 3736134Scasper mutex_exit(&ckp->crkl_lock); 3746134Scasper rv = klpd_do_call(p, req, ap); 3756134Scasper mutex_enter(&ckp->crkl_lock); 3766134Scasper klpd_rele(p); 3776134Scasper mutex_exit(&ckp->crkl_lock); 3786134Scasper if (rv != -1) 3796134Scasper return (rv == 0 ? 0 : -1); 3806134Scasper } else { 3816134Scasper mutex_exit(&ckp->crkl_lock); 3826134Scasper } 3836134Scasper } 3846134Scasper 3856134Scasper /* Try 2: get the project specific klpd */ 3866134Scasper mutex_enter(&klpd_mutex); 3876134Scasper 3886134Scasper if ((p = curproj->kpj_klpd) != NULL) { 3896134Scasper klpd_hold(p); 3906134Scasper mutex_exit(&klpd_mutex); 3916134Scasper if (p->klpd_indel == 0 && 3926134Scasper priv_issubset(req, &p->klpd_pset)) { 3936134Scasper rv = klpd_do_call(p, req, ap); 3946134Scasper } 3956134Scasper mutex_enter(&klpd_mutex); 3966134Scasper klpd_rele(p); 3976134Scasper mutex_exit(&klpd_mutex); 3986134Scasper 3996134Scasper if (rv != -1) 4006134Scasper return (rv == 0 ? 0 : -1); 4016134Scasper } else { 4026134Scasper mutex_exit(&klpd_mutex); 4036134Scasper } 4046134Scasper 4056134Scasper /* Try 3: get the global klpd list */ 4066134Scasper ckzone = crgetzone(cr); 4076134Scasper mutex_enter(&klpd_mutex); 4086134Scasper 4096134Scasper for (p = klpd_list; p != NULL; ) { 4106134Scasper zone_t *kkzone = crgetzone(p->klpd_cred); 4116134Scasper if ((kkzone == &zone0 || kkzone == ckzone) && 4126134Scasper p->klpd_indel == 0 && 4136134Scasper priv_issubset(req, &p->klpd_pset)) { 4146134Scasper klpd_hold(p); 4156134Scasper mutex_exit(&klpd_mutex); 4166134Scasper rv = klpd_do_call(p, req, ap); 4176134Scasper mutex_enter(&klpd_mutex); 4186134Scasper 4196134Scasper p = klpd_rele_next(p); 4206134Scasper 4216134Scasper if (rv != -1) 4226134Scasper break; 4236134Scasper } else { 4246134Scasper p = p->klpd_next; 4256134Scasper } 4266134Scasper } 4276134Scasper mutex_exit(&klpd_mutex); 4286134Scasper return (rv == 0 ? 0 : -1); 4296134Scasper } 4306134Scasper 4316134Scasper /* 4326134Scasper * Register the klpd. 4336134Scasper * If the pid_t passed in is positive, update the registration for 4346134Scasper * the specific process; that is only possible if the process already 4356134Scasper * has a registration on it. This change of registration will affect 4366134Scasper * all processes which share common ancestry. 4376134Scasper * 4386134Scasper * MY_PID (pid 0) can be used to create or change the context for 4396134Scasper * the current process, typically done after fork(). 4406134Scasper * 4416134Scasper * A negative value can be used to register a klpd globally. 4426134Scasper * 4436134Scasper * The per-credential klpd needs to be cleaned up when entering 4446134Scasper * a zone or unsetting the flag. 4456134Scasper */ 4466134Scasper int 4476134Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) 4486134Scasper { 4496134Scasper cred_t *cr = CRED(); 4506134Scasper door_handle_t dh; 4516134Scasper klpd_reg_t *kpd; 4526134Scasper priv_set_t pset; 4536134Scasper door_info_t di; 4546134Scasper credklpd_t *ckp = NULL; 4556134Scasper pid_t pid = -1; 4566134Scasper projid_t proj = -1; 4576134Scasper kproject_t *kpp = NULL; 4586134Scasper 4596134Scasper if (CR_FLAGS(cr) & PRIV_XPOLICY) 4606134Scasper return (set_errno(EINVAL)); 4616134Scasper 4626134Scasper if (copyin(psetbuf, &pset, sizeof (priv_set_t))) 4636134Scasper return (set_errno(EFAULT)); 4646134Scasper 4656134Scasper if (!priv_issubset(&pset, &CR_OEPRIV(cr))) 4666134Scasper return (set_errno(EPERM)); 4676134Scasper 4686134Scasper switch (type) { 4696134Scasper case P_PID: 4706134Scasper pid = (pid_t)id; 4716134Scasper if (pid == P_MYPID) 4726134Scasper pid = curproc->p_pid; 4736134Scasper if (pid == curproc->p_pid) 4746134Scasper ckp = crklpd_alloc(); 4756134Scasper break; 4766134Scasper case P_PROJID: 4776134Scasper proj = (projid_t)id; 4786134Scasper kpp = project_hold_by_id(proj, crgetzone(cr), 4796134Scasper PROJECT_HOLD_FIND); 4806134Scasper if (kpp == NULL) 4816134Scasper return (set_errno(ESRCH)); 4826134Scasper break; 4836134Scasper default: 4846134Scasper return (set_errno(ENOTSUP)); 4856134Scasper } 4866134Scasper 4876134Scasper 4886134Scasper /* 4896134Scasper * Verify the door passed in; it must be a door and we won't 4906134Scasper * allow processes to be called on their own behalf. 4916134Scasper */ 4926134Scasper dh = door_ki_lookup(did); 4936134Scasper if (dh == NULL || door_ki_info(dh, &di) != 0) { 4946134Scasper if (ckp != NULL) 4956134Scasper crklpd_rele(ckp); 4966134Scasper if (kpp != NULL) 4976134Scasper project_rele(kpp); 4986134Scasper return (set_errno(EBADF)); 4996134Scasper } 5006134Scasper if (type == P_PID && pid == di.di_target) { 5016134Scasper if (ckp != NULL) 5026134Scasper crklpd_rele(ckp); 5036134Scasper ASSERT(kpp == NULL); 5046134Scasper return (set_errno(EINVAL)); 5056134Scasper } 5066134Scasper 5076134Scasper kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); 5086134Scasper crhold(kpd->klpd_cred = cr); 5096134Scasper kpd->klpd_door = dh; 5106134Scasper kpd->klpd_door_pid = di.di_target; 5116134Scasper kpd->klpd_ref = 1; 5126134Scasper kpd->klpd_pset = pset; 5136134Scasper 5146134Scasper if (kpp != NULL) { 5156134Scasper mutex_enter(&klpd_mutex); 5166134Scasper kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); 5176134Scasper mutex_exit(&klpd_mutex); 5186134Scasper if (kpd != NULL) 5196134Scasper klpd_rele(kpd); 5206134Scasper project_rele(kpp); 5216134Scasper } else if ((int)pid < 0) { 5226134Scasper /* Global daemon */ 5236134Scasper mutex_enter(&klpd_mutex); 5246134Scasper (void) klpd_link(kpd, &klpd_list, B_FALSE); 5256134Scasper mutex_exit(&klpd_mutex); 5266134Scasper } else if (pid == curproc->p_pid) { 5276134Scasper proc_t *p = curproc; 5286134Scasper cred_t *newcr = cralloc(); 5296134Scasper 5306134Scasper /* No need to lock, sole reference to ckp */ 5316134Scasper kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); 5326134Scasper 5336134Scasper if (kpd != NULL) 5346134Scasper klpd_rele(kpd); 5356134Scasper 5366134Scasper mutex_enter(&p->p_crlock); 5376134Scasper cr = p->p_cred; 5386134Scasper crdup_to(cr, newcr); 5396134Scasper crsetcrklpd(newcr, ckp); 5406134Scasper p->p_cred = newcr; /* Already held for p_cred */ 5416134Scasper 5426134Scasper crhold(newcr); /* Hold once for the current thread */ 5436134Scasper mutex_exit(&p->p_crlock); 5446134Scasper crfree(cr); /* One for the p_cred */ 5456134Scasper crset(p, newcr); 5466134Scasper } else { 5476134Scasper proc_t *p; 5486134Scasper cred_t *pcr; 5496134Scasper mutex_enter(&pidlock); 5506134Scasper p = prfind(pid); 5516134Scasper if (p == NULL || !prochasprocperm(p, curproc, CRED())) { 5526134Scasper mutex_exit(&pidlock); 5536134Scasper klpd_rele(kpd); 5546134Scasper return (set_errno(p == NULL ? ESRCH : EPERM)); 5556134Scasper } 5566134Scasper mutex_enter(&p->p_crlock); 5576134Scasper crhold(pcr = p->p_cred); 5586134Scasper mutex_exit(&pidlock); 5596134Scasper mutex_exit(&p->p_crlock); 5606134Scasper /* 5616134Scasper * We're going to update the credential's ckp in place; 5626134Scasper * this requires that it exists. 5636134Scasper */ 5646134Scasper ckp = crgetcrklpd(pcr); 5656134Scasper if (ckp == NULL) { 5666134Scasper crfree(pcr); 5676134Scasper klpd_rele(kpd); 5686134Scasper return (set_errno(EINVAL)); 5696134Scasper } 5706134Scasper crklpd_setreg(ckp, kpd); 5716134Scasper crfree(pcr); 5726134Scasper } 5736134Scasper 5746134Scasper return (0); 5756134Scasper } 5766134Scasper 5776134Scasper static int 5786134Scasper klpd_unreg_dh(door_handle_t dh) 5796134Scasper { 5806134Scasper klpd_reg_t *p; 5816134Scasper 5826134Scasper mutex_enter(&klpd_mutex); 5836134Scasper for (p = klpd_list; p != NULL; p = p->klpd_next) { 5846134Scasper if (p->klpd_door == dh) 5856134Scasper break; 5866134Scasper } 5876134Scasper if (p == NULL) { 5886134Scasper mutex_exit(&klpd_mutex); 5896134Scasper return (EINVAL); 5906134Scasper } 5916134Scasper if (p->klpd_indel != 0) { 5926134Scasper mutex_exit(&klpd_mutex); 5936134Scasper return (EAGAIN); 5946134Scasper } 5956134Scasper p->klpd_indel = 1; 5966134Scasper klpd_rele(p); 5976134Scasper mutex_exit(&klpd_mutex); 5986134Scasper return (0); 5996134Scasper } 6006134Scasper 6016134Scasper int 6026134Scasper klpd_unreg(int did, idtype_t type, id_t id) 6036134Scasper { 6046134Scasper door_handle_t dh; 6056134Scasper int res = 0; 6066134Scasper proc_t *p; 6076134Scasper pid_t pid; 6086134Scasper projid_t proj; 6096134Scasper kproject_t *kpp = NULL; 6106134Scasper credklpd_t *ckp; 6116134Scasper 6126134Scasper switch (type) { 6136134Scasper case P_PID: 6146134Scasper pid = (pid_t)id; 6156134Scasper break; 6166134Scasper case P_PROJID: 6176134Scasper proj = (projid_t)id; 6186134Scasper kpp = project_hold_by_id(proj, crgetzone(CRED()), 6196134Scasper PROJECT_HOLD_FIND); 6206134Scasper if (kpp == NULL) 6216134Scasper return (set_errno(ESRCH)); 6226134Scasper break; 6236134Scasper default: 6246134Scasper return (set_errno(ENOTSUP)); 6256134Scasper } 6266134Scasper 6276134Scasper dh = door_ki_lookup(did); 6286134Scasper if (dh == NULL) { 6296134Scasper if (kpp != NULL) 6306134Scasper project_rele(kpp); 6316134Scasper return (set_errno(EINVAL)); 6326134Scasper } 6336134Scasper 6346134Scasper if (kpp != NULL) { 6356134Scasper mutex_enter(&klpd_mutex); 6366134Scasper if (kpp->kpj_klpd == NULL) 6376134Scasper res = ESRCH; 6386134Scasper else 6396134Scasper klpd_remove(&kpp->kpj_klpd); 6406134Scasper mutex_exit(&klpd_mutex); 6416134Scasper project_rele(kpp); 6426134Scasper goto out; 6436134Scasper } else if ((int)pid > 0) { 6446134Scasper mutex_enter(&pidlock); 6456134Scasper p = prfind(pid); 6466134Scasper if (p == NULL) { 6476134Scasper mutex_exit(&pidlock); 6486134Scasper door_ki_rele(dh); 6496134Scasper return (set_errno(ESRCH)); 6506134Scasper } 6516134Scasper mutex_enter(&p->p_crlock); 6526134Scasper mutex_exit(&pidlock); 6536134Scasper } else if (pid == 0) { 6546134Scasper p = curproc; 6556134Scasper mutex_enter(&p->p_crlock); 6566134Scasper } else { 6576134Scasper res = klpd_unreg_dh(dh); 6586134Scasper goto out; 6596134Scasper } 6606134Scasper 6616134Scasper ckp = crgetcrklpd(p->p_cred); 6626134Scasper if (ckp != NULL) { 6636134Scasper crklpd_setreg(ckp, NULL); 6646134Scasper } else { 6656134Scasper res = ESRCH; 6666134Scasper } 6676134Scasper mutex_exit(&p->p_crlock); 6686134Scasper 6696134Scasper out: 6706134Scasper door_ki_rele(dh); 6716134Scasper 6726134Scasper if (res != 0) 6736134Scasper return (set_errno(res)); 6746134Scasper return (0); 6756134Scasper } 6766134Scasper 6776134Scasper void 6786134Scasper crklpd_hold(credklpd_t *crkpd) 6796134Scasper { 6806134Scasper atomic_add_32(&crkpd->crkl_ref, 1); 6816134Scasper } 6826134Scasper 6836134Scasper void 6846134Scasper crklpd_rele(credklpd_t *crkpd) 6856134Scasper { 6866134Scasper if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) { 6876134Scasper if (crkpd->crkl_reg != NULL) 6886134Scasper klpd_rele(crkpd->crkl_reg); 6896134Scasper mutex_destroy(&crkpd->crkl_lock); 6906134Scasper kmem_free(crkpd, sizeof (*crkpd)); 6916134Scasper } 6926134Scasper } 6936134Scasper 6946134Scasper static credklpd_t * 6956134Scasper crklpd_alloc(void) 6966134Scasper { 6976134Scasper credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP); 6986134Scasper 6996134Scasper mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL); 7006134Scasper res->crkl_ref = 1; 7016134Scasper res->crkl_reg = NULL; 7026134Scasper 7036134Scasper return (res); 7046134Scasper } 7056134Scasper 7066134Scasper void 7076134Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new) 7086134Scasper { 7096134Scasper klpd_reg_t *old; 7106134Scasper 7116134Scasper mutex_enter(&crk->crkl_lock); 7126134Scasper if (new == NULL) { 7136134Scasper old = crk->crkl_reg; 7146134Scasper if (old != NULL) 7156134Scasper klpd_unlink(old); 7166134Scasper } else { 7176134Scasper old = klpd_link(new, &crk->crkl_reg, B_TRUE); 7186134Scasper } 7196134Scasper mutex_exit(&crk->crkl_lock); 7206134Scasper 7216134Scasper if (old != NULL) 7226134Scasper klpd_rele(old); 7236134Scasper } 724