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 /* 23134a1f4eSCasper 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 { 90*1a5e258fSJosef 'Jeff' Sipek if (atomic_dec_32_nv(&p->klpd_ref) == 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 { 119*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&p->klpd_ref); 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 /* 139134a1f4eSCasper 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 141134a1f4eSCasper H.S. Dik * called when we are sure we want to destroy the list completely 142134a1f4eSCasper H.S. Dik * list but not so sure that the reference counts of all elements have 143134a1f4eSCasper H.S. Dik * dropped back to 1. 144ddf7fe95Scasper */ 145ddf7fe95Scasper void 146134a1f4eSCasper H.S. Dik klpd_freelist(klpd_reg_t **pp) 147ddf7fe95Scasper { 148134a1f4eSCasper H.S. Dik klpd_reg_t *p; 149134a1f4eSCasper H.S. Dik 150134a1f4eSCasper H.S. Dik while ((p = *pp) != NULL) { 151134a1f4eSCasper H.S. Dik klpd_unlink(p); 152134a1f4eSCasper H.S. Dik klpd_rele(p); 153134a1f4eSCasper 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 { 192134a1f4eSCasper 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 214134a1f4eSCasper H.S. Dik tmp = va_arg(ap, char *); 215ddf7fe95Scasper 216134a1f4eSCasper H.S. Dik if (tmp != NULL && *tmp != '\0') 217134a1f4eSCasper 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] = '/'; 242134a1f4eSCasper 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)) { 353*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&klpd_bad_locks); 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 636134a1f4eSCasper 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 { 677*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&crkpd->crkl_ref); 678ddf7fe95Scasper } 679ddf7fe95Scasper 680ddf7fe95Scasper void 681ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd) 682ddf7fe95Scasper { 683*1a5e258fSJosef 'Jeff' Sipek if (atomic_dec_32_nv(&crkpd->crkl_ref) == 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 } 721134a1f4eSCasper H.S. Dik 722134a1f4eSCasper H.S. Dik /* Allocate and register the pfexec specific callback */ 723134a1f4eSCasper H.S. Dik int 724134a1f4eSCasper H.S. Dik pfexec_reg(int did) 725134a1f4eSCasper H.S. Dik { 726134a1f4eSCasper H.S. Dik door_handle_t dh; 727134a1f4eSCasper H.S. Dik int err = secpolicy_pfexec_register(CRED()); 728134a1f4eSCasper H.S. Dik klpd_reg_t *pfx; 729134a1f4eSCasper H.S. Dik door_info_t di; 730134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 731134a1f4eSCasper H.S. Dik 732134a1f4eSCasper H.S. Dik if (err != 0) 733134a1f4eSCasper H.S. Dik return (set_errno(err)); 734134a1f4eSCasper H.S. Dik 735134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did); 736134a1f4eSCasper H.S. Dik if (dh == NULL || door_ki_info(dh, &di) != 0) 737134a1f4eSCasper H.S. Dik return (set_errno(EBADF)); 738134a1f4eSCasper H.S. Dik 739134a1f4eSCasper H.S. Dik pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP); 740134a1f4eSCasper H.S. Dik 741134a1f4eSCasper H.S. Dik pfx->klpd_door = dh; 742134a1f4eSCasper H.S. Dik pfx->klpd_door_pid = di.di_target; 743134a1f4eSCasper H.S. Dik pfx->klpd_ref = 1; 744134a1f4eSCasper H.S. Dik pfx->klpd_cred = NULL; 745134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 746134a1f4eSCasper H.S. Dik pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE); 747134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 748134a1f4eSCasper H.S. Dik if (pfx != NULL) 749134a1f4eSCasper H.S. Dik klpd_rele(pfx); 750134a1f4eSCasper H.S. Dik 751134a1f4eSCasper H.S. Dik return (0); 752134a1f4eSCasper H.S. Dik } 753134a1f4eSCasper H.S. Dik 754134a1f4eSCasper H.S. Dik int 755134a1f4eSCasper H.S. Dik pfexec_unreg(int did) 756134a1f4eSCasper H.S. Dik { 757134a1f4eSCasper H.S. Dik door_handle_t dh; 758134a1f4eSCasper H.S. Dik int err = 0; 759134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 760134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 761134a1f4eSCasper H.S. Dik 762134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did); 763134a1f4eSCasper H.S. Dik if (dh == NULL) 764134a1f4eSCasper H.S. Dik return (set_errno(EBADF)); 765134a1f4eSCasper H.S. Dik 766134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 767134a1f4eSCasper H.S. Dik pfd = myzone->zone_pfexecd; 768134a1f4eSCasper H.S. Dik if (pfd != NULL && pfd->klpd_door == dh) { 769134a1f4eSCasper H.S. Dik klpd_unlink(pfd); 770134a1f4eSCasper H.S. Dik } else { 771134a1f4eSCasper H.S. Dik pfd = NULL; 772134a1f4eSCasper H.S. Dik err = EINVAL; 773134a1f4eSCasper H.S. Dik } 774134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 775134a1f4eSCasper H.S. Dik door_ki_rele(dh); 776134a1f4eSCasper H.S. Dik /* 777134a1f4eSCasper H.S. Dik * crfree() cannot be called with zone_lock held; it is called 778134a1f4eSCasper H.S. Dik * indirectly through closing the door handle 779134a1f4eSCasper H.S. Dik */ 780134a1f4eSCasper H.S. Dik if (pfd != NULL) 781134a1f4eSCasper H.S. Dik klpd_rele(pfd); 782134a1f4eSCasper H.S. Dik if (err != 0) 783134a1f4eSCasper H.S. Dik return (set_errno(err)); 784134a1f4eSCasper H.S. Dik return (0); 785134a1f4eSCasper H.S. Dik } 786134a1f4eSCasper H.S. Dik 787134a1f4eSCasper H.S. Dik static int 788134a1f4eSCasper H.S. Dik get_path(char *buf, const char *path, int len) 789134a1f4eSCasper H.S. Dik { 790134a1f4eSCasper H.S. Dik size_t lc; 791134a1f4eSCasper H.S. Dik char *s; 792134a1f4eSCasper H.S. Dik 793134a1f4eSCasper H.S. Dik if (len < 0) 794134a1f4eSCasper H.S. Dik len = strlen(path); 795134a1f4eSCasper H.S. Dik 796134a1f4eSCasper H.S. Dik if (*path == '/' && len < MAXPATHLEN) { 797134a1f4eSCasper H.S. Dik (void) strcpy(buf, path); 798134a1f4eSCasper H.S. Dik return (0); 799134a1f4eSCasper H.S. Dik } 800134a1f4eSCasper H.S. Dik /* 801134a1f4eSCasper H.S. Dik * Build the pathname using the current directory + resolve pathname. 802134a1f4eSCasper H.S. Dik * The resolve pathname either starts with a normal component and 803134a1f4eSCasper H.S. Dik * we can just concatenate them or it starts with one 804134a1f4eSCasper H.S. Dik * or more ".." component and we can remove those; the 805134a1f4eSCasper H.S. Dik * last one cannot be a ".." and the current directory has 806134a1f4eSCasper H.S. Dik * more components than the number of ".." in the resolved pathname. 807134a1f4eSCasper H.S. Dik */ 808134a1f4eSCasper H.S. Dik if (dogetcwd(buf, MAXPATHLEN) != 0) 809134a1f4eSCasper H.S. Dik return (-1); 810134a1f4eSCasper H.S. Dik 811134a1f4eSCasper H.S. Dik lc = strlen(buf); 812134a1f4eSCasper H.S. Dik 813134a1f4eSCasper H.S. Dik while (len > 3 && strncmp("../", path, 3) == 0) { 814134a1f4eSCasper H.S. Dik len -= 3; 815134a1f4eSCasper H.S. Dik path += 3; 816134a1f4eSCasper H.S. Dik 817134a1f4eSCasper H.S. Dik s = strrchr(buf, '/'); 818134a1f4eSCasper H.S. Dik if (s == NULL || s == buf) 819134a1f4eSCasper H.S. Dik return (-1); 820134a1f4eSCasper H.S. Dik 821134a1f4eSCasper H.S. Dik *s = '\0'; 822134a1f4eSCasper H.S. Dik lc = s - buf; 823134a1f4eSCasper H.S. Dik } 824134a1f4eSCasper H.S. Dik /* Add a "/" and a NUL */ 825134a1f4eSCasper H.S. Dik if (lc < 2 || lc + len + 2 >= MAXPATHLEN) 826134a1f4eSCasper H.S. Dik return (-1); 827134a1f4eSCasper H.S. Dik 828134a1f4eSCasper H.S. Dik buf[lc] = '/'; 829134a1f4eSCasper H.S. Dik (void) strcpy(buf + lc + 1, path); 830134a1f4eSCasper H.S. Dik 831134a1f4eSCasper H.S. Dik return (0); 832134a1f4eSCasper H.S. Dik } 833134a1f4eSCasper H.S. Dik 834134a1f4eSCasper H.S. Dik /* 835134a1f4eSCasper H.S. Dik * Perform the pfexec upcall. 836134a1f4eSCasper H.S. Dik * 837134a1f4eSCasper H.S. Dik * The pfexec upcall is different from the klpd_upcall in that a failure 838134a1f4eSCasper H.S. Dik * will lead to a denial of execution. 839134a1f4eSCasper H.S. Dik */ 840134a1f4eSCasper H.S. Dik int 841134a1f4eSCasper H.S. Dik pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr, 842134a1f4eSCasper H.S. Dik boolean_t *scrub) 843134a1f4eSCasper H.S. Dik { 844134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 845134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 846134a1f4eSCasper H.S. Dik pfexec_reply_t pr, *prp; 847134a1f4eSCasper H.S. Dik door_arg_t da; 848134a1f4eSCasper H.S. Dik int dres; 849134a1f4eSCasper H.S. Dik cred_t *ncr = NULL; 850134a1f4eSCasper H.S. Dik int err = -1; 851134a1f4eSCasper H.S. Dik priv_set_t *iset; 852134a1f4eSCasper H.S. Dik priv_set_t *lset; 853134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED()); 854134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN); 855134a1f4eSCasper H.S. Dik 856134a1f4eSCasper H.S. Dik /* Find registration */ 857134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 858134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 859134a1f4eSCasper H.S. Dik klpd_hold(pfd); 860134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 861134a1f4eSCasper H.S. Dik 862134a1f4eSCasper H.S. Dik if (pfd == NULL) 863134a1f4eSCasper H.S. Dik return (0); 864134a1f4eSCasper H.S. Dik 865134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 866134a1f4eSCasper H.S. Dik klpd_rele(pfd); 867134a1f4eSCasper H.S. Dik return (0); 868134a1f4eSCasper H.S. Dik } 869134a1f4eSCasper H.S. Dik 870134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 871134a1f4eSCasper H.S. Dik 872134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1) 873134a1f4eSCasper H.S. Dik goto out1; 874134a1f4eSCasper H.S. Dik 875134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 876134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_EXEC_ATTRS; 877134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 878134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr); 879134a1f4eSCasper H.S. Dik 880134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 881134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 882134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 883134a1f4eSCasper H.S. Dik da.desc_num = 0; 884134a1f4eSCasper H.S. Dik da.rbuf = (char *)≺ 885134a1f4eSCasper H.S. Dik da.rsize = sizeof (pr); 886134a1f4eSCasper H.S. Dik 887134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 888134a1f4eSCasper H.S. Dik switch (dres) { 889134a1f4eSCasper H.S. Dik case EAGAIN: 890134a1f4eSCasper H.S. Dik delay(1); 891134a1f4eSCasper H.S. Dik continue; 892134a1f4eSCasper H.S. Dik case EINVAL: 893134a1f4eSCasper H.S. Dik case EBADF: 894134a1f4eSCasper H.S. Dik /* FALLTHROUGH */ 895134a1f4eSCasper H.S. Dik case EINTR: 896134a1f4eSCasper H.S. Dik /* FALLTHROUGH */ 897134a1f4eSCasper H.S. Dik default: 898134a1f4eSCasper H.S. Dik goto out; 899134a1f4eSCasper H.S. Dik } 900134a1f4eSCasper H.S. Dik } 901134a1f4eSCasper H.S. Dik 902134a1f4eSCasper H.S. Dik prp = (pfexec_reply_t *)da.rbuf; 903134a1f4eSCasper H.S. Dik /* 904134a1f4eSCasper H.S. Dik * Check the size of the result and the alignment of the 905134a1f4eSCasper H.S. Dik * privilege sets. 906134a1f4eSCasper H.S. Dik */ 907134a1f4eSCasper H.S. Dik if (da.rsize < sizeof (pr) || 908134a1f4eSCasper H.S. Dik prp->pfr_ioff > da.rsize - sizeof (priv_set_t) || 909134a1f4eSCasper H.S. Dik prp->pfr_loff > da.rsize - sizeof (priv_set_t) || 910134a1f4eSCasper H.S. Dik (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 || 911134a1f4eSCasper H.S. Dik (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0) 912134a1f4eSCasper H.S. Dik goto out; 913134a1f4eSCasper H.S. Dik 914134a1f4eSCasper H.S. Dik /* 915134a1f4eSCasper H.S. Dik * Get results: 916134a1f4eSCasper H.S. Dik * allow/allow with additional credentials/disallow[*] 917134a1f4eSCasper H.S. Dik * 918134a1f4eSCasper H.S. Dik * euid, uid, egid, gid, privs, and limitprivs 919134a1f4eSCasper H.S. Dik * We now have somewhat more flexibility we could even set E and P 920134a1f4eSCasper H.S. Dik * judiciously but that would break some currently valid assumptions 921134a1f4eSCasper H.S. Dik * [*] Disallow is not readily supported by always including 922134a1f4eSCasper H.S. Dik * the Basic Solaris User profile in all user's profiles. 923134a1f4eSCasper H.S. Dik */ 924134a1f4eSCasper H.S. Dik 925134a1f4eSCasper H.S. Dik if (!prp->pfr_allowed) { 926134a1f4eSCasper H.S. Dik err = EACCES; 927134a1f4eSCasper H.S. Dik goto out; 928134a1f4eSCasper H.S. Dik } 929134a1f4eSCasper H.S. Dik if (!prp->pfr_setcred) { 930134a1f4eSCasper H.S. Dik err = 0; 931134a1f4eSCasper H.S. Dik goto out; 932134a1f4eSCasper H.S. Dik } 933134a1f4eSCasper H.S. Dik ncr = crdup((cred_t *)cr); 934134a1f4eSCasper H.S. Dik 935134a1f4eSCasper H.S. Dik /* 936134a1f4eSCasper H.S. Dik * Generate the new credential set scrubenv if ruid != euid (or set) 937134a1f4eSCasper H.S. Dik * the "I'm set-uid flag" but that is not inherited so scrubbing 938134a1f4eSCasper H.S. Dik * the environment is a requirement. 939134a1f4eSCasper H.S. Dik */ 940134a1f4eSCasper H.S. Dik /* Set uids or gids, note that -1 will do the right thing */ 941134a1f4eSCasper H.S. Dik if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0) 942134a1f4eSCasper H.S. Dik goto out; 943134a1f4eSCasper H.S. Dik if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0) 944134a1f4eSCasper H.S. Dik goto out; 945134a1f4eSCasper H.S. Dik 946134a1f4eSCasper H.S. Dik *scrub = prp->pfr_scrubenv; 947134a1f4eSCasper H.S. Dik 948134a1f4eSCasper H.S. Dik if (prp->pfr_clearflag) 949134a1f4eSCasper H.S. Dik CR_FLAGS(ncr) &= ~PRIV_PFEXEC; 950134a1f4eSCasper H.S. Dik 951134a1f4eSCasper H.S. Dik /* We cannot exceed our Limit set, no matter what */ 952134a1f4eSCasper H.S. Dik iset = PFEXEC_REPLY_IPRIV(prp); 953134a1f4eSCasper H.S. Dik 954134a1f4eSCasper H.S. Dik if (iset != NULL) { 955134a1f4eSCasper H.S. Dik if (!priv_issubset(iset, &CR_LPRIV(ncr))) 956134a1f4eSCasper H.S. Dik goto out; 957134a1f4eSCasper H.S. Dik priv_union(iset, &CR_IPRIV(ncr)); 958134a1f4eSCasper H.S. Dik } 959134a1f4eSCasper H.S. Dik 960134a1f4eSCasper H.S. Dik /* Nor can we increate our Limit set itself */ 961134a1f4eSCasper H.S. Dik lset = PFEXEC_REPLY_LPRIV(prp); 962134a1f4eSCasper H.S. Dik 963134a1f4eSCasper H.S. Dik if (lset != NULL) { 964134a1f4eSCasper H.S. Dik if (!priv_issubset(lset, &CR_LPRIV(ncr))) 965134a1f4eSCasper H.S. Dik goto out; 966134a1f4eSCasper H.S. Dik CR_LPRIV(ncr) = *lset; 967134a1f4eSCasper H.S. Dik } 968134a1f4eSCasper H.S. Dik 969134a1f4eSCasper H.S. Dik /* Exec will do the standard set operations */ 970134a1f4eSCasper H.S. Dik 971134a1f4eSCasper H.S. Dik err = 0; 972134a1f4eSCasper H.S. Dik out: 973134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pr) 974134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 975134a1f4eSCasper H.S. Dik out1: 976134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 977134a1f4eSCasper H.S. Dik klpd_rele(pfd); 978134a1f4eSCasper H.S. Dik if (ncr != NULL) { 979134a1f4eSCasper H.S. Dik if (err == 0) 980134a1f4eSCasper H.S. Dik *pfcr = ncr; 981134a1f4eSCasper H.S. Dik else 982134a1f4eSCasper H.S. Dik crfree(ncr); 983134a1f4eSCasper H.S. Dik } 984134a1f4eSCasper H.S. Dik return (err); 985134a1f4eSCasper H.S. Dik } 986134a1f4eSCasper H.S. Dik 987134a1f4eSCasper H.S. Dik int 988134a1f4eSCasper H.S. Dik get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set) 989134a1f4eSCasper H.S. Dik { 990134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 991134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 992134a1f4eSCasper H.S. Dik door_arg_t da; 993134a1f4eSCasper H.S. Dik int dres; 994134a1f4eSCasper H.S. Dik int err = -1; 995134a1f4eSCasper H.S. Dik priv_set_t *fset, pmem; 996134a1f4eSCasper H.S. Dik cred_t *zkcr; 997134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr); 998134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN); 999134a1f4eSCasper H.S. Dik 1000134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 1001134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 1002134a1f4eSCasper H.S. Dik klpd_hold(pfd); 1003134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 1004134a1f4eSCasper H.S. Dik 1005134a1f4eSCasper H.S. Dik if (pfd == NULL) 1006134a1f4eSCasper H.S. Dik return (-1); 1007134a1f4eSCasper H.S. Dik 1008134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 1009134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1010134a1f4eSCasper H.S. Dik return (0); 1011134a1f4eSCasper H.S. Dik } 1012134a1f4eSCasper H.S. Dik 1013134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 1014134a1f4eSCasper H.S. Dik 1015134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, respn, -1) == -1) 1016134a1f4eSCasper H.S. Dik goto out1; 1017134a1f4eSCasper H.S. Dik 1018134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 1019134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_FORCED_PRIVS; 1020134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 1021134a1f4eSCasper H.S. Dik pap->pfa_uid = (uid_t)-1; /* Not relevant */ 1022134a1f4eSCasper H.S. Dik 1023134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 1024134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 1025134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 1026134a1f4eSCasper H.S. Dik da.desc_num = 0; 1027134a1f4eSCasper H.S. Dik da.rbuf = (char *)&pmem; 1028134a1f4eSCasper H.S. Dik da.rsize = sizeof (pmem); 1029134a1f4eSCasper H.S. Dik 1030134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 1031134a1f4eSCasper H.S. Dik switch (dres) { 1032134a1f4eSCasper H.S. Dik case EAGAIN: 1033134a1f4eSCasper H.S. Dik delay(1); 1034134a1f4eSCasper H.S. Dik continue; 1035134a1f4eSCasper H.S. Dik case EINVAL: 1036134a1f4eSCasper H.S. Dik case EBADF: 1037134a1f4eSCasper H.S. Dik case EINTR: 1038134a1f4eSCasper H.S. Dik default: 1039134a1f4eSCasper H.S. Dik goto out; 1040134a1f4eSCasper H.S. Dik } 1041134a1f4eSCasper H.S. Dik } 1042134a1f4eSCasper H.S. Dik 1043134a1f4eSCasper H.S. Dik /* 1044134a1f4eSCasper H.S. Dik * Check the size of the result, it's a privilege set. 1045134a1f4eSCasper H.S. Dik */ 1046134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (priv_set_t)) 1047134a1f4eSCasper H.S. Dik goto out; 1048134a1f4eSCasper H.S. Dik 1049134a1f4eSCasper H.S. Dik fset = (priv_set_t *)da.rbuf; 1050134a1f4eSCasper H.S. Dik 1051134a1f4eSCasper H.S. Dik /* 1052134a1f4eSCasper H.S. Dik * We restrict the forced privileges with whatever is available in 1053134a1f4eSCasper H.S. Dik * the current zone. 1054134a1f4eSCasper H.S. Dik */ 1055134a1f4eSCasper H.S. Dik zkcr = zone_kcred(); 1056134a1f4eSCasper H.S. Dik priv_intersect(&CR_LPRIV(zkcr), fset); 1057134a1f4eSCasper H.S. Dik 1058134a1f4eSCasper H.S. Dik /* 1059134a1f4eSCasper H.S. Dik * But we fail if the forced privileges are not found in the current 1060134a1f4eSCasper H.S. Dik * Limit set. 1061134a1f4eSCasper H.S. Dik */ 1062134a1f4eSCasper H.S. Dik if (!priv_issubset(fset, &CR_LPRIV(cr))) { 1063134a1f4eSCasper H.S. Dik err = EACCES; 1064134a1f4eSCasper H.S. Dik } else if (!priv_isemptyset(fset)) { 1065134a1f4eSCasper H.S. Dik err = 0; 1066134a1f4eSCasper H.S. Dik *set = *fset; 1067134a1f4eSCasper H.S. Dik } 1068134a1f4eSCasper H.S. Dik out: 1069134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pmem) 1070134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 1071134a1f4eSCasper H.S. Dik out1: 1072134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 1073134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1074134a1f4eSCasper H.S. Dik return (err); 1075134a1f4eSCasper H.S. Dik } 1076134a1f4eSCasper H.S. Dik 1077134a1f4eSCasper H.S. Dik int 1078134a1f4eSCasper H.S. Dik check_user_privs(const cred_t *cr, const priv_set_t *set) 1079134a1f4eSCasper H.S. Dik { 1080134a1f4eSCasper H.S. Dik klpd_reg_t *pfd; 1081134a1f4eSCasper H.S. Dik pfexec_arg_t *pap; 1082134a1f4eSCasper H.S. Dik door_arg_t da; 1083134a1f4eSCasper H.S. Dik int dres; 1084134a1f4eSCasper H.S. Dik int err = -1; 1085134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr); 1086134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t)); 1087134a1f4eSCasper H.S. Dik uint32_t res; 1088134a1f4eSCasper H.S. Dik 1089134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock); 1090134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL) 1091134a1f4eSCasper H.S. Dik klpd_hold(pfd); 1092134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock); 1093134a1f4eSCasper H.S. Dik 1094134a1f4eSCasper H.S. Dik if (pfd == NULL) 1095134a1f4eSCasper H.S. Dik return (-1); 1096134a1f4eSCasper H.S. Dik 1097134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) { 1098134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1099134a1f4eSCasper H.S. Dik return (0); 1100134a1f4eSCasper H.S. Dik } 1101134a1f4eSCasper H.S. Dik 1102134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP); 1103134a1f4eSCasper H.S. Dik 1104134a1f4eSCasper H.S. Dik *(priv_set_t *)&pap->pfa_buf = *set; 1105134a1f4eSCasper H.S. Dik 1106134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS; 1107134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_USER_PRIVS; 1108134a1f4eSCasper H.S. Dik pap->pfa_len = pasize; 1109134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr); 1110134a1f4eSCasper H.S. Dik 1111134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap; 1112134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len; 1113134a1f4eSCasper H.S. Dik da.desc_ptr = NULL; 1114134a1f4eSCasper H.S. Dik da.desc_num = 0; 1115134a1f4eSCasper H.S. Dik da.rbuf = (char *)&res; 1116134a1f4eSCasper H.S. Dik da.rsize = sizeof (res); 1117134a1f4eSCasper H.S. Dik 1118134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) { 1119134a1f4eSCasper H.S. Dik switch (dres) { 1120134a1f4eSCasper H.S. Dik case EAGAIN: 1121134a1f4eSCasper H.S. Dik delay(1); 1122134a1f4eSCasper H.S. Dik continue; 1123134a1f4eSCasper H.S. Dik case EINVAL: 1124134a1f4eSCasper H.S. Dik case EBADF: 1125134a1f4eSCasper H.S. Dik case EINTR: 1126134a1f4eSCasper H.S. Dik default: 1127134a1f4eSCasper H.S. Dik goto out; 1128134a1f4eSCasper H.S. Dik } 1129134a1f4eSCasper H.S. Dik } 1130134a1f4eSCasper H.S. Dik 1131134a1f4eSCasper H.S. Dik /* 1132134a1f4eSCasper H.S. Dik * Check the size of the result. 1133134a1f4eSCasper H.S. Dik */ 1134134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (res)) 1135134a1f4eSCasper H.S. Dik goto out; 1136134a1f4eSCasper H.S. Dik 1137134a1f4eSCasper H.S. Dik if (*(uint32_t *)da.rbuf == 1) 1138134a1f4eSCasper H.S. Dik err = 0; 1139134a1f4eSCasper H.S. Dik out: 1140134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&res) 1141134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize); 1142134a1f4eSCasper H.S. Dik out1: 1143134a1f4eSCasper H.S. Dik kmem_free(pap, pasize); 1144134a1f4eSCasper H.S. Dik klpd_rele(pfd); 1145134a1f4eSCasper H.S. Dik return (err); 1146134a1f4eSCasper H.S. Dik } 1147