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