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 /* 23*134a1f4eSCasper H.S. Dik * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24ddf7fe95Scasper */ 25ddf7fe95Scasper 26ddf7fe95Scasper #include <sys/atomic.h> 27ddf7fe95Scasper #include <sys/door.h> 28ddf7fe95Scasper #include <sys/proc.h> 29ddf7fe95Scasper #include <sys/cred_impl.h> 30ddf7fe95Scasper #include <sys/policy.h> 31ddf7fe95Scasper #include <sys/priv.h> 32ddf7fe95Scasper #include <sys/klpd.h> 33ddf7fe95Scasper #include <sys/errno.h> 34ddf7fe95Scasper #include <sys/kmem.h> 35ddf7fe95Scasper #include <sys/project.h> 36ddf7fe95Scasper #include <sys/systm.h> 37ddf7fe95Scasper #include <sys/sysmacros.h> 38ddf7fe95Scasper #include <sys/pathname.h> 39ddf7fe95Scasper #include <sys/varargs.h> 40ddf7fe95Scasper #include <sys/zone.h> 41ddf7fe95Scasper #include <netinet/in.h> 42ddf7fe95Scasper 43ddf7fe95Scasper #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1)) 44ddf7fe95Scasper 45ddf7fe95Scasper static kmutex_t klpd_mutex; 46ddf7fe95Scasper 47ddf7fe95Scasper typedef struct klpd_reg { 48ddf7fe95Scasper struct klpd_reg *klpd_next; 49ddf7fe95Scasper struct klpd_reg **klpd_refp; 50ddf7fe95Scasper door_handle_t klpd_door; 51ddf7fe95Scasper pid_t klpd_door_pid; 52ddf7fe95Scasper priv_set_t klpd_pset; 53ddf7fe95Scasper cred_t *klpd_cred; 54ddf7fe95Scasper int klpd_indel; /* Disabled */ 55ddf7fe95Scasper uint32_t klpd_ref; 56ddf7fe95Scasper } klpd_reg_t; 57ddf7fe95Scasper 58ddf7fe95Scasper 59ddf7fe95Scasper /* 60ddf7fe95Scasper * This data structure hangs off the credential of a process; the 61ddf7fe95Scasper * credential is finalized and cannot be changed; but this structure 62ddf7fe95Scasper * can be changed when a new door server for the particular group 63ddf7fe95Scasper * needs to be registered. It is refcounted and shared between 64ddf7fe95Scasper * processes with common ancestry. 65ddf7fe95Scasper * 66ddf7fe95Scasper * The reference count is atomically updated. 67ddf7fe95Scasper * 68ddf7fe95Scasper * But the registration probably needs to be updated under a lock. 69ddf7fe95Scasper */ 70ddf7fe95Scasper typedef struct credklpd { 71ddf7fe95Scasper kmutex_t crkl_lock; 72ddf7fe95Scasper klpd_reg_t *crkl_reg; 73ddf7fe95Scasper uint32_t crkl_ref; 74ddf7fe95Scasper } credklpd_t; 75ddf7fe95Scasper 76ddf7fe95Scasper klpd_reg_t *klpd_list; 77ddf7fe95Scasper 78ddf7fe95Scasper static void klpd_unlink(klpd_reg_t *); 79ddf7fe95Scasper static int klpd_unreg_dh(door_handle_t); 80ddf7fe95Scasper 81ddf7fe95Scasper static credklpd_t *crklpd_alloc(void); 82ddf7fe95Scasper 83ddf7fe95Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *); 84ddf7fe95Scasper 85ddf7fe95Scasper extern size_t max_vnode_path; 86ddf7fe95Scasper 87ddf7fe95Scasper void 88ddf7fe95Scasper klpd_rele(klpd_reg_t *p) 89ddf7fe95Scasper { 90ddf7fe95Scasper if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) { 91ddf7fe95Scasper if (p->klpd_refp != NULL) 92ddf7fe95Scasper klpd_unlink(p); 93ddf7fe95Scasper if (p->klpd_cred != NULL) 94ddf7fe95Scasper crfree(p->klpd_cred); 95ddf7fe95Scasper door_ki_rele(p->klpd_door); 96ddf7fe95Scasper kmem_free(p, sizeof (*p)); 97ddf7fe95Scasper } 98ddf7fe95Scasper } 99ddf7fe95Scasper 100ddf7fe95Scasper /* 101ddf7fe95Scasper * In order to be able to walk the lists, we can't unlink the entry 102ddf7fe95Scasper * until the reference count drops to 0. If we remove it too soon, 103ddf7fe95Scasper * list walkers will terminate when they happen to call a now orphaned 104ddf7fe95Scasper * entry. 105ddf7fe95Scasper */ 106ddf7fe95Scasper static klpd_reg_t * 107ddf7fe95Scasper klpd_rele_next(klpd_reg_t *p) 108ddf7fe95Scasper { 109ddf7fe95Scasper klpd_reg_t *r = p->klpd_next; 110ddf7fe95Scasper 111ddf7fe95Scasper klpd_rele(p); 112ddf7fe95Scasper return (r); 113ddf7fe95Scasper } 114ddf7fe95Scasper 115ddf7fe95Scasper 116ddf7fe95Scasper static void 117ddf7fe95Scasper klpd_hold(klpd_reg_t *p) 118ddf7fe95Scasper { 119ddf7fe95Scasper atomic_add_32(&p->klpd_ref, 1); 120ddf7fe95Scasper } 121ddf7fe95Scasper 122ddf7fe95Scasper /* 123ddf7fe95Scasper * Remove registration from where it is registered. Returns next in list. 124ddf7fe95Scasper */ 125ddf7fe95Scasper static void 126ddf7fe95Scasper klpd_unlink(klpd_reg_t *p) 127ddf7fe95Scasper { 128ddf7fe95Scasper ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p); 129ddf7fe95Scasper 130ddf7fe95Scasper if (p->klpd_refp != NULL) 131ddf7fe95Scasper *p->klpd_refp = p->klpd_next; 132ddf7fe95Scasper 133ddf7fe95Scasper if (p->klpd_next != NULL) 134ddf7fe95Scasper p->klpd_next->klpd_refp = p->klpd_refp; 135ddf7fe95Scasper p->klpd_refp = NULL; 136ddf7fe95Scasper } 137ddf7fe95Scasper 138ddf7fe95Scasper /* 139*134a1f4eSCasper H.S. Dik * Remove all elements of the klpd list and decrement their refcnts. 140ddf7fe95Scasper * The lock guarding the list should be held; this function is 141*134a1f4eSCasper H.S. Dik * called when we are sure we want to destroy the list completely 142*134a1f4eSCasper H.S. Dik * list but not so sure that the reference counts of all elements have 143*134a1f4eSCasper H.S. Dik * dropped back to 1. 144ddf7fe95Scasper */ 145ddf7fe95Scasper void 146*134a1f4eSCasper H.S. Dik klpd_freelist(klpd_reg_t **pp) 147ddf7fe95Scasper { 148*134a1f4eSCasper H.S. Dik klpd_reg_t *p; 149*134a1f4eSCasper H.S. Dik 150*134a1f4eSCasper H.S. Dik while ((p = *pp) != NULL) { 151*134a1f4eSCasper H.S. Dik klpd_unlink(p); 152*134a1f4eSCasper H.S. Dik klpd_rele(p); 153*134a1f4eSCasper H.S. Dik } 154ddf7fe95Scasper } 155ddf7fe95Scasper 156ddf7fe95Scasper /* 157ddf7fe95Scasper * Link new entry in list. The Boolean argument specifies whether this 158ddf7fe95Scasper * list can contain only a single item or multiple items. 159ddf7fe95Scasper * Returns the entry which needs to be released if single is B_TRUE. 160ddf7fe95Scasper */ 161ddf7fe95Scasper static klpd_reg_t * 162ddf7fe95Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single) 163ddf7fe95Scasper { 164ddf7fe95Scasper klpd_reg_t *old = *listp; 165ddf7fe95Scasper 166ddf7fe95Scasper ASSERT(p->klpd_ref == 1); 167ddf7fe95Scasper 168ddf7fe95Scasper ASSERT(old == NULL || *old->klpd_refp == old); 169ddf7fe95Scasper p->klpd_refp = listp; 170ddf7fe95Scasper p->klpd_next = single ? NULL : old; 171ddf7fe95Scasper *listp = p; 172ddf7fe95Scasper if (old != NULL) { 173ddf7fe95Scasper if (single) { 174ddf7fe95Scasper ASSERT(old->klpd_next == NULL); 175ddf7fe95Scasper old->klpd_refp = NULL; 176ddf7fe95Scasper return (old); 177ddf7fe95Scasper } else 178ddf7fe95Scasper old->klpd_refp = &p->klpd_next; 179ddf7fe95Scasper } 180ddf7fe95Scasper return (NULL); 181ddf7fe95Scasper } 182ddf7fe95Scasper 183ddf7fe95Scasper /* 184ddf7fe95Scasper * The typical call consists of: 185ddf7fe95Scasper * - priv_set_t 186ddf7fe95Scasper * - some integer data (type, value) 187ddf7fe95Scasper * for now, it's just one bit. 188ddf7fe95Scasper */ 189ddf7fe95Scasper static klpd_head_t * 190ddf7fe95Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap) 191ddf7fe95Scasper { 192*134a1f4eSCasper H.S. Dik char *tmp; 193ddf7fe95Scasper uint_t type; 194ddf7fe95Scasper vnode_t *vp; 195ddf7fe95Scasper size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t); 196ddf7fe95Scasper size_t plen, clen; 197ddf7fe95Scasper int proto; 198ddf7fe95Scasper 199ddf7fe95Scasper klpd_arg_t *kap = NULL; 200ddf7fe95Scasper klpd_head_t *khp; 201ddf7fe95Scasper 202ddf7fe95Scasper type = va_arg(ap, uint_t); 203ddf7fe95Scasper switch (type) { 204ddf7fe95Scasper case KLPDARG_NOMORE: 205ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 206ddf7fe95Scasper khp->klh_argoff = 0; 207ddf7fe95Scasper break; 208ddf7fe95Scasper case KLPDARG_VNODE: 209ddf7fe95Scasper len += offsetof(klpd_arg_t, kla_str); 210ddf7fe95Scasper vp = va_arg(ap, vnode_t *); 211ddf7fe95Scasper if (vp == NULL) 212ddf7fe95Scasper return (NULL); 213ddf7fe95Scasper 214*134a1f4eSCasper H.S. Dik tmp = va_arg(ap, char *); 215ddf7fe95Scasper 216*134a1f4eSCasper H.S. Dik if (tmp != NULL && *tmp != '\0') 217*134a1f4eSCasper H.S. Dik clen = strlen(tmp) + 1; 218ddf7fe95Scasper else 219ddf7fe95Scasper clen = 0; 220ddf7fe95Scasper 221ddf7fe95Scasper len += ROUNDUP(MAXPATHLEN, sizeof (uint_t)); 222ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 223ddf7fe95Scasper 224ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 225ddf7fe95Scasper kap = KLH_ARG(khp); 226ddf7fe95Scasper 227ddf7fe95Scasper if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp, 228ddf7fe95Scasper vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) { 229ddf7fe95Scasper kmem_free(khp, len); 230ddf7fe95Scasper return (NULL); 231ddf7fe95Scasper } 232ddf7fe95Scasper if (clen != 0) { 233ddf7fe95Scasper plen = strlen(kap->kla_str); 234ddf7fe95Scasper if (plen + clen + 1 >= MAXPATHLEN) { 235ddf7fe95Scasper kmem_free(khp, len); 236ddf7fe95Scasper return (NULL); 237ddf7fe95Scasper } 238ddf7fe95Scasper /* Don't make root into a double "/" */ 239ddf7fe95Scasper if (plen <= 2) 240ddf7fe95Scasper plen = 0; 241ddf7fe95Scasper kap->kla_str[plen] = '/'; 242*134a1f4eSCasper H.S. Dik bcopy(tmp, &kap->kla_str[plen + 1], clen); 243ddf7fe95Scasper } 244ddf7fe95Scasper break; 245ddf7fe95Scasper case KLPDARG_PORT: 246ddf7fe95Scasper proto = va_arg(ap, int); 247ddf7fe95Scasper switch (proto) { 248ddf7fe95Scasper case IPPROTO_TCP: type = KLPDARG_TCPPORT; 249ddf7fe95Scasper break; 250ddf7fe95Scasper case IPPROTO_UDP: type = KLPDARG_UDPPORT; 251ddf7fe95Scasper break; 252ddf7fe95Scasper case IPPROTO_SCTP: type = KLPDARG_SCTPPORT; 253ddf7fe95Scasper break; 254ddf7fe95Scasper case PROTO_SDP: type = KLPDARG_SDPPORT; 255ddf7fe95Scasper break; 256ddf7fe95Scasper } 257ddf7fe95Scasper /* FALLTHROUGH */ 258ddf7fe95Scasper case KLPDARG_INT: 259ddf7fe95Scasper case KLPDARG_TCPPORT: 260ddf7fe95Scasper case KLPDARG_UDPPORT: 261ddf7fe95Scasper case KLPDARG_SCTPPORT: 262ddf7fe95Scasper case KLPDARG_SDPPORT: 263ddf7fe95Scasper len += sizeof (*kap); 264ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP); 265ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 266ddf7fe95Scasper kap = KLH_ARG(khp); 267ddf7fe95Scasper kap->kla_int = va_arg(ap, int); 268ddf7fe95Scasper break; 269ddf7fe95Scasper default: 270ddf7fe95Scasper return (NULL); 271ddf7fe95Scasper } 272ddf7fe95Scasper khp->klh_vers = KLPDCALL_VERS; 273ddf7fe95Scasper khp->klh_len = len; 274ddf7fe95Scasper khp->klh_privoff = sizeof (*khp); 275ddf7fe95Scasper *KLH_PRIVSET(khp) = *rq; 276ddf7fe95Scasper if (kap != NULL) { 277ddf7fe95Scasper kap->kla_type = type; 278ddf7fe95Scasper kap->kla_dlen = len - khp->klh_argoff; 279ddf7fe95Scasper } 280ddf7fe95Scasper return (khp); 281ddf7fe95Scasper } 282ddf7fe95Scasper 283ddf7fe95Scasper static int 284ddf7fe95Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap) 285ddf7fe95Scasper { 286ddf7fe95Scasper door_arg_t da; 287ddf7fe95Scasper int res; 288ddf7fe95Scasper int dres; 289ddf7fe95Scasper klpd_head_t *klh; 290ddf7fe95Scasper 291ddf7fe95Scasper if (p->klpd_door_pid == curproc->p_pid) 292ddf7fe95Scasper return (-1); 293ddf7fe95Scasper 294ddf7fe95Scasper klh = klpd_marshall(p, req, ap); 295ddf7fe95Scasper 296ddf7fe95Scasper if (klh == NULL) 297ddf7fe95Scasper return (-1); 298ddf7fe95Scasper 299ddf7fe95Scasper da.data_ptr = (char *)klh; 300ddf7fe95Scasper da.data_size = klh->klh_len; 301ddf7fe95Scasper da.desc_ptr = NULL; 302ddf7fe95Scasper da.desc_num = 0; 303ddf7fe95Scasper da.rbuf = (char *)&res; 304ddf7fe95Scasper da.rsize = sizeof (res); 305ddf7fe95Scasper 306323a81d9Sjwadams while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL, 307323a81d9Sjwadams SIZE_MAX, 0)) != 0) { 308ddf7fe95Scasper switch (dres) { 309ddf7fe95Scasper case EAGAIN: 310ddf7fe95Scasper delay(1); 311ddf7fe95Scasper continue; 312ddf7fe95Scasper case EINVAL: 313ddf7fe95Scasper case EBADF: 314ddf7fe95Scasper /* Bad door, don't call it again. */ 315ddf7fe95Scasper (void) klpd_unreg_dh(p->klpd_door); 316ddf7fe95Scasper /* FALLTHROUGH */ 317ddf7fe95Scasper case EINTR: 318ddf7fe95Scasper /* Pending signal, nothing we can do. */ 319ddf7fe95Scasper /* FALLTHROUGH */ 320ddf7fe95Scasper default: 321ddf7fe95Scasper kmem_free(klh, klh->klh_len); 322ddf7fe95Scasper return (-1); 323ddf7fe95Scasper } 324ddf7fe95Scasper } 325ddf7fe95Scasper kmem_free(klh, klh->klh_len); 326ddf7fe95Scasper /* Bogus return value, must be a failure */ 327ddf7fe95Scasper if (da.rbuf != (char *)&res) { 328ddf7fe95Scasper kmem_free(da.rbuf, da.rsize); 329ddf7fe95Scasper return (-1); 330ddf7fe95Scasper } 331ddf7fe95Scasper return (res); 332ddf7fe95Scasper } 333ddf7fe95Scasper 334ddf7fe95Scasper uint32_t klpd_bad_locks; 335ddf7fe95Scasper 336ddf7fe95Scasper int 337ddf7fe95Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) 338ddf7fe95Scasper { 339ddf7fe95Scasper klpd_reg_t *p; 340ddf7fe95Scasper int rv = -1; 341ddf7fe95Scasper credklpd_t *ckp; 342ddf7fe95Scasper zone_t *ckzone; 343ddf7fe95Scasper 344ddf7fe95Scasper /* 345ddf7fe95Scasper * These locks must not be held when this code is called; 346ddf7fe95Scasper * callbacks to userland with these locks held will result 347ddf7fe95Scasper * in issues. That said, the code at the call sides was 348ddf7fe95Scasper * restructured not to call with any of the locks held and 349ddf7fe95Scasper * no policies operate by default on most processes. 350ddf7fe95Scasper */ 351ddf7fe95Scasper if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || 352ddf7fe95Scasper mutex_owned(&curproc->p_crlock)) { 353ddf7fe95Scasper atomic_add_32(&klpd_bad_locks, 1); 354ddf7fe95Scasper return (-1); 355ddf7fe95Scasper } 356ddf7fe95Scasper 357ddf7fe95Scasper /* 358ddf7fe95Scasper * Enforce the limit set for the call process (still). 359ddf7fe95Scasper */ 360ddf7fe95Scasper if (!priv_issubset(req, &CR_LPRIV(cr))) 361ddf7fe95Scasper return (-1); 362ddf7fe95Scasper 363ddf7fe95Scasper /* Try 1: get the credential specific klpd */ 364ddf7fe95Scasper if ((ckp = crgetcrklpd(cr)) != NULL) { 365ddf7fe95Scasper mutex_enter(&ckp->crkl_lock); 366ddf7fe95Scasper if ((p = ckp->crkl_reg) != NULL && 367ddf7fe95Scasper p->klpd_indel == 0 && 368ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 369ddf7fe95Scasper klpd_hold(p); 370ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 371ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 372ddf7fe95Scasper mutex_enter(&ckp->crkl_lock); 373ddf7fe95Scasper klpd_rele(p); 374ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 375ddf7fe95Scasper if (rv != -1) 376ddf7fe95Scasper return (rv == 0 ? 0 : -1); 377ddf7fe95Scasper } else { 378ddf7fe95Scasper mutex_exit(&ckp->crkl_lock); 379ddf7fe95Scasper } 380ddf7fe95Scasper } 381ddf7fe95Scasper 382ddf7fe95Scasper /* Try 2: get the project specific klpd */ 383ddf7fe95Scasper mutex_enter(&klpd_mutex); 384ddf7fe95Scasper 385ddf7fe95Scasper if ((p = curproj->kpj_klpd) != NULL) { 386ddf7fe95Scasper klpd_hold(p); 387ddf7fe95Scasper mutex_exit(&klpd_mutex); 388ddf7fe95Scasper if (p->klpd_indel == 0 && 389ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 390ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 391ddf7fe95Scasper } 392ddf7fe95Scasper mutex_enter(&klpd_mutex); 393ddf7fe95Scasper klpd_rele(p); 394ddf7fe95Scasper mutex_exit(&klpd_mutex); 395ddf7fe95Scasper 396ddf7fe95Scasper if (rv != -1) 397ddf7fe95Scasper return (rv == 0 ? 0 : -1); 398ddf7fe95Scasper } else { 399ddf7fe95Scasper mutex_exit(&klpd_mutex); 400ddf7fe95Scasper } 401ddf7fe95Scasper 402ddf7fe95Scasper /* Try 3: get the global klpd list */ 403ddf7fe95Scasper ckzone = crgetzone(cr); 404ddf7fe95Scasper mutex_enter(&klpd_mutex); 405ddf7fe95Scasper 406ddf7fe95Scasper for (p = klpd_list; p != NULL; ) { 407ddf7fe95Scasper zone_t *kkzone = crgetzone(p->klpd_cred); 408ddf7fe95Scasper if ((kkzone == &zone0 || kkzone == ckzone) && 409ddf7fe95Scasper p->klpd_indel == 0 && 410ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) { 411ddf7fe95Scasper klpd_hold(p); 412ddf7fe95Scasper mutex_exit(&klpd_mutex); 413ddf7fe95Scasper rv = klpd_do_call(p, req, ap); 414ddf7fe95Scasper mutex_enter(&klpd_mutex); 415ddf7fe95Scasper 416ddf7fe95Scasper p = klpd_rele_next(p); 417ddf7fe95Scasper 418ddf7fe95Scasper if (rv != -1) 419ddf7fe95Scasper break; 420ddf7fe95Scasper } else { 421ddf7fe95Scasper p = p->klpd_next; 422ddf7fe95Scasper } 423ddf7fe95Scasper } 424ddf7fe95Scasper mutex_exit(&klpd_mutex); 425ddf7fe95Scasper return (rv == 0 ? 0 : -1); 426ddf7fe95Scasper } 427ddf7fe95Scasper 428ddf7fe95Scasper /* 429ddf7fe95Scasper * Register the klpd. 430ddf7fe95Scasper * If the pid_t passed in is positive, update the registration for 431ddf7fe95Scasper * the specific process; that is only possible if the process already 432ddf7fe95Scasper * has a registration on it. This change of registration will affect 433ddf7fe95Scasper * all processes which share common ancestry. 434ddf7fe95Scasper * 435ddf7fe95Scasper * MY_PID (pid 0) can be used to create or change the context for 436ddf7fe95Scasper * the current process, typically done after fork(). 437ddf7fe95Scasper * 438ddf7fe95Scasper * A negative value can be used to register a klpd globally. 439ddf7fe95Scasper * 440ddf7fe95Scasper * The per-credential klpd needs to be cleaned up when entering 441ddf7fe95Scasper * a zone or unsetting the flag. 442ddf7fe95Scasper */ 443ddf7fe95Scasper int 444ddf7fe95Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) 445ddf7fe95Scasper { 446ddf7fe95Scasper cred_t *cr = CRED(); 447ddf7fe95Scasper door_handle_t dh; 448ddf7fe95Scasper klpd_reg_t *kpd; 449ddf7fe95Scasper priv_set_t pset; 450ddf7fe95Scasper door_info_t di; 451ddf7fe95Scasper credklpd_t *ckp = NULL; 452ddf7fe95Scasper pid_t pid = -1; 453ddf7fe95Scasper projid_t proj = -1; 454ddf7fe95Scasper kproject_t *kpp = NULL; 455ddf7fe95Scasper 456ddf7fe95Scasper if (CR_FLAGS(cr) & PRIV_XPOLICY) 457ddf7fe95Scasper return (set_errno(EINVAL)); 458ddf7fe95Scasper 459ddf7fe95Scasper if (copyin(psetbuf, &pset, sizeof (priv_set_t))) 460ddf7fe95Scasper return (set_errno(EFAULT)); 461ddf7fe95Scasper 462ddf7fe95Scasper if (!priv_issubset(&pset, &CR_OEPRIV(cr))) 463ddf7fe95Scasper return (set_errno(EPERM)); 464ddf7fe95Scasper 465ddf7fe95Scasper switch (type) { 466ddf7fe95Scasper case P_PID: 467ddf7fe95Scasper pid = (pid_t)id; 468ddf7fe95Scasper if (pid == P_MYPID) 469ddf7fe95Scasper pid = curproc->p_pid; 470ddf7fe95Scasper if (pid == curproc->p_pid) 471ddf7fe95Scasper ckp = crklpd_alloc(); 472ddf7fe95Scasper break; 473ddf7fe95Scasper case P_PROJID: 474ddf7fe95Scasper proj = (projid_t)id; 475ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(cr), 476ddf7fe95Scasper PROJECT_HOLD_FIND); 477ddf7fe95Scasper if (kpp == NULL) 478ddf7fe95Scasper return (set_errno(ESRCH)); 479ddf7fe95Scasper break; 480ddf7fe95Scasper default: 481ddf7fe95Scasper return (set_errno(ENOTSUP)); 482ddf7fe95Scasper } 483ddf7fe95Scasper 484ddf7fe95Scasper 485ddf7fe95Scasper /* 486ddf7fe95Scasper * Verify the door passed in; it must be a door and we won't 487ddf7fe95Scasper * allow processes to be called on their own behalf. 488ddf7fe95Scasper */ 489ddf7fe95Scasper dh = door_ki_lookup(did); 490ddf7fe95Scasper if (dh == NULL || door_ki_info(dh, &di) != 0) { 491ddf7fe95Scasper if (ckp != NULL) 492ddf7fe95Scasper crklpd_rele(ckp); 493ddf7fe95Scasper if (kpp != NULL) 494ddf7fe95Scasper project_rele(kpp); 495ddf7fe95Scasper return (set_errno(EBADF)); 496ddf7fe95Scasper } 497ddf7fe95Scasper if (type == P_PID && pid == di.di_target) { 498ddf7fe95Scasper if (ckp != NULL) 499ddf7fe95Scasper crklpd_rele(ckp); 500ddf7fe95Scasper ASSERT(kpp == NULL); 501ddf7fe95Scasper return (set_errno(EINVAL)); 502ddf7fe95Scasper } 503ddf7fe95Scasper 504ddf7fe95Scasper kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); 505ddf7fe95Scasper crhold(kpd->klpd_cred = cr); 506ddf7fe95Scasper kpd->klpd_door = dh; 507ddf7fe95Scasper kpd->klpd_door_pid = di.di_target; 508ddf7fe95Scasper kpd->klpd_ref = 1; 509ddf7fe95Scasper kpd->klpd_pset = pset; 510ddf7fe95Scasper 511ddf7fe95Scasper if (kpp != NULL) { 512ddf7fe95Scasper mutex_enter(&klpd_mutex); 513ddf7fe95Scasper kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); 514ddf7fe95Scasper mutex_exit(&klpd_mutex); 515ddf7fe95Scasper if (kpd != NULL) 516ddf7fe95Scasper klpd_rele(kpd); 517ddf7fe95Scasper project_rele(kpp); 518ddf7fe95Scasper } else if ((int)pid < 0) { 519ddf7fe95Scasper /* Global daemon */ 520ddf7fe95Scasper mutex_enter(&klpd_mutex); 521ddf7fe95Scasper (void) klpd_link(kpd, &klpd_list, B_FALSE); 522ddf7fe95Scasper mutex_exit(&klpd_mutex); 523ddf7fe95Scasper } else if (pid == curproc->p_pid) { 524ddf7fe95Scasper proc_t *p = curproc; 525ddf7fe95Scasper cred_t *newcr = cralloc(); 526ddf7fe95Scasper 527ddf7fe95Scasper /* No need to lock, sole reference to ckp */ 528ddf7fe95Scasper kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); 529ddf7fe95Scasper 530ddf7fe95Scasper if (kpd != NULL) 531ddf7fe95Scasper klpd_rele(kpd); 532ddf7fe95Scasper 533ddf7fe95Scasper mutex_enter(&p->p_crlock); 534ddf7fe95Scasper cr = p->p_cred; 535ddf7fe95Scasper crdup_to(cr, newcr); 536ddf7fe95Scasper crsetcrklpd(newcr, ckp); 537ddf7fe95Scasper p->p_cred = newcr; /* Already held for p_cred */ 538ddf7fe95Scasper 539ddf7fe95Scasper crhold(newcr); /* Hold once for the current thread */ 540ddf7fe95Scasper mutex_exit(&p->p_crlock); 541ddf7fe95Scasper crfree(cr); /* One for the p_cred */ 542ddf7fe95Scasper crset(p, newcr); 543ddf7fe95Scasper } else { 544ddf7fe95Scasper proc_t *p; 545ddf7fe95Scasper cred_t *pcr; 546ddf7fe95Scasper mutex_enter(&pidlock); 547ddf7fe95Scasper p = prfind(pid); 548ddf7fe95Scasper if (p == NULL || !prochasprocperm(p, curproc, CRED())) { 549ddf7fe95Scasper mutex_exit(&pidlock); 550ddf7fe95Scasper klpd_rele(kpd); 551ddf7fe95Scasper return (set_errno(p == NULL ? ESRCH : EPERM)); 552ddf7fe95Scasper } 553ddf7fe95Scasper mutex_enter(&p->p_crlock); 554ddf7fe95Scasper crhold(pcr = p->p_cred); 555ddf7fe95Scasper mutex_exit(&pidlock); 556ddf7fe95Scasper mutex_exit(&p->p_crlock); 557ddf7fe95Scasper /* 558ddf7fe95Scasper * We're going to update the credential's ckp in place; 559ddf7fe95Scasper * this requires that it exists. 560ddf7fe95Scasper */ 561ddf7fe95Scasper ckp = crgetcrklpd(pcr); 562ddf7fe95Scasper if (ckp == NULL) { 563ddf7fe95Scasper crfree(pcr); 564ddf7fe95Scasper klpd_rele(kpd); 565ddf7fe95Scasper return (set_errno(EINVAL)); 566ddf7fe95Scasper } 567ddf7fe95Scasper crklpd_setreg(ckp, kpd); 568ddf7fe95Scasper crfree(pcr); 569ddf7fe95Scasper } 570ddf7fe95Scasper 571ddf7fe95Scasper return (0); 572ddf7fe95Scasper } 573ddf7fe95Scasper 574ddf7fe95Scasper static int 575ddf7fe95Scasper klpd_unreg_dh(door_handle_t dh) 576ddf7fe95Scasper { 577ddf7fe95Scasper klpd_reg_t *p; 578ddf7fe95Scasper 579ddf7fe95Scasper mutex_enter(&klpd_mutex); 580ddf7fe95Scasper for (p = klpd_list; p != NULL; p = p->klpd_next) { 581ddf7fe95Scasper if (p->klpd_door == dh) 582ddf7fe95Scasper break; 583ddf7fe95Scasper } 584ddf7fe95Scasper if (p == NULL) { 585ddf7fe95Scasper mutex_exit(&klpd_mutex); 586ddf7fe95Scasper return (EINVAL); 587ddf7fe95Scasper } 588ddf7fe95Scasper if (p->klpd_indel != 0) { 589ddf7fe95Scasper mutex_exit(&klpd_mutex); 590ddf7fe95Scasper return (EAGAIN); 591ddf7fe95Scasper } 592ddf7fe95Scasper p->klpd_indel = 1; 593ddf7fe95Scasper klpd_rele(p); 594ddf7fe95Scasper mutex_exit(&klpd_mutex); 595ddf7fe95Scasper return (0); 596ddf7fe95Scasper } 597ddf7fe95Scasper 598ddf7fe95Scasper int 599ddf7fe95Scasper klpd_unreg(int did, idtype_t type, id_t id) 600ddf7fe95Scasper { 601ddf7fe95Scasper door_handle_t dh; 602ddf7fe95Scasper int res = 0; 603ddf7fe95Scasper proc_t *p; 604ddf7fe95Scasper pid_t pid; 605ddf7fe95Scasper projid_t proj; 606ddf7fe95Scasper kproject_t *kpp = NULL; 607ddf7fe95Scasper credklpd_t *ckp; 608ddf7fe95Scasper 609ddf7fe95Scasper switch (type) { 610ddf7fe95Scasper case P_PID: 611ddf7fe95Scasper pid = (pid_t)id; 612ddf7fe95Scasper break; 613ddf7fe95Scasper case P_PROJID: 614ddf7fe95Scasper proj = (projid_t)id; 615ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(CRED()), 616ddf7fe95Scasper PROJECT_HOLD_FIND); 617ddf7fe95Scasper if (kpp == NULL) 618ddf7fe95Scasper return (set_errno(ESRCH)); 619ddf7fe95Scasper break; 620ddf7fe95Scasper default: 621ddf7fe95Scasper return (set_errno(ENOTSUP)); 622ddf7fe95Scasper } 623ddf7fe95Scasper 624ddf7fe95Scasper dh = door_ki_lookup(did); 625ddf7fe95Scasper if (dh == NULL) { 626ddf7fe95Scasper if (kpp != NULL) 627ddf7fe95Scasper project_rele(kpp); 628ddf7fe95Scasper return (set_errno(EINVAL)); 629ddf7fe95Scasper } 630ddf7fe95Scasper 631ddf7fe95Scasper if (kpp != NULL) { 632ddf7fe95Scasper mutex_enter(&klpd_mutex); 633ddf7fe95Scasper if (kpp->kpj_klpd == NULL) 634ddf7fe95Scasper res = ESRCH; 635ddf7fe95Scasper else 636*134a1f4eSCasper H.S. Dik klpd_freelist(&kpp->kpj_klpd); 637ddf7fe95Scasper mutex_exit(&klpd_mutex); 638ddf7fe95Scasper project_rele(kpp); 639ddf7fe95Scasper goto out; 640ddf7fe95Scasper } else if ((int)pid > 0) { 641ddf7fe95Scasper mutex_enter(&pidlock); 642ddf7fe95Scasper p = prfind(pid); 643ddf7fe95Scasper if (p == NULL) { 644ddf7fe95Scasper mutex_exit(&pidlock); 645ddf7fe95Scasper door_ki_rele(dh); 646ddf7fe95Scasper return (set_errno(ESRCH)); 647ddf7fe95Scasper } 648ddf7fe95Scasper mutex_enter(&p->p_crlock); 649ddf7fe95Scasper mutex_exit(&pidlock); 650ddf7fe95Scasper } else if (pid == 0) { 651ddf7fe95Scasper p = curproc; 652ddf7fe95Scasper mutex_enter(&p->p_crlock); 653ddf7fe95Scasper } else { 654ddf7fe95Scasper res = klpd_unreg_dh(dh); 655ddf7fe95Scasper goto out; 656ddf7fe95Scasper } 657ddf7fe95Scasper 658ddf7fe95Scasper ckp = crgetcrklpd(p->p_cred); 659ddf7fe95Scasper if (ckp != NULL) { 660ddf7fe95Scasper crklpd_setreg(ckp, NULL); 661ddf7fe95Scasper } else { 662ddf7fe95Scasper res = ESRCH; 663ddf7fe95Scasper } 664ddf7fe95Scasper mutex_exit(&p->p_crlock); 665ddf7fe95Scasper 666ddf7fe95Scasper out: 667ddf7fe95Scasper door_ki_rele(dh); 668ddf7fe95Scasper 669ddf7fe95Scasper if (res != 0) 670ddf7fe95Scasper return (set_errno(res)); 671ddf7fe95Scasper return (0); 672ddf7fe95Scasper } 673ddf7fe95Scasper 674ddf7fe95Scasper void 675ddf7fe95Scasper crklpd_hold(credklpd_t *crkpd) 676ddf7fe95Scasper { 677ddf7fe95Scasper atomic_add_32(&crkpd->crkl_ref, 1); 678ddf7fe95Scasper } 679ddf7fe95Scasper 680ddf7fe95Scasper void 681ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd) 682ddf7fe95Scasper { 683ddf7fe95Scasper if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) { 684ddf7fe95Scasper if (crkpd->crkl_reg != NULL) 685ddf7fe95Scasper klpd_rele(crkpd->crkl_reg); 686ddf7fe95Scasper mutex_destroy(&crkpd->crkl_lock); 687ddf7fe95Scasper kmem_free(crkpd, sizeof (*crkpd)); 688ddf7fe95Scasper } 689ddf7fe95Scasper } 690ddf7fe95Scasper 691ddf7fe95Scasper static credklpd_t * 692ddf7fe95Scasper crklpd_alloc(void) 693ddf7fe95Scasper { 694ddf7fe95Scasper credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP); 695ddf7fe95Scasper 696ddf7fe95Scasper mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL); 697ddf7fe95Scasper res->crkl_ref = 1; 698ddf7fe95Scasper res->crkl_reg = NULL; 699ddf7fe95Scasper 700ddf7fe95Scasper return (res); 701ddf7fe95Scasper } 702ddf7fe95Scasper 703ddf7fe95Scasper void 704ddf7fe95Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new) 705ddf7fe95Scasper { 706ddf7fe95Scasper klpd_reg_t *old; 707ddf7fe95Scasper 708ddf7fe95Scasper mutex_enter(&crk->crkl_lock); 709ddf7fe95Scasper if (new == NULL) { 710ddf7fe95Scasper old = crk->crkl_reg; 711ddf7fe95Scasper if (old != NULL) 712ddf7fe95Scasper klpd_unlink(old); 713ddf7fe95Scasper } else { 714ddf7fe95Scasper old = klpd_link(new, &crk->crkl_reg, B_TRUE); 715ddf7fe95Scasper } 716ddf7fe95Scasper mutex_exit(&crk->crkl_lock); 717ddf7fe95Scasper 718ddf7fe95Scasper if (old != NULL) 719ddf7fe95Scasper klpd_rele(old); 720ddf7fe95Scasper } 721*134a1f4eSCasper H.S. Dik 722*134a1f4eSCasper H.S. Dik /* Allocate and register the pfexec specific callback */ 723*134a1f4eSCasper H.S. Dik int 724*134a1f4eSCasper H.S. Dik pfexec_reg(int did) 725*134a1f4eSCasper H.S. Dik { 726*134a1f4eSCasper H.S. Dik door_handle_t dh; 727*134a1f4eSCasper H.S. Dik int err = secpolicy_pfexec_register(CRED()); 728*134a1f4eSCasper H.S. Dik klpd_reg_t *pfx; 729*134a1f4eSCasper H.S. Dik door_info_t di; 730*134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 731*134a1f4eSCasper H.S. Dik 732*134a1f4eSCasper H.S. Dik if (err != 0) 733*134a1f4eSCasper H.S. Dik return (set_errno(err)); 734*134a1f4eSCasper H.S. Dik 735*134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did); 736*134a1f4eSCasper H.S. Dik if (dh == NULL || door_ki_info(dh, &di) != 0) 737*134a1f4eSCasper H.S. Dik return (set_errno(EBADF)); 738*134a1f4eSCasper H.S. Dik 739*134a1f4eSCasper H.S. Dik pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP); 740*134a1f4eSCasper H.S. Dik 741*134a1f4eSCasper H.S. Dik pfx->klpd_door = dh; 742*134a1f4eSCasper H.S. Dik pfx->klpd_door_pid = di.di_target; 743*134a1f4eSCasper H.S. Dik pfx->klpd_ref = 1; 744*134a1f4eSCasper H.S. Dik pfx->klpd_cred = NULL; 745*134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 746*134a1f4eSCasper H.S. Dik pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE); 747*134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 748*134a1f4eSCasper H.S. Dik if (pfx != NULL) 749*134a1f4eSCasper H.S. Dik klpd_rele(pfx); 750*134a1f4eSCasper H.S. Dik 751*134a1f4eSCasper H.S. Dik return (0); 752*134a1f4eSCasper H.S. Dik } 753*134a1f4eSCasper H.S. Dik 754*134a1f4eSCasper H.S. Dik int 755*134a1f4eSCasper H.S. Dik pfexec_unreg(int did) 756*134a1f4eSCasper H.S. Dik { 757*134a1f4eSCasper H.S. Dik door_handle_t dh; 758*134a1f4eSCasper H.S. Dik int err = 0; 759*134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 760*134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 761*134a1f4eSCasper H.S. Dik 762*134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did); 763*134a1f4eSCasper H.S. Dik if (dh == NULL) 764*134a1f4eSCasper H.S. Dik return (set_errno(EBADF)); 765*134a1f4eSCasper H.S. Dik 766*134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 767*134a1f4eSCasper H.S. Dik pfd = myzone->zone_pfexecd; 768*134a1f4eSCasper H.S. Dik if (pfd != NULL && pfd->klpd_door == dh) { 769*134a1f4eSCasper H.S. Dik klpd_unlink(pfd); 770*134a1f4eSCasper H.S. Dik } else { 771*134a1f4eSCasper H.S. Dik pfd = NULL; 772*134a1f4eSCasper H.S. Dik err = EINVAL; 773*134a1f4eSCasper H.S. Dik } 774*134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 775*134a1f4eSCasper H.S. Dik door_ki_rele(dh); 776*134a1f4eSCasper H.S. Dik /* 777*134a1f4eSCasper H.S. Dik * crfree() cannot be called with zone_lock held; it is called 778*134a1f4eSCasper H.S. Dik * indirectly through closing the door handle 779*134a1f4eSCasper H.S. Dik */ 780*134a1f4eSCasper H.S. Dik if (pfd != NULL) 781*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 782*134a1f4eSCasper H.S. Dik if (err != 0) 783*134a1f4eSCasper H.S. Dik return (set_errno(err)); 784*134a1f4eSCasper H.S. Dik return (0); 785*134a1f4eSCasper H.S. Dik } 786*134a1f4eSCasper H.S. Dik 787*134a1f4eSCasper H.S. Dik static int 788*134a1f4eSCasper H.S. Dik get_path(char *buf, const char *path, int len) 789*134a1f4eSCasper H.S. Dik { 790*134a1f4eSCasper H.S. Dik size_t lc; 791*134a1f4eSCasper H.S. Dik char *s; 792*134a1f4eSCasper H.S. Dik 793*134a1f4eSCasper H.S. Dik if (len < 0) 794*134a1f4eSCasper H.S. Dik len = strlen(path); 795*134a1f4eSCasper H.S. Dik 796*134a1f4eSCasper H.S. Dik if (*path == '/' && len < MAXPATHLEN) { 797*134a1f4eSCasper H.S. Dik (void) strcpy(buf, path); 798*134a1f4eSCasper H.S. Dik return (0); 799*134a1f4eSCasper H.S. Dik } 800*134a1f4eSCasper H.S. Dik /* 801*134a1f4eSCasper H.S. Dik * Build the pathname using the current directory + resolve pathname. 802*134a1f4eSCasper H.S. Dik * The resolve pathname either starts with a normal component and 803*134a1f4eSCasper H.S. Dik * we can just concatenate them or it starts with one 804*134a1f4eSCasper H.S. Dik * or more ".." component and we can remove those; the 805*134a1f4eSCasper H.S. Dik * last one cannot be a ".." and the current directory has 806*134a1f4eSCasper H.S. Dik * more components than the number of ".." in the resolved pathname. 807*134a1f4eSCasper H.S. Dik */ 808*134a1f4eSCasper H.S. Dik if (dogetcwd(buf, MAXPATHLEN) != 0) 809*134a1f4eSCasper H.S. Dik return (-1); 810*134a1f4eSCasper H.S. Dik 811*134a1f4eSCasper H.S. Dik lc = strlen(buf); 812*134a1f4eSCasper H.S. Dik 813*134a1f4eSCasper H.S. Dik while (len > 3 && strncmp("../", path, 3) == 0) { 814*134a1f4eSCasper H.S. Dik len -= 3; 815*134a1f4eSCasper H.S. Dik path += 3; 816*134a1f4eSCasper H.S. Dik 817*134a1f4eSCasper H.S. Dik s = strrchr(buf, '/'); 818*134a1f4eSCasper H.S. Dik if (s == NULL || s == buf) 819*134a1f4eSCasper H.S. Dik return (-1); 820*134a1f4eSCasper H.S. Dik 821*134a1f4eSCasper H.S. Dik *s = '\0'; 822*134a1f4eSCasper H.S. Dik lc = s - buf; 823*134a1f4eSCasper H.S. Dik } 824*134a1f4eSCasper H.S. Dik /* Add a "/" and a NUL */ 825*134a1f4eSCasper H.S. Dik if (lc < 2 || lc + len + 2 >= MAXPATHLEN) 826*134a1f4eSCasper H.S. Dik return (-1); 827*134a1f4eSCasper H.S. Dik 828*134a1f4eSCasper H.S. Dik buf[lc] = '/'; 829*134a1f4eSCasper H.S. Dik (void) strcpy(buf + lc + 1, path); 830*134a1f4eSCasper H.S. Dik 831*134a1f4eSCasper H.S. Dik return (0); 832*134a1f4eSCasper H.S. Dik } 833*134a1f4eSCasper H.S. Dik 834*134a1f4eSCasper H.S. Dik /* 835*134a1f4eSCasper H.S. Dik * Perform the pfexec upcall. 836*134a1f4eSCasper H.S. Dik * 837*134a1f4eSCasper H.S. Dik * The pfexec upcall is different from the klpd_upcall in that a failure 838*134a1f4eSCasper H.S. Dik * will lead to a denial of execution. 839*134a1f4eSCasper H.S. Dik */ 840*134a1f4eSCasper H.S. Dik int 841*134a1f4eSCasper H.S. Dik pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr, 842*134a1f4eSCasper H.S. Dik boolean_t *scrub) 843*134a1f4eSCasper H.S. Dik { 844*134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 845*134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 846*134a1f4eSCasper H.S. Dik pfexec_reply_t pr, *prp; 847*134a1f4eSCasper H.S. Dik door_arg_t da; 848*134a1f4eSCasper H.S. Dik int dres; 849*134a1f4eSCasper H.S. Dik cred_t *ncr = NULL; 850*134a1f4eSCasper H.S. Dik int err = -1; 851*134a1f4eSCasper H.S. Dik priv_set_t *iset; 852*134a1f4eSCasper H.S. Dik priv_set_t *lset; 853*134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 854*134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN); 855*134a1f4eSCasper H.S. Dik 856*134a1f4eSCasper H.S. Dik /* Find registration */ 857*134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 858*134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 859*134a1f4eSCasper H.S. Dik klpd_hold(pfd); 860*134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 861*134a1f4eSCasper H.S. Dik 862*134a1f4eSCasper H.S. Dik if (pfd == NULL) 863*134a1f4eSCasper H.S. Dik return (0); 864*134a1f4eSCasper H.S. Dik 865*134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 866*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 867*134a1f4eSCasper H.S. Dik return (0); 868*134a1f4eSCasper H.S. Dik } 869*134a1f4eSCasper H.S. Dik 870*134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 871*134a1f4eSCasper H.S. Dik 872*134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1) 873*134a1f4eSCasper H.S. Dik goto out1; 874*134a1f4eSCasper H.S. Dik 875*134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 876*134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_EXEC_ATTRS; 877*134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 878*134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr); 879*134a1f4eSCasper H.S. Dik 880*134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 881*134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 882*134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 883*134a1f4eSCasper H.S. Dik da.desc_num = 0; 884*134a1f4eSCasper H.S. Dik da.rbuf = (char *)≺ 885*134a1f4eSCasper H.S. Dik da.rsize = sizeof (pr); 886*134a1f4eSCasper H.S. Dik 887*134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 888*134a1f4eSCasper H.S. Dik switch (dres) { 889*134a1f4eSCasper H.S. Dik case EAGAIN: 890*134a1f4eSCasper H.S. Dik delay(1); 891*134a1f4eSCasper H.S. Dik continue; 892*134a1f4eSCasper H.S. Dik case EINVAL: 893*134a1f4eSCasper H.S. Dik case EBADF: 894*134a1f4eSCasper H.S. Dik /* FALLTHROUGH */ 895*134a1f4eSCasper H.S. Dik case EINTR: 896*134a1f4eSCasper H.S. Dik /* FALLTHROUGH */ 897*134a1f4eSCasper H.S. Dik default: 898*134a1f4eSCasper H.S. Dik goto out; 899*134a1f4eSCasper H.S. Dik } 900*134a1f4eSCasper H.S. Dik } 901*134a1f4eSCasper H.S. Dik 902*134a1f4eSCasper H.S. Dik prp = (pfexec_reply_t *)da.rbuf; 903*134a1f4eSCasper H.S. Dik /* 904*134a1f4eSCasper H.S. Dik * Check the size of the result and the alignment of the 905*134a1f4eSCasper H.S. Dik * privilege sets. 906*134a1f4eSCasper H.S. Dik */ 907*134a1f4eSCasper H.S. Dik if (da.rsize < sizeof (pr) || 908*134a1f4eSCasper H.S. Dik prp->pfr_ioff > da.rsize - sizeof (priv_set_t) || 909*134a1f4eSCasper H.S. Dik prp->pfr_loff > da.rsize - sizeof (priv_set_t) || 910*134a1f4eSCasper H.S. Dik (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 || 911*134a1f4eSCasper H.S. Dik (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0) 912*134a1f4eSCasper H.S. Dik goto out; 913*134a1f4eSCasper H.S. Dik 914*134a1f4eSCasper H.S. Dik /* 915*134a1f4eSCasper H.S. Dik * Get results: 916*134a1f4eSCasper H.S. Dik * allow/allow with additional credentials/disallow[*] 917*134a1f4eSCasper H.S. Dik * 918*134a1f4eSCasper H.S. Dik * euid, uid, egid, gid, privs, and limitprivs 919*134a1f4eSCasper H.S. Dik * We now have somewhat more flexibility we could even set E and P 920*134a1f4eSCasper H.S. Dik * judiciously but that would break some currently valid assumptions 921*134a1f4eSCasper H.S. Dik * [*] Disallow is not readily supported by always including 922*134a1f4eSCasper H.S. Dik * the Basic Solaris User profile in all user's profiles. 923*134a1f4eSCasper H.S. Dik */ 924*134a1f4eSCasper H.S. Dik 925*134a1f4eSCasper H.S. Dik if (!prp->pfr_allowed) { 926*134a1f4eSCasper H.S. Dik err = EACCES; 927*134a1f4eSCasper H.S. Dik goto out; 928*134a1f4eSCasper H.S. Dik } 929*134a1f4eSCasper H.S. Dik if (!prp->pfr_setcred) { 930*134a1f4eSCasper H.S. Dik err = 0; 931*134a1f4eSCasper H.S. Dik goto out; 932*134a1f4eSCasper H.S. Dik } 933*134a1f4eSCasper H.S. Dik ncr = crdup((cred_t *)cr); 934*134a1f4eSCasper H.S. Dik 935*134a1f4eSCasper H.S. Dik /* 936*134a1f4eSCasper H.S. Dik * Generate the new credential set scrubenv if ruid != euid (or set) 937*134a1f4eSCasper H.S. Dik * the "I'm set-uid flag" but that is not inherited so scrubbing 938*134a1f4eSCasper H.S. Dik * the environment is a requirement. 939*134a1f4eSCasper H.S. Dik */ 940*134a1f4eSCasper H.S. Dik /* Set uids or gids, note that -1 will do the right thing */ 941*134a1f4eSCasper H.S. Dik if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0) 942*134a1f4eSCasper H.S. Dik goto out; 943*134a1f4eSCasper H.S. Dik if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0) 944*134a1f4eSCasper H.S. Dik goto out; 945*134a1f4eSCasper H.S. Dik 946*134a1f4eSCasper H.S. Dik *scrub = prp->pfr_scrubenv; 947*134a1f4eSCasper H.S. Dik 948*134a1f4eSCasper H.S. Dik if (prp->pfr_clearflag) 949*134a1f4eSCasper H.S. Dik CR_FLAGS(ncr) &= ~PRIV_PFEXEC; 950*134a1f4eSCasper H.S. Dik 951*134a1f4eSCasper H.S. Dik /* We cannot exceed our Limit set, no matter what */ 952*134a1f4eSCasper H.S. Dik iset = PFEXEC_REPLY_IPRIV(prp); 953*134a1f4eSCasper H.S. Dik 954*134a1f4eSCasper H.S. Dik if (iset != NULL) { 955*134a1f4eSCasper H.S. Dik if (!priv_issubset(iset, &CR_LPRIV(ncr))) 956*134a1f4eSCasper H.S. Dik goto out; 957*134a1f4eSCasper H.S. Dik priv_union(iset, &CR_IPRIV(ncr)); 958*134a1f4eSCasper H.S. Dik } 959*134a1f4eSCasper H.S. Dik 960*134a1f4eSCasper H.S. Dik /* Nor can we increate our Limit set itself */ 961*134a1f4eSCasper H.S. Dik lset = PFEXEC_REPLY_LPRIV(prp); 962*134a1f4eSCasper H.S. Dik 963*134a1f4eSCasper H.S. Dik if (lset != NULL) { 964*134a1f4eSCasper H.S. Dik if (!priv_issubset(lset, &CR_LPRIV(ncr))) 965*134a1f4eSCasper H.S. Dik goto out; 966*134a1f4eSCasper H.S. Dik CR_LPRIV(ncr) = *lset; 967*134a1f4eSCasper H.S. Dik } 968*134a1f4eSCasper H.S. Dik 969*134a1f4eSCasper H.S. Dik /* Exec will do the standard set operations */ 970*134a1f4eSCasper H.S. Dik 971*134a1f4eSCasper H.S. Dik err = 0; 972*134a1f4eSCasper H.S. Dik out: 973*134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pr) 974*134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 975*134a1f4eSCasper H.S. Dik out1: 976*134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 977*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 978*134a1f4eSCasper H.S. Dik if (ncr != NULL) { 979*134a1f4eSCasper H.S. Dik if (err == 0) 980*134a1f4eSCasper H.S. Dik *pfcr = ncr; 981*134a1f4eSCasper H.S. Dik else 982*134a1f4eSCasper H.S. Dik crfree(ncr); 983*134a1f4eSCasper H.S. Dik } 984*134a1f4eSCasper H.S. Dik return (err); 985*134a1f4eSCasper H.S. Dik } 986*134a1f4eSCasper H.S. Dik 987*134a1f4eSCasper H.S. Dik int 988*134a1f4eSCasper H.S. Dik get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set) 989*134a1f4eSCasper H.S. Dik { 990*134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 991*134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 992*134a1f4eSCasper H.S. Dik door_arg_t da; 993*134a1f4eSCasper H.S. Dik int dres; 994*134a1f4eSCasper H.S. Dik int err = -1; 995*134a1f4eSCasper H.S. Dik priv_set_t *fset, pmem; 996*134a1f4eSCasper H.S. Dik cred_t *zkcr; 997*134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr); 998*134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN); 999*134a1f4eSCasper H.S. Dik 1000*134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 1001*134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 1002*134a1f4eSCasper H.S. Dik klpd_hold(pfd); 1003*134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 1004*134a1f4eSCasper H.S. Dik 1005*134a1f4eSCasper H.S. Dik if (pfd == NULL) 1006*134a1f4eSCasper H.S. Dik return (-1); 1007*134a1f4eSCasper H.S. Dik 1008*134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 1009*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1010*134a1f4eSCasper H.S. Dik return (0); 1011*134a1f4eSCasper H.S. Dik } 1012*134a1f4eSCasper H.S. Dik 1013*134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 1014*134a1f4eSCasper H.S. Dik 1015*134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, respn, -1) == -1) 1016*134a1f4eSCasper H.S. Dik goto out1; 1017*134a1f4eSCasper H.S. Dik 1018*134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 1019*134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_FORCED_PRIVS; 1020*134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 1021*134a1f4eSCasper H.S. Dik pap->pfa_uid = (uid_t)-1; /* Not relevant */ 1022*134a1f4eSCasper H.S. Dik 1023*134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 1024*134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 1025*134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 1026*134a1f4eSCasper H.S. Dik da.desc_num = 0; 1027*134a1f4eSCasper H.S. Dik da.rbuf = (char *)&pmem; 1028*134a1f4eSCasper H.S. Dik da.rsize = sizeof (pmem); 1029*134a1f4eSCasper H.S. Dik 1030*134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 1031*134a1f4eSCasper H.S. Dik switch (dres) { 1032*134a1f4eSCasper H.S. Dik case EAGAIN: 1033*134a1f4eSCasper H.S. Dik delay(1); 1034*134a1f4eSCasper H.S. Dik continue; 1035*134a1f4eSCasper H.S. Dik case EINVAL: 1036*134a1f4eSCasper H.S. Dik case EBADF: 1037*134a1f4eSCasper H.S. Dik case EINTR: 1038*134a1f4eSCasper H.S. Dik default: 1039*134a1f4eSCasper H.S. Dik goto out; 1040*134a1f4eSCasper H.S. Dik } 1041*134a1f4eSCasper H.S. Dik } 1042*134a1f4eSCasper H.S. Dik 1043*134a1f4eSCasper H.S. Dik /* 1044*134a1f4eSCasper H.S. Dik * Check the size of the result, it's a privilege set. 1045*134a1f4eSCasper H.S. Dik */ 1046*134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (priv_set_t)) 1047*134a1f4eSCasper H.S. Dik goto out; 1048*134a1f4eSCasper H.S. Dik 1049*134a1f4eSCasper H.S. Dik fset = (priv_set_t *)da.rbuf; 1050*134a1f4eSCasper H.S. Dik 1051*134a1f4eSCasper H.S. Dik /* 1052*134a1f4eSCasper H.S. Dik * We restrict the forced privileges with whatever is available in 1053*134a1f4eSCasper H.S. Dik * the current zone. 1054*134a1f4eSCasper H.S. Dik */ 1055*134a1f4eSCasper H.S. Dik zkcr = zone_kcred(); 1056*134a1f4eSCasper H.S. Dik priv_intersect(&CR_LPRIV(zkcr), fset); 1057*134a1f4eSCasper H.S. Dik 1058*134a1f4eSCasper H.S. Dik /* 1059*134a1f4eSCasper H.S. Dik * But we fail if the forced privileges are not found in the current 1060*134a1f4eSCasper H.S. Dik * Limit set. 1061*134a1f4eSCasper H.S. Dik */ 1062*134a1f4eSCasper H.S. Dik if (!priv_issubset(fset, &CR_LPRIV(cr))) { 1063*134a1f4eSCasper H.S. Dik err = EACCES; 1064*134a1f4eSCasper H.S. Dik } else if (!priv_isemptyset(fset)) { 1065*134a1f4eSCasper H.S. Dik err = 0; 1066*134a1f4eSCasper H.S. Dik *set = *fset; 1067*134a1f4eSCasper H.S. Dik } 1068*134a1f4eSCasper H.S. Dik out: 1069*134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pmem) 1070*134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 1071*134a1f4eSCasper H.S. Dik out1: 1072*134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 1073*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1074*134a1f4eSCasper H.S. Dik return (err); 1075*134a1f4eSCasper H.S. Dik } 1076*134a1f4eSCasper H.S. Dik 1077*134a1f4eSCasper H.S. Dik int 1078*134a1f4eSCasper H.S. Dik check_user_privs(const cred_t *cr, const priv_set_t *set) 1079*134a1f4eSCasper H.S. Dik { 1080*134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 1081*134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 1082*134a1f4eSCasper H.S. Dik door_arg_t da; 1083*134a1f4eSCasper H.S. Dik int dres; 1084*134a1f4eSCasper H.S. Dik int err = -1; 1085*134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr); 1086*134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t)); 1087*134a1f4eSCasper H.S. Dik uint32_t res; 1088*134a1f4eSCasper H.S. Dik 1089*134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 1090*134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 1091*134a1f4eSCasper H.S. Dik klpd_hold(pfd); 1092*134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 1093*134a1f4eSCasper H.S. Dik 1094*134a1f4eSCasper H.S. Dik if (pfd == NULL) 1095*134a1f4eSCasper H.S. Dik return (-1); 1096*134a1f4eSCasper H.S. Dik 1097*134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 1098*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1099*134a1f4eSCasper H.S. Dik return (0); 1100*134a1f4eSCasper H.S. Dik } 1101*134a1f4eSCasper H.S. Dik 1102*134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 1103*134a1f4eSCasper H.S. Dik 1104*134a1f4eSCasper H.S. Dik *(priv_set_t *)&pap->pfa_buf = *set; 1105*134a1f4eSCasper H.S. Dik 1106*134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 1107*134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_USER_PRIVS; 1108*134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 1109*134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr); 1110*134a1f4eSCasper H.S. Dik 1111*134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 1112*134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 1113*134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 1114*134a1f4eSCasper H.S. Dik da.desc_num = 0; 1115*134a1f4eSCasper H.S. Dik da.rbuf = (char *)&res; 1116*134a1f4eSCasper H.S. Dik da.rsize = sizeof (res); 1117*134a1f4eSCasper H.S. Dik 1118*134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 1119*134a1f4eSCasper H.S. Dik switch (dres) { 1120*134a1f4eSCasper H.S. Dik case EAGAIN: 1121*134a1f4eSCasper H.S. Dik delay(1); 1122*134a1f4eSCasper H.S. Dik continue; 1123*134a1f4eSCasper H.S. Dik case EINVAL: 1124*134a1f4eSCasper H.S. Dik case EBADF: 1125*134a1f4eSCasper H.S. Dik case EINTR: 1126*134a1f4eSCasper H.S. Dik default: 1127*134a1f4eSCasper H.S. Dik goto out; 1128*134a1f4eSCasper H.S. Dik } 1129*134a1f4eSCasper H.S. Dik } 1130*134a1f4eSCasper H.S. Dik 1131*134a1f4eSCasper H.S. Dik /* 1132*134a1f4eSCasper H.S. Dik * Check the size of the result. 1133*134a1f4eSCasper H.S. Dik */ 1134*134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (res)) 1135*134a1f4eSCasper H.S. Dik goto out; 1136*134a1f4eSCasper H.S. Dik 1137*134a1f4eSCasper H.S. Dik if (*(uint32_t *)da.rbuf == 1) 1138*134a1f4eSCasper H.S. Dik err = 0; 1139*134a1f4eSCasper H.S. Dik out: 1140*134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&res) 1141*134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 1142*134a1f4eSCasper H.S. Dik out1: 1143*134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 1144*134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1145*134a1f4eSCasper H.S. Dik return (err); 1146*134a1f4eSCasper H.S. Dik } 1147