1*ddf7fe95Scasper /* 2*ddf7fe95Scasper * CDDL HEADER START 3*ddf7fe95Scasper * 4*ddf7fe95Scasper * The contents of this file are subject to the terms of the 5*ddf7fe95Scasper * Common Development and Distribution License (the "License"). 6*ddf7fe95Scasper * You may not use this file except in compliance with the License. 7*ddf7fe95Scasper * 8*ddf7fe95Scasper * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*ddf7fe95Scasper * or http://www.opensolaris.org/os/licensing. 10*ddf7fe95Scasper * See the License for the specific language governing permissions 11*ddf7fe95Scasper * and limitations under the License. 12*ddf7fe95Scasper * 13*ddf7fe95Scasper * When distributing Covered Code, include this CDDL HEADER in each 14*ddf7fe95Scasper * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*ddf7fe95Scasper * If applicable, add the following below this CDDL HEADER, with the 16*ddf7fe95Scasper * fields enclosed by brackets "[]" replaced with your own identifying 17*ddf7fe95Scasper * information: Portions Copyright [yyyy] [name of copyright owner] 18*ddf7fe95Scasper * 19*ddf7fe95Scasper * CDDL HEADER END 20*ddf7fe95Scasper */ 21*ddf7fe95Scasper 22*ddf7fe95Scasper /* 23*ddf7fe95Scasper * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24*ddf7fe95Scasper * Use is subject to license terms. 25*ddf7fe95Scasper */ 26*ddf7fe95Scasper 27*ddf7fe95Scasper #pragma ident "%Z%%M% %I% %E% SMI" 28*ddf7fe95Scasper 29*ddf7fe95Scasper #include <sys/atomic.h> 30*ddf7fe95Scasper #include <sys/door.h> 31*ddf7fe95Scasper #include <sys/proc.h> 32*ddf7fe95Scasper #include <sys/cred_impl.h> 33*ddf7fe95Scasper #include <sys/policy.h> 34*ddf7fe95Scasper #include <sys/priv.h> 35*ddf7fe95Scasper #include <sys/klpd.h> 36*ddf7fe95Scasper #include <sys/errno.h> 37*ddf7fe95Scasper #include <sys/kmem.h> 38*ddf7fe95Scasper #include <sys/project.h> 39*ddf7fe95Scasper #include <sys/systm.h> 40*ddf7fe95Scasper #include <sys/sysmacros.h> 41*ddf7fe95Scasper #include <sys/pathname.h> 42*ddf7fe95Scasper #include <sys/varargs.h> 43*ddf7fe95Scasper #include <sys/zone.h> 44*ddf7fe95Scasper #include <netinet/in.h> 45*ddf7fe95Scasper 46*ddf7fe95Scasper #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1)) 47*ddf7fe95Scasper 48*ddf7fe95Scasper static kmutex_t klpd_mutex; 49*ddf7fe95Scasper 50*ddf7fe95Scasper typedef struct klpd_reg { 51*ddf7fe95Scasper struct klpd_reg *klpd_next; 52*ddf7fe95Scasper struct klpd_reg **klpd_refp; 53*ddf7fe95Scasper door_handle_t klpd_door; 54*ddf7fe95Scasper pid_t klpd_door_pid; 55*ddf7fe95Scasper priv_set_t klpd_pset; 56*ddf7fe95Scasper cred_t *klpd_cred; 57*ddf7fe95Scasper int klpd_indel; /* Disabled */ 58*ddf7fe95Scasper uint32_t klpd_ref; 59*ddf7fe95Scasper } klpd_reg_t; 60*ddf7fe95Scasper 61*ddf7fe95Scasper 62*ddf7fe95Scasper /* 63*ddf7fe95Scasper * This data structure hangs off the credential of a process; the 64*ddf7fe95Scasper * credential is finalized and cannot be changed; but this structure 65*ddf7fe95Scasper * can be changed when a new door server for the particular group 66*ddf7fe95Scasper * needs to be registered. It is refcounted and shared between 67*ddf7fe95Scasper * processes with common ancestry. 68*ddf7fe95Scasper * 69*ddf7fe95Scasper * The reference count is atomically updated. 70*ddf7fe95Scasper * 71*ddf7fe95Scasper * But the registration probably needs to be updated under a lock. 72*ddf7fe95Scasper */ 73*ddf7fe95Scasper typedef struct credklpd { 74*ddf7fe95Scasper kmutex_t crkl_lock; 75*ddf7fe95Scasper klpd_reg_t *crkl_reg; 76*ddf7fe95Scasper uint32_t crkl_ref; 77*ddf7fe95Scasper } credklpd_t; 78*ddf7fe95Scasper 79*ddf7fe95Scasper klpd_reg_t *klpd_list; 80*ddf7fe95Scasper 81*ddf7fe95Scasper static void klpd_unlink(klpd_reg_t *); 82*ddf7fe95Scasper static int klpd_unreg_dh(door_handle_t); 83*ddf7fe95Scasper 84*ddf7fe95Scasper static credklpd_t *crklpd_alloc(void); 85*ddf7fe95Scasper 86*ddf7fe95Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *); 87*ddf7fe95Scasper 88*ddf7fe95Scasper extern size_t max_vnode_path; 89*ddf7fe95Scasper 90*ddf7fe95Scasper void 91*ddf7fe95Scasper klpd_rele(klpd_reg_t *p) 92*ddf7fe95Scasper { 93*ddf7fe95Scasper if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) { 94*ddf7fe95Scasper if (p->klpd_refp != NULL) 95*ddf7fe95Scasper klpd_unlink(p); 96*ddf7fe95Scasper if (p->klpd_cred != NULL) 97*ddf7fe95Scasper crfree(p->klpd_cred); 98*ddf7fe95Scasper door_ki_rele(p->klpd_door); 99*ddf7fe95Scasper kmem_free(p, sizeof (*p)); 100*ddf7fe95Scasper } 101*ddf7fe95Scasper } 102*ddf7fe95Scasper 103*ddf7fe95Scasper /* 104*ddf7fe95Scasper * In order to be able to walk the lists, we can't unlink the entry 105*ddf7fe95Scasper * until the reference count drops to 0. If we remove it too soon, 106*ddf7fe95Scasper * list walkers will terminate when they happen to call a now orphaned 107*ddf7fe95Scasper * entry. 108*ddf7fe95Scasper */ 109*ddf7fe95Scasper static klpd_reg_t * 110*ddf7fe95Scasper klpd_rele_next(klpd_reg_t *p) 111*ddf7fe95Scasper { 112*ddf7fe95Scasper klpd_reg_t *r = p->klpd_next; 113*ddf7fe95Scasper 114*ddf7fe95Scasper klpd_rele(p); 115*ddf7fe95Scasper return (r); 116*ddf7fe95Scasper } 117*ddf7fe95Scasper 118*ddf7fe95Scasper 119*ddf7fe95Scasper static void 120*ddf7fe95Scasper klpd_hold(klpd_reg_t *p) 121*ddf7fe95Scasper { 122*ddf7fe95Scasper atomic_add_32(&p->klpd_ref, 1); 123*ddf7fe95Scasper } 124*ddf7fe95Scasper 125*ddf7fe95Scasper /* 126*ddf7fe95Scasper * Remove registration from where it is registered. Returns next in list. 127*ddf7fe95Scasper */ 128*ddf7fe95Scasper static void 129*ddf7fe95Scasper klpd_unlink(klpd_reg_t *p) 130*ddf7fe95Scasper { 131*ddf7fe95Scasper ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p); 132*ddf7fe95Scasper 133*ddf7fe95Scasper if (p->klpd_refp != NULL) 134*ddf7fe95Scasper *p->klpd_refp = p->klpd_next; 135*ddf7fe95Scasper 136*ddf7fe95Scasper if (p->klpd_next != NULL) 137*ddf7fe95Scasper p->klpd_next->klpd_refp = p->klpd_refp; 138*ddf7fe95Scasper p->klpd_refp = NULL; 139*ddf7fe95Scasper } 140*ddf7fe95Scasper 141*ddf7fe95Scasper /* 142*ddf7fe95Scasper * Remove the head of the klpd list and decrement its refcnt. 143*ddf7fe95Scasper * The lock guarding the list should be held; this function is 144*ddf7fe95Scasper * called when we are sure we want to remove the entry from the 145*ddf7fe95Scasper * list but not so sure that the reference count has dropped back to 146*ddf7fe95Scasper * 1 and is specifically intended to remove the non-list variants. 147*ddf7fe95Scasper */ 148*ddf7fe95Scasper void 149*ddf7fe95Scasper klpd_remove(klpd_reg_t **pp) 150*ddf7fe95Scasper { 151*ddf7fe95Scasper klpd_reg_t *p = *pp; 152*ddf7fe95Scasper if (p == NULL) 153*ddf7fe95Scasper return; 154*ddf7fe95Scasper ASSERT(p->klpd_next == NULL); 155*ddf7fe95Scasper klpd_unlink(p); 156*ddf7fe95Scasper klpd_rele(p); 157*ddf7fe95Scasper } 158*ddf7fe95Scasper 159*ddf7fe95Scasper /* 160*ddf7fe95Scasper * Link new entry in list. The Boolean argument specifies whether this 161*ddf7fe95Scasper * list can contain only a single item or multiple items. 162*ddf7fe95Scasper * Returns the entry which needs to be released if single is B_TRUE. 163*ddf7fe95Scasper */ 164*ddf7fe95Scasper static klpd_reg_t * 165*ddf7fe95Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single) 166*ddf7fe95Scasper { 167*ddf7fe95Scasper klpd_reg_t *old = *listp; 168*ddf7fe95Scasper 169*ddf7fe95Scasper ASSERT(p->klpd_ref == 1); 170*ddf7fe95Scasper 171*ddf7fe95Scasper ASSERT(old == NULL || *old->klpd_refp == old); 172*ddf7fe95Scasper p->klpd_refp = listp; 173*ddf7fe95Scasper p->klpd_next = single ? NULL : old; 174*ddf7fe95Scasper *listp = p; 175*ddf7fe95Scasper if (old != NULL) { 176*ddf7fe95Scasper if (single) { 177*ddf7fe95Scasper ASSERT(old->klpd_next == NULL); 178*ddf7fe95Scasper old->klpd_refp = NULL; 179*ddf7fe95Scasper return (old); 180*ddf7fe95Scasper } else 181*ddf7fe95Scasper old->klpd_refp = &p->klpd_next; 182*ddf7fe95Scasper } 183*ddf7fe95Scasper return (NULL); 184*ddf7fe95Scasper } 185*ddf7fe95Scasper 186*ddf7fe95Scasper /* 187*ddf7fe95Scasper * The typical call consists of: 188*ddf7fe95Scasper * - priv_set_t 189*ddf7fe95Scasper * - some integer data (type, value) 190*ddf7fe95Scasper * for now, it's just one bit. 191*ddf7fe95Scasper */ 192*ddf7fe95Scasper static klpd_head_t * 193*ddf7fe95Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap) 194*ddf7fe95Scasper { 195*ddf7fe95Scasper char *comp; 196*ddf7fe95Scasper uint_t type; 197*ddf7fe95Scasper vnode_t *vp; 198*ddf7fe95Scasper size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t); 199*ddf7fe95Scasper size_t plen, clen; 200*ddf7fe95Scasper int proto; 201*ddf7fe95Scasper 202*ddf7fe95Scasper klpd_arg_t *kap = NULL; 203*ddf7fe95Scasper klpd_head_t *khp; 204*ddf7fe95Scasper 205*ddf7fe95Scasper type = va_arg(ap, uint_t); 206*ddf7fe95Scasper switch (type) { 207*ddf7fe95Scasper case KLPDARG_NOMORE: 208*ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 209*ddf7fe95Scasper khp->klh_argoff = 0; 210*ddf7fe95Scasper break; 211*ddf7fe95Scasper case KLPDARG_VNODE: 212*ddf7fe95Scasper len += offsetof(klpd_arg_t, kla_str); 213*ddf7fe95Scasper vp = va_arg(ap, vnode_t *); 214*ddf7fe95Scasper if (vp == NULL) 215*ddf7fe95Scasper return (NULL); 216*ddf7fe95Scasper 217*ddf7fe95Scasper comp = va_arg(ap, char *); 218*ddf7fe95Scasper 219*ddf7fe95Scasper if (comp != NULL && *comp != '\0') 220*ddf7fe95Scasper clen = strlen(comp) + 1; 221*ddf7fe95Scasper else 222*ddf7fe95Scasper clen = 0; 223*ddf7fe95Scasper 224*ddf7fe95Scasper len += ROUNDUP(MAXPATHLEN, sizeof (uint_t)); 225*ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 226*ddf7fe95Scasper 227*ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 228*ddf7fe95Scasper kap = KLH_ARG(khp); 229*ddf7fe95Scasper 230*ddf7fe95Scasper if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp, 231*ddf7fe95Scasper vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) { 232*ddf7fe95Scasper kmem_free(khp, len); 233*ddf7fe95Scasper return (NULL); 234*ddf7fe95Scasper } 235*ddf7fe95Scasper if (clen != 0) { 236*ddf7fe95Scasper plen = strlen(kap->kla_str); 237*ddf7fe95Scasper if (plen + clen + 1 >= MAXPATHLEN) { 238*ddf7fe95Scasper kmem_free(khp, len); 239*ddf7fe95Scasper return (NULL); 240*ddf7fe95Scasper } 241*ddf7fe95Scasper /* Don't make root into a double "/" */ 242*ddf7fe95Scasper if (plen <= 2) 243*ddf7fe95Scasper plen = 0; 244*ddf7fe95Scasper kap->kla_str[plen] = '/'; 245*ddf7fe95Scasper bcopy(comp, &kap->kla_str[plen + 1], clen); 246*ddf7fe95Scasper } 247*ddf7fe95Scasper break; 248*ddf7fe95Scasper case KLPDARG_PORT: 249*ddf7fe95Scasper proto = va_arg(ap, int); 250*ddf7fe95Scasper switch (proto) { 251*ddf7fe95Scasper case IPPROTO_TCP: type = KLPDARG_TCPPORT; 252*ddf7fe95Scasper break; 253*ddf7fe95Scasper case IPPROTO_UDP: type = KLPDARG_UDPPORT; 254*ddf7fe95Scasper break; 255*ddf7fe95Scasper case IPPROTO_SCTP: type = KLPDARG_SCTPPORT; 256*ddf7fe95Scasper break; 257*ddf7fe95Scasper case PROTO_SDP: type = KLPDARG_SDPPORT; 258*ddf7fe95Scasper break; 259*ddf7fe95Scasper } 260*ddf7fe95Scasper /* FALLTHROUGH */ 261*ddf7fe95Scasper case KLPDARG_INT: 262*ddf7fe95Scasper case KLPDARG_TCPPORT: 263*ddf7fe95Scasper case KLPDARG_UDPPORT: 264*ddf7fe95Scasper case KLPDARG_SCTPPORT: 265*ddf7fe95Scasper case KLPDARG_SDPPORT: 266*ddf7fe95Scasper len += sizeof (*kap); 267*ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 268*ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 269*ddf7fe95Scasper kap = KLH_ARG(khp); 270*ddf7fe95Scasper kap->kla_int = va_arg(ap, int); 271*ddf7fe95Scasper break; 272*ddf7fe95Scasper default: 273*ddf7fe95Scasper return (NULL); 274*ddf7fe95Scasper } 275*ddf7fe95Scasper khp->klh_vers = KLPDCALL_VERS; 276*ddf7fe95Scasper khp->klh_len = len; 277*ddf7fe95Scasper khp->klh_privoff = sizeof (*khp); 278*ddf7fe95Scasper *KLH_PRIVSET(khp) = *rq; 279*ddf7fe95Scasper if (kap != NULL) { 280*ddf7fe95Scasper kap->kla_type = type; 281*ddf7fe95Scasper kap->kla_dlen = len - khp->klh_argoff; 282*ddf7fe95Scasper } 283*ddf7fe95Scasper return (khp); 284*ddf7fe95Scasper } 285*ddf7fe95Scasper 286*ddf7fe95Scasper static int 287*ddf7fe95Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap) 288*ddf7fe95Scasper { 289*ddf7fe95Scasper door_arg_t da; 290*ddf7fe95Scasper int res; 291*ddf7fe95Scasper int dres; 292*ddf7fe95Scasper klpd_head_t *klh; 293*ddf7fe95Scasper 294*ddf7fe95Scasper if (p->klpd_door_pid == curproc->p_pid) 295*ddf7fe95Scasper return (-1); 296*ddf7fe95Scasper 297*ddf7fe95Scasper klh = klpd_marshall(p, req, ap); 298*ddf7fe95Scasper 299*ddf7fe95Scasper if (klh == NULL) 300*ddf7fe95Scasper return (-1); 301*ddf7fe95Scasper 302*ddf7fe95Scasper da.data_ptr = (char *)klh; 303*ddf7fe95Scasper da.data_size = klh->klh_len; 304*ddf7fe95Scasper da.desc_ptr = NULL; 305*ddf7fe95Scasper da.desc_num = 0; 306*ddf7fe95Scasper da.rbuf = (char *)&res; 307*ddf7fe95Scasper da.rsize = sizeof (res); 308*ddf7fe95Scasper 309*ddf7fe95Scasper while ((dres = door_ki_upcall(p->klpd_door, &da)) != 0) { 310*ddf7fe95Scasper switch (dres) { 311*ddf7fe95Scasper case EAGAIN: 312*ddf7fe95Scasper delay(1); 313*ddf7fe95Scasper continue; 314*ddf7fe95Scasper case EINVAL: 315*ddf7fe95Scasper case EBADF: 316*ddf7fe95Scasper /* Bad door, don't call it again. */ 317*ddf7fe95Scasper (void) klpd_unreg_dh(p->klpd_door); 318*ddf7fe95Scasper /* FALLTHROUGH */ 319*ddf7fe95Scasper case EINTR: 320*ddf7fe95Scasper /* Pending signal, nothing we can do. */ 321*ddf7fe95Scasper /* FALLTHROUGH */ 322*ddf7fe95Scasper default: 323*ddf7fe95Scasper kmem_free(klh, klh->klh_len); 324*ddf7fe95Scasper return (-1); 325*ddf7fe95Scasper } 326*ddf7fe95Scasper } 327*ddf7fe95Scasper kmem_free(klh, klh->klh_len); 328*ddf7fe95Scasper /* Bogus return value, must be a failure */ 329*ddf7fe95Scasper if (da.rbuf != (char *)&res) { 330*ddf7fe95Scasper kmem_free(da.rbuf, da.rsize); 331*ddf7fe95Scasper return (-1); 332*ddf7fe95Scasper } 333*ddf7fe95Scasper return (res); 334*ddf7fe95Scasper } 335*ddf7fe95Scasper 336*ddf7fe95Scasper uint32_t klpd_bad_locks; 337*ddf7fe95Scasper 338*ddf7fe95Scasper int 339*ddf7fe95Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) 340*ddf7fe95Scasper { 341*ddf7fe95Scasper klpd_reg_t *p; 342*ddf7fe95Scasper int rv = -1; 343*ddf7fe95Scasper credklpd_t *ckp; 344*ddf7fe95Scasper zone_t *ckzone; 345*ddf7fe95Scasper 346*ddf7fe95Scasper /* 347*ddf7fe95Scasper * These locks must not be held when this code is called; 348*ddf7fe95Scasper * callbacks to userland with these locks held will result 349*ddf7fe95Scasper * in issues. That said, the code at the call sides was 350*ddf7fe95Scasper * restructured not to call with any of the locks held and 351*ddf7fe95Scasper * no policies operate by default on most processes. 352*ddf7fe95Scasper */ 353*ddf7fe95Scasper if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || 354*ddf7fe95Scasper mutex_owned(&curproc->p_crlock)) { 355*ddf7fe95Scasper atomic_add_32(&klpd_bad_locks, 1); 356*ddf7fe95Scasper return (-1); 357*ddf7fe95Scasper } 358*ddf7fe95Scasper 359*ddf7fe95Scasper /* 360*ddf7fe95Scasper * Enforce the limit set for the call process (still). 361*ddf7fe95Scasper */ 362*ddf7fe95Scasper if (!priv_issubset(req, &CR_LPRIV(cr))) 363*ddf7fe95Scasper return (-1); 364*ddf7fe95Scasper 365*ddf7fe95Scasper /* Try 1: get the credential specific klpd */ 366*ddf7fe95Scasper if ((ckp = crgetcrklpd(cr)) != NULL) { 367*ddf7fe95Scasper mutex_enter(&ckp->crkl_lock); 368*ddf7fe95Scasper if ((p = ckp->crkl_reg) != NULL && 369*ddf7fe95Scasper p->klpd_indel == 0 && 370*ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 371*ddf7fe95Scasper klpd_hold(p); 372*ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 373*ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 374*ddf7fe95Scasper mutex_enter(&ckp->crkl_lock); 375*ddf7fe95Scasper klpd_rele(p); 376*ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 377*ddf7fe95Scasper if (rv != -1) 378*ddf7fe95Scasper return (rv == 0 ? 0 : -1); 379*ddf7fe95Scasper } else { 380*ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 381*ddf7fe95Scasper } 382*ddf7fe95Scasper } 383*ddf7fe95Scasper 384*ddf7fe95Scasper /* Try 2: get the project specific klpd */ 385*ddf7fe95Scasper mutex_enter(&klpd_mutex); 386*ddf7fe95Scasper 387*ddf7fe95Scasper if ((p = curproj->kpj_klpd) != NULL) { 388*ddf7fe95Scasper klpd_hold(p); 389*ddf7fe95Scasper mutex_exit(&klpd_mutex); 390*ddf7fe95Scasper if (p->klpd_indel == 0 && 391*ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 392*ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 393*ddf7fe95Scasper } 394*ddf7fe95Scasper mutex_enter(&klpd_mutex); 395*ddf7fe95Scasper klpd_rele(p); 396*ddf7fe95Scasper mutex_exit(&klpd_mutex); 397*ddf7fe95Scasper 398*ddf7fe95Scasper if (rv != -1) 399*ddf7fe95Scasper return (rv == 0 ? 0 : -1); 400*ddf7fe95Scasper } else { 401*ddf7fe95Scasper mutex_exit(&klpd_mutex); 402*ddf7fe95Scasper } 403*ddf7fe95Scasper 404*ddf7fe95Scasper /* Try 3: get the global klpd list */ 405*ddf7fe95Scasper ckzone = crgetzone(cr); 406*ddf7fe95Scasper mutex_enter(&klpd_mutex); 407*ddf7fe95Scasper 408*ddf7fe95Scasper for (p = klpd_list; p != NULL; ) { 409*ddf7fe95Scasper zone_t *kkzone = crgetzone(p->klpd_cred); 410*ddf7fe95Scasper if ((kkzone == &zone0 || kkzone == ckzone) && 411*ddf7fe95Scasper p->klpd_indel == 0 && 412*ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 413*ddf7fe95Scasper klpd_hold(p); 414*ddf7fe95Scasper mutex_exit(&klpd_mutex); 415*ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 416*ddf7fe95Scasper mutex_enter(&klpd_mutex); 417*ddf7fe95Scasper 418*ddf7fe95Scasper p = klpd_rele_next(p); 419*ddf7fe95Scasper 420*ddf7fe95Scasper if (rv != -1) 421*ddf7fe95Scasper break; 422*ddf7fe95Scasper } else { 423*ddf7fe95Scasper p = p->klpd_next; 424*ddf7fe95Scasper } 425*ddf7fe95Scasper } 426*ddf7fe95Scasper mutex_exit(&klpd_mutex); 427*ddf7fe95Scasper return (rv == 0 ? 0 : -1); 428*ddf7fe95Scasper } 429*ddf7fe95Scasper 430*ddf7fe95Scasper /* 431*ddf7fe95Scasper * Register the klpd. 432*ddf7fe95Scasper * If the pid_t passed in is positive, update the registration for 433*ddf7fe95Scasper * the specific process; that is only possible if the process already 434*ddf7fe95Scasper * has a registration on it. This change of registration will affect 435*ddf7fe95Scasper * all processes which share common ancestry. 436*ddf7fe95Scasper * 437*ddf7fe95Scasper * MY_PID (pid 0) can be used to create or change the context for 438*ddf7fe95Scasper * the current process, typically done after fork(). 439*ddf7fe95Scasper * 440*ddf7fe95Scasper * A negative value can be used to register a klpd globally. 441*ddf7fe95Scasper * 442*ddf7fe95Scasper * The per-credential klpd needs to be cleaned up when entering 443*ddf7fe95Scasper * a zone or unsetting the flag. 444*ddf7fe95Scasper */ 445*ddf7fe95Scasper int 446*ddf7fe95Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) 447*ddf7fe95Scasper { 448*ddf7fe95Scasper cred_t *cr = CRED(); 449*ddf7fe95Scasper door_handle_t dh; 450*ddf7fe95Scasper klpd_reg_t *kpd; 451*ddf7fe95Scasper priv_set_t pset; 452*ddf7fe95Scasper door_info_t di; 453*ddf7fe95Scasper credklpd_t *ckp = NULL; 454*ddf7fe95Scasper pid_t pid = -1; 455*ddf7fe95Scasper projid_t proj = -1; 456*ddf7fe95Scasper kproject_t *kpp = NULL; 457*ddf7fe95Scasper 458*ddf7fe95Scasper if (CR_FLAGS(cr) & PRIV_XPOLICY) 459*ddf7fe95Scasper return (set_errno(EINVAL)); 460*ddf7fe95Scasper 461*ddf7fe95Scasper if (copyin(psetbuf, &pset, sizeof (priv_set_t))) 462*ddf7fe95Scasper return (set_errno(EFAULT)); 463*ddf7fe95Scasper 464*ddf7fe95Scasper if (!priv_issubset(&pset, &CR_OEPRIV(cr))) 465*ddf7fe95Scasper return (set_errno(EPERM)); 466*ddf7fe95Scasper 467*ddf7fe95Scasper switch (type) { 468*ddf7fe95Scasper case P_PID: 469*ddf7fe95Scasper pid = (pid_t)id; 470*ddf7fe95Scasper if (pid == P_MYPID) 471*ddf7fe95Scasper pid = curproc->p_pid; 472*ddf7fe95Scasper if (pid == curproc->p_pid) 473*ddf7fe95Scasper ckp = crklpd_alloc(); 474*ddf7fe95Scasper break; 475*ddf7fe95Scasper case P_PROJID: 476*ddf7fe95Scasper proj = (projid_t)id; 477*ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(cr), 478*ddf7fe95Scasper PROJECT_HOLD_FIND); 479*ddf7fe95Scasper if (kpp == NULL) 480*ddf7fe95Scasper return (set_errno(ESRCH)); 481*ddf7fe95Scasper break; 482*ddf7fe95Scasper default: 483*ddf7fe95Scasper return (set_errno(ENOTSUP)); 484*ddf7fe95Scasper } 485*ddf7fe95Scasper 486*ddf7fe95Scasper 487*ddf7fe95Scasper /* 488*ddf7fe95Scasper * Verify the door passed in; it must be a door and we won't 489*ddf7fe95Scasper * allow processes to be called on their own behalf. 490*ddf7fe95Scasper */ 491*ddf7fe95Scasper dh = door_ki_lookup(did); 492*ddf7fe95Scasper if (dh == NULL || door_ki_info(dh, &di) != 0) { 493*ddf7fe95Scasper if (ckp != NULL) 494*ddf7fe95Scasper crklpd_rele(ckp); 495*ddf7fe95Scasper if (kpp != NULL) 496*ddf7fe95Scasper project_rele(kpp); 497*ddf7fe95Scasper return (set_errno(EBADF)); 498*ddf7fe95Scasper } 499*ddf7fe95Scasper if (type == P_PID && pid == di.di_target) { 500*ddf7fe95Scasper if (ckp != NULL) 501*ddf7fe95Scasper crklpd_rele(ckp); 502*ddf7fe95Scasper ASSERT(kpp == NULL); 503*ddf7fe95Scasper return (set_errno(EINVAL)); 504*ddf7fe95Scasper } 505*ddf7fe95Scasper 506*ddf7fe95Scasper kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); 507*ddf7fe95Scasper crhold(kpd->klpd_cred = cr); 508*ddf7fe95Scasper kpd->klpd_door = dh; 509*ddf7fe95Scasper kpd->klpd_door_pid = di.di_target; 510*ddf7fe95Scasper kpd->klpd_ref = 1; 511*ddf7fe95Scasper kpd->klpd_pset = pset; 512*ddf7fe95Scasper 513*ddf7fe95Scasper if (kpp != NULL) { 514*ddf7fe95Scasper mutex_enter(&klpd_mutex); 515*ddf7fe95Scasper kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); 516*ddf7fe95Scasper mutex_exit(&klpd_mutex); 517*ddf7fe95Scasper if (kpd != NULL) 518*ddf7fe95Scasper klpd_rele(kpd); 519*ddf7fe95Scasper project_rele(kpp); 520*ddf7fe95Scasper } else if ((int)pid < 0) { 521*ddf7fe95Scasper /* Global daemon */ 522*ddf7fe95Scasper mutex_enter(&klpd_mutex); 523*ddf7fe95Scasper (void) klpd_link(kpd, &klpd_list, B_FALSE); 524*ddf7fe95Scasper mutex_exit(&klpd_mutex); 525*ddf7fe95Scasper } else if (pid == curproc->p_pid) { 526*ddf7fe95Scasper proc_t *p = curproc; 527*ddf7fe95Scasper cred_t *newcr = cralloc(); 528*ddf7fe95Scasper 529*ddf7fe95Scasper /* No need to lock, sole reference to ckp */ 530*ddf7fe95Scasper kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); 531*ddf7fe95Scasper 532*ddf7fe95Scasper if (kpd != NULL) 533*ddf7fe95Scasper klpd_rele(kpd); 534*ddf7fe95Scasper 535*ddf7fe95Scasper mutex_enter(&p->p_crlock); 536*ddf7fe95Scasper cr = p->p_cred; 537*ddf7fe95Scasper crdup_to(cr, newcr); 538*ddf7fe95Scasper crsetcrklpd(newcr, ckp); 539*ddf7fe95Scasper p->p_cred = newcr; /* Already held for p_cred */ 540*ddf7fe95Scasper 541*ddf7fe95Scasper crhold(newcr); /* Hold once for the current thread */ 542*ddf7fe95Scasper mutex_exit(&p->p_crlock); 543*ddf7fe95Scasper crfree(cr); /* One for the p_cred */ 544*ddf7fe95Scasper crset(p, newcr); 545*ddf7fe95Scasper } else { 546*ddf7fe95Scasper proc_t *p; 547*ddf7fe95Scasper cred_t *pcr; 548*ddf7fe95Scasper mutex_enter(&pidlock); 549*ddf7fe95Scasper p = prfind(pid); 550*ddf7fe95Scasper if (p == NULL || !prochasprocperm(p, curproc, CRED())) { 551*ddf7fe95Scasper mutex_exit(&pidlock); 552*ddf7fe95Scasper klpd_rele(kpd); 553*ddf7fe95Scasper return (set_errno(p == NULL ? ESRCH : EPERM)); 554*ddf7fe95Scasper } 555*ddf7fe95Scasper mutex_enter(&p->p_crlock); 556*ddf7fe95Scasper crhold(pcr = p->p_cred); 557*ddf7fe95Scasper mutex_exit(&pidlock); 558*ddf7fe95Scasper mutex_exit(&p->p_crlock); 559*ddf7fe95Scasper /* 560*ddf7fe95Scasper * We're going to update the credential's ckp in place; 561*ddf7fe95Scasper * this requires that it exists. 562*ddf7fe95Scasper */ 563*ddf7fe95Scasper ckp = crgetcrklpd(pcr); 564*ddf7fe95Scasper if (ckp == NULL) { 565*ddf7fe95Scasper crfree(pcr); 566*ddf7fe95Scasper klpd_rele(kpd); 567*ddf7fe95Scasper return (set_errno(EINVAL)); 568*ddf7fe95Scasper } 569*ddf7fe95Scasper crklpd_setreg(ckp, kpd); 570*ddf7fe95Scasper crfree(pcr); 571*ddf7fe95Scasper } 572*ddf7fe95Scasper 573*ddf7fe95Scasper return (0); 574*ddf7fe95Scasper } 575*ddf7fe95Scasper 576*ddf7fe95Scasper static int 577*ddf7fe95Scasper klpd_unreg_dh(door_handle_t dh) 578*ddf7fe95Scasper { 579*ddf7fe95Scasper klpd_reg_t *p; 580*ddf7fe95Scasper 581*ddf7fe95Scasper mutex_enter(&klpd_mutex); 582*ddf7fe95Scasper for (p = klpd_list; p != NULL; p = p->klpd_next) { 583*ddf7fe95Scasper if (p->klpd_door == dh) 584*ddf7fe95Scasper break; 585*ddf7fe95Scasper } 586*ddf7fe95Scasper if (p == NULL) { 587*ddf7fe95Scasper mutex_exit(&klpd_mutex); 588*ddf7fe95Scasper return (EINVAL); 589*ddf7fe95Scasper } 590*ddf7fe95Scasper if (p->klpd_indel != 0) { 591*ddf7fe95Scasper mutex_exit(&klpd_mutex); 592*ddf7fe95Scasper return (EAGAIN); 593*ddf7fe95Scasper } 594*ddf7fe95Scasper p->klpd_indel = 1; 595*ddf7fe95Scasper klpd_rele(p); 596*ddf7fe95Scasper mutex_exit(&klpd_mutex); 597*ddf7fe95Scasper return (0); 598*ddf7fe95Scasper } 599*ddf7fe95Scasper 600*ddf7fe95Scasper int 601*ddf7fe95Scasper klpd_unreg(int did, idtype_t type, id_t id) 602*ddf7fe95Scasper { 603*ddf7fe95Scasper door_handle_t dh; 604*ddf7fe95Scasper int res = 0; 605*ddf7fe95Scasper proc_t *p; 606*ddf7fe95Scasper pid_t pid; 607*ddf7fe95Scasper projid_t proj; 608*ddf7fe95Scasper kproject_t *kpp = NULL; 609*ddf7fe95Scasper credklpd_t *ckp; 610*ddf7fe95Scasper 611*ddf7fe95Scasper switch (type) { 612*ddf7fe95Scasper case P_PID: 613*ddf7fe95Scasper pid = (pid_t)id; 614*ddf7fe95Scasper break; 615*ddf7fe95Scasper case P_PROJID: 616*ddf7fe95Scasper proj = (projid_t)id; 617*ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(CRED()), 618*ddf7fe95Scasper PROJECT_HOLD_FIND); 619*ddf7fe95Scasper if (kpp == NULL) 620*ddf7fe95Scasper return (set_errno(ESRCH)); 621*ddf7fe95Scasper break; 622*ddf7fe95Scasper default: 623*ddf7fe95Scasper return (set_errno(ENOTSUP)); 624*ddf7fe95Scasper } 625*ddf7fe95Scasper 626*ddf7fe95Scasper dh = door_ki_lookup(did); 627*ddf7fe95Scasper if (dh == NULL) { 628*ddf7fe95Scasper if (kpp != NULL) 629*ddf7fe95Scasper project_rele(kpp); 630*ddf7fe95Scasper return (set_errno(EINVAL)); 631*ddf7fe95Scasper } 632*ddf7fe95Scasper 633*ddf7fe95Scasper if (kpp != NULL) { 634*ddf7fe95Scasper mutex_enter(&klpd_mutex); 635*ddf7fe95Scasper if (kpp->kpj_klpd == NULL) 636*ddf7fe95Scasper res = ESRCH; 637*ddf7fe95Scasper else 638*ddf7fe95Scasper klpd_remove(&kpp->kpj_klpd); 639*ddf7fe95Scasper mutex_exit(&klpd_mutex); 640*ddf7fe95Scasper project_rele(kpp); 641*ddf7fe95Scasper goto out; 642*ddf7fe95Scasper } else if ((int)pid > 0) { 643*ddf7fe95Scasper mutex_enter(&pidlock); 644*ddf7fe95Scasper p = prfind(pid); 645*ddf7fe95Scasper if (p == NULL) { 646*ddf7fe95Scasper mutex_exit(&pidlock); 647*ddf7fe95Scasper door_ki_rele(dh); 648*ddf7fe95Scasper return (set_errno(ESRCH)); 649*ddf7fe95Scasper } 650*ddf7fe95Scasper mutex_enter(&p->p_crlock); 651*ddf7fe95Scasper mutex_exit(&pidlock); 652*ddf7fe95Scasper } else if (pid == 0) { 653*ddf7fe95Scasper p = curproc; 654*ddf7fe95Scasper mutex_enter(&p->p_crlock); 655*ddf7fe95Scasper } else { 656*ddf7fe95Scasper res = klpd_unreg_dh(dh); 657*ddf7fe95Scasper goto out; 658*ddf7fe95Scasper } 659*ddf7fe95Scasper 660*ddf7fe95Scasper ckp = crgetcrklpd(p->p_cred); 661*ddf7fe95Scasper if (ckp != NULL) { 662*ddf7fe95Scasper crklpd_setreg(ckp, NULL); 663*ddf7fe95Scasper } else { 664*ddf7fe95Scasper res = ESRCH; 665*ddf7fe95Scasper } 666*ddf7fe95Scasper mutex_exit(&p->p_crlock); 667*ddf7fe95Scasper 668*ddf7fe95Scasper out: 669*ddf7fe95Scasper door_ki_rele(dh); 670*ddf7fe95Scasper 671*ddf7fe95Scasper if (res != 0) 672*ddf7fe95Scasper return (set_errno(res)); 673*ddf7fe95Scasper return (0); 674*ddf7fe95Scasper } 675*ddf7fe95Scasper 676*ddf7fe95Scasper void 677*ddf7fe95Scasper crklpd_hold(credklpd_t *crkpd) 678*ddf7fe95Scasper { 679*ddf7fe95Scasper atomic_add_32(&crkpd->crkl_ref, 1); 680*ddf7fe95Scasper } 681*ddf7fe95Scasper 682*ddf7fe95Scasper void 683*ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd) 684*ddf7fe95Scasper { 685*ddf7fe95Scasper if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) { 686*ddf7fe95Scasper if (crkpd->crkl_reg != NULL) 687*ddf7fe95Scasper klpd_rele(crkpd->crkl_reg); 688*ddf7fe95Scasper mutex_destroy(&crkpd->crkl_lock); 689*ddf7fe95Scasper kmem_free(crkpd, sizeof (*crkpd)); 690*ddf7fe95Scasper } 691*ddf7fe95Scasper } 692*ddf7fe95Scasper 693*ddf7fe95Scasper static credklpd_t * 694*ddf7fe95Scasper crklpd_alloc(void) 695*ddf7fe95Scasper { 696*ddf7fe95Scasper credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP); 697*ddf7fe95Scasper 698*ddf7fe95Scasper mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL); 699*ddf7fe95Scasper res->crkl_ref = 1; 700*ddf7fe95Scasper res->crkl_reg = NULL; 701*ddf7fe95Scasper 702*ddf7fe95Scasper return (res); 703*ddf7fe95Scasper } 704*ddf7fe95Scasper 705*ddf7fe95Scasper void 706*ddf7fe95Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new) 707*ddf7fe95Scasper { 708*ddf7fe95Scasper klpd_reg_t *old; 709*ddf7fe95Scasper 710*ddf7fe95Scasper mutex_enter(&crk->crkl_lock); 711*ddf7fe95Scasper if (new == NULL) { 712*ddf7fe95Scasper old = crk->crkl_reg; 713*ddf7fe95Scasper if (old != NULL) 714*ddf7fe95Scasper klpd_unlink(old); 715*ddf7fe95Scasper } else { 716*ddf7fe95Scasper old = klpd_link(new, &crk->crkl_reg, B_TRUE); 717*ddf7fe95Scasper } 718*ddf7fe95Scasper mutex_exit(&crk->crkl_lock); 719*ddf7fe95Scasper 720*ddf7fe95Scasper if (old != NULL) 721*ddf7fe95Scasper klpd_rele(old); 722*ddf7fe95Scasper } 723