16134Scasper /*
26134Scasper * CDDL HEADER START
36134Scasper *
46134Scasper * The contents of this file are subject to the terms of the
56134Scasper * Common Development and Distribution License (the "License").
66134Scasper * You may not use this file except in compliance with the License.
76134Scasper *
86134Scasper * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96134Scasper * or http://www.opensolaris.org/os/licensing.
106134Scasper * See the License for the specific language governing permissions
116134Scasper * and limitations under the License.
126134Scasper *
136134Scasper * When distributing Covered Code, include this CDDL HEADER in each
146134Scasper * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156134Scasper * If applicable, add the following below this CDDL HEADER, with the
166134Scasper * fields enclosed by brackets "[]" replaced with your own identifying
176134Scasper * information: Portions Copyright [yyyy] [name of copyright owner]
186134Scasper *
196134Scasper * CDDL HEADER END
206134Scasper */
216134Scasper
226134Scasper /*
23*12273SCasper.Dik@Sun.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246134Scasper */
256134Scasper
266134Scasper #include <sys/atomic.h>
276134Scasper #include <sys/door.h>
286134Scasper #include <sys/proc.h>
296134Scasper #include <sys/cred_impl.h>
306134Scasper #include <sys/policy.h>
316134Scasper #include <sys/priv.h>
326134Scasper #include <sys/klpd.h>
336134Scasper #include <sys/errno.h>
346134Scasper #include <sys/kmem.h>
356134Scasper #include <sys/project.h>
366134Scasper #include <sys/systm.h>
376134Scasper #include <sys/sysmacros.h>
386134Scasper #include <sys/pathname.h>
396134Scasper #include <sys/varargs.h>
406134Scasper #include <sys/zone.h>
416134Scasper #include <netinet/in.h>
426134Scasper
436134Scasper #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
446134Scasper
456134Scasper static kmutex_t klpd_mutex;
466134Scasper
476134Scasper typedef struct klpd_reg {
486134Scasper struct klpd_reg *klpd_next;
496134Scasper struct klpd_reg **klpd_refp;
506134Scasper door_handle_t klpd_door;
516134Scasper pid_t klpd_door_pid;
526134Scasper priv_set_t klpd_pset;
536134Scasper cred_t *klpd_cred;
546134Scasper int klpd_indel; /* Disabled */
556134Scasper uint32_t klpd_ref;
566134Scasper } klpd_reg_t;
576134Scasper
586134Scasper
596134Scasper /*
606134Scasper * This data structure hangs off the credential of a process; the
616134Scasper * credential is finalized and cannot be changed; but this structure
626134Scasper * can be changed when a new door server for the particular group
636134Scasper * needs to be registered. It is refcounted and shared between
646134Scasper * processes with common ancestry.
656134Scasper *
666134Scasper * The reference count is atomically updated.
676134Scasper *
686134Scasper * But the registration probably needs to be updated under a lock.
696134Scasper */
706134Scasper typedef struct credklpd {
716134Scasper kmutex_t crkl_lock;
726134Scasper klpd_reg_t *crkl_reg;
736134Scasper uint32_t crkl_ref;
746134Scasper } credklpd_t;
756134Scasper
766134Scasper klpd_reg_t *klpd_list;
776134Scasper
786134Scasper static void klpd_unlink(klpd_reg_t *);
796134Scasper static int klpd_unreg_dh(door_handle_t);
806134Scasper
816134Scasper static credklpd_t *crklpd_alloc(void);
826134Scasper
836134Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
846134Scasper
856134Scasper extern size_t max_vnode_path;
866134Scasper
876134Scasper void
klpd_rele(klpd_reg_t * p)886134Scasper klpd_rele(klpd_reg_t *p)
896134Scasper {
906134Scasper if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
916134Scasper if (p->klpd_refp != NULL)
926134Scasper klpd_unlink(p);
936134Scasper if (p->klpd_cred != NULL)
946134Scasper crfree(p->klpd_cred);
956134Scasper door_ki_rele(p->klpd_door);
966134Scasper kmem_free(p, sizeof (*p));
976134Scasper }
986134Scasper }
996134Scasper
1006134Scasper /*
1016134Scasper * In order to be able to walk the lists, we can't unlink the entry
1026134Scasper * until the reference count drops to 0. If we remove it too soon,
1036134Scasper * list walkers will terminate when they happen to call a now orphaned
1046134Scasper * entry.
1056134Scasper */
1066134Scasper static klpd_reg_t *
klpd_rele_next(klpd_reg_t * p)1076134Scasper klpd_rele_next(klpd_reg_t *p)
1086134Scasper {
1096134Scasper klpd_reg_t *r = p->klpd_next;
1106134Scasper
1116134Scasper klpd_rele(p);
1126134Scasper return (r);
1136134Scasper }
1146134Scasper
1156134Scasper
1166134Scasper static void
klpd_hold(klpd_reg_t * p)1176134Scasper klpd_hold(klpd_reg_t *p)
1186134Scasper {
1196134Scasper atomic_add_32(&p->klpd_ref, 1);
1206134Scasper }
1216134Scasper
1226134Scasper /*
1236134Scasper * Remove registration from where it is registered. Returns next in list.
1246134Scasper */
1256134Scasper static void
klpd_unlink(klpd_reg_t * p)1266134Scasper klpd_unlink(klpd_reg_t *p)
1276134Scasper {
1286134Scasper ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
1296134Scasper
1306134Scasper if (p->klpd_refp != NULL)
1316134Scasper *p->klpd_refp = p->klpd_next;
1326134Scasper
1336134Scasper if (p->klpd_next != NULL)
1346134Scasper p->klpd_next->klpd_refp = p->klpd_refp;
1356134Scasper p->klpd_refp = NULL;
1366134Scasper }
1376134Scasper
1386134Scasper /*
139*12273SCasper.Dik@Sun.COM * Remove all elements of the klpd list and decrement their refcnts.
1406134Scasper * The lock guarding the list should be held; this function is
141*12273SCasper.Dik@Sun.COM * called when we are sure we want to destroy the list completely
142*12273SCasper.Dik@Sun.COM * list but not so sure that the reference counts of all elements have
143*12273SCasper.Dik@Sun.COM * dropped back to 1.
1446134Scasper */
1456134Scasper void
klpd_freelist(klpd_reg_t ** pp)146*12273SCasper.Dik@Sun.COM klpd_freelist(klpd_reg_t **pp)
1476134Scasper {
148*12273SCasper.Dik@Sun.COM klpd_reg_t *p;
149*12273SCasper.Dik@Sun.COM
150*12273SCasper.Dik@Sun.COM while ((p = *pp) != NULL) {
151*12273SCasper.Dik@Sun.COM klpd_unlink(p);
152*12273SCasper.Dik@Sun.COM klpd_rele(p);
153*12273SCasper.Dik@Sun.COM }
1546134Scasper }
1556134Scasper
1566134Scasper /*
1576134Scasper * Link new entry in list. The Boolean argument specifies whether this
1586134Scasper * list can contain only a single item or multiple items.
1596134Scasper * Returns the entry which needs to be released if single is B_TRUE.
1606134Scasper */
1616134Scasper static klpd_reg_t *
klpd_link(klpd_reg_t * p,klpd_reg_t ** listp,boolean_t single)1626134Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
1636134Scasper {
1646134Scasper klpd_reg_t *old = *listp;
1656134Scasper
1666134Scasper ASSERT(p->klpd_ref == 1);
1676134Scasper
1686134Scasper ASSERT(old == NULL || *old->klpd_refp == old);
1696134Scasper p->klpd_refp = listp;
1706134Scasper p->klpd_next = single ? NULL : old;
1716134Scasper *listp = p;
1726134Scasper if (old != NULL) {
1736134Scasper if (single) {
1746134Scasper ASSERT(old->klpd_next == NULL);
1756134Scasper old->klpd_refp = NULL;
1766134Scasper return (old);
1776134Scasper } else
1786134Scasper old->klpd_refp = &p->klpd_next;
1796134Scasper }
1806134Scasper return (NULL);
1816134Scasper }
1826134Scasper
1836134Scasper /*
1846134Scasper * The typical call consists of:
1856134Scasper * - priv_set_t
1866134Scasper * - some integer data (type, value)
1876134Scasper * for now, it's just one bit.
1886134Scasper */
1896134Scasper static klpd_head_t *
klpd_marshall(klpd_reg_t * p,const priv_set_t * rq,va_list ap)1906134Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
1916134Scasper {
192*12273SCasper.Dik@Sun.COM char *tmp;
1936134Scasper uint_t type;
1946134Scasper vnode_t *vp;
1956134Scasper size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t);
1966134Scasper size_t plen, clen;
1976134Scasper int proto;
1986134Scasper
1996134Scasper klpd_arg_t *kap = NULL;
2006134Scasper klpd_head_t *khp;
2016134Scasper
2026134Scasper type = va_arg(ap, uint_t);
2036134Scasper switch (type) {
2046134Scasper case KLPDARG_NOMORE:
2056134Scasper khp = kmem_zalloc(len, KM_SLEEP);
2066134Scasper khp->klh_argoff = 0;
2076134Scasper break;
2086134Scasper case KLPDARG_VNODE:
2096134Scasper len += offsetof(klpd_arg_t, kla_str);
2106134Scasper vp = va_arg(ap, vnode_t *);
2116134Scasper if (vp == NULL)
2126134Scasper return (NULL);
2136134Scasper
214*12273SCasper.Dik@Sun.COM tmp = va_arg(ap, char *);
2156134Scasper
216*12273SCasper.Dik@Sun.COM if (tmp != NULL && *tmp != '\0')
217*12273SCasper.Dik@Sun.COM clen = strlen(tmp) + 1;
2186134Scasper else
2196134Scasper clen = 0;
2206134Scasper
2216134Scasper len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
2226134Scasper khp = kmem_zalloc(len, KM_SLEEP);
2236134Scasper
2246134Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2256134Scasper kap = KLH_ARG(khp);
2266134Scasper
2276134Scasper if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
2286134Scasper vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
2296134Scasper kmem_free(khp, len);
2306134Scasper return (NULL);
2316134Scasper }
2326134Scasper if (clen != 0) {
2336134Scasper plen = strlen(kap->kla_str);
2346134Scasper if (plen + clen + 1 >= MAXPATHLEN) {
2356134Scasper kmem_free(khp, len);
2366134Scasper return (NULL);
2376134Scasper }
2386134Scasper /* Don't make root into a double "/" */
2396134Scasper if (plen <= 2)
2406134Scasper plen = 0;
2416134Scasper kap->kla_str[plen] = '/';
242*12273SCasper.Dik@Sun.COM bcopy(tmp, &kap->kla_str[plen + 1], clen);
2436134Scasper }
2446134Scasper break;
2456134Scasper case KLPDARG_PORT:
2466134Scasper proto = va_arg(ap, int);
2476134Scasper switch (proto) {
2486134Scasper case IPPROTO_TCP: type = KLPDARG_TCPPORT;
2496134Scasper break;
2506134Scasper case IPPROTO_UDP: type = KLPDARG_UDPPORT;
2516134Scasper break;
2526134Scasper case IPPROTO_SCTP: type = KLPDARG_SCTPPORT;
2536134Scasper break;
2546134Scasper case PROTO_SDP: type = KLPDARG_SDPPORT;
2556134Scasper break;
2566134Scasper }
2576134Scasper /* FALLTHROUGH */
2586134Scasper case KLPDARG_INT:
2596134Scasper case KLPDARG_TCPPORT:
2606134Scasper case KLPDARG_UDPPORT:
2616134Scasper case KLPDARG_SCTPPORT:
2626134Scasper case KLPDARG_SDPPORT:
2636134Scasper len += sizeof (*kap);
2646134Scasper khp = kmem_zalloc(len, KM_SLEEP);
2656134Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2666134Scasper kap = KLH_ARG(khp);
2676134Scasper kap->kla_int = va_arg(ap, int);
2686134Scasper break;
2696134Scasper default:
2706134Scasper return (NULL);
2716134Scasper }
2726134Scasper khp->klh_vers = KLPDCALL_VERS;
2736134Scasper khp->klh_len = len;
2746134Scasper khp->klh_privoff = sizeof (*khp);
2756134Scasper *KLH_PRIVSET(khp) = *rq;
2766134Scasper if (kap != NULL) {
2776134Scasper kap->kla_type = type;
2786134Scasper kap->kla_dlen = len - khp->klh_argoff;
2796134Scasper }
2806134Scasper return (khp);
2816134Scasper }
2826134Scasper
2836134Scasper static int
klpd_do_call(klpd_reg_t * p,const priv_set_t * req,va_list ap)2846134Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
2856134Scasper {
2866134Scasper door_arg_t da;
2876134Scasper int res;
2886134Scasper int dres;
2896134Scasper klpd_head_t *klh;
2906134Scasper
2916134Scasper if (p->klpd_door_pid == curproc->p_pid)
2926134Scasper return (-1);
2936134Scasper
2946134Scasper klh = klpd_marshall(p, req, ap);
2956134Scasper
2966134Scasper if (klh == NULL)
2976134Scasper return (-1);
2986134Scasper
2996134Scasper da.data_ptr = (char *)klh;
3006134Scasper da.data_size = klh->klh_len;
3016134Scasper da.desc_ptr = NULL;
3026134Scasper da.desc_num = 0;
3036134Scasper da.rbuf = (char *)&res;
3046134Scasper da.rsize = sizeof (res);
3056134Scasper
3066997Sjwadams while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
3076997Sjwadams SIZE_MAX, 0)) != 0) {
3086134Scasper switch (dres) {
3096134Scasper case EAGAIN:
3106134Scasper delay(1);
3116134Scasper continue;
3126134Scasper case EINVAL:
3136134Scasper case EBADF:
3146134Scasper /* Bad door, don't call it again. */
3156134Scasper (void) klpd_unreg_dh(p->klpd_door);
3166134Scasper /* FALLTHROUGH */
3176134Scasper case EINTR:
3186134Scasper /* Pending signal, nothing we can do. */
3196134Scasper /* FALLTHROUGH */
3206134Scasper default:
3216134Scasper kmem_free(klh, klh->klh_len);
3226134Scasper return (-1);
3236134Scasper }
3246134Scasper }
3256134Scasper kmem_free(klh, klh->klh_len);
3266134Scasper /* Bogus return value, must be a failure */
3276134Scasper if (da.rbuf != (char *)&res) {
3286134Scasper kmem_free(da.rbuf, da.rsize);
3296134Scasper return (-1);
3306134Scasper }
3316134Scasper return (res);
3326134Scasper }
3336134Scasper
3346134Scasper uint32_t klpd_bad_locks;
3356134Scasper
3366134Scasper int
klpd_call(const cred_t * cr,const priv_set_t * req,va_list ap)3376134Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
3386134Scasper {
3396134Scasper klpd_reg_t *p;
3406134Scasper int rv = -1;
3416134Scasper credklpd_t *ckp;
3426134Scasper zone_t *ckzone;
3436134Scasper
3446134Scasper /*
3456134Scasper * These locks must not be held when this code is called;
3466134Scasper * callbacks to userland with these locks held will result
3476134Scasper * in issues. That said, the code at the call sides was
3486134Scasper * restructured not to call with any of the locks held and
3496134Scasper * no policies operate by default on most processes.
3506134Scasper */
3516134Scasper if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
3526134Scasper mutex_owned(&curproc->p_crlock)) {
3536134Scasper atomic_add_32(&klpd_bad_locks, 1);
3546134Scasper return (-1);
3556134Scasper }
3566134Scasper
3576134Scasper /*
3586134Scasper * Enforce the limit set for the call process (still).
3596134Scasper */
3606134Scasper if (!priv_issubset(req, &CR_LPRIV(cr)))
3616134Scasper return (-1);
3626134Scasper
3636134Scasper /* Try 1: get the credential specific klpd */
3646134Scasper if ((ckp = crgetcrklpd(cr)) != NULL) {
3656134Scasper mutex_enter(&ckp->crkl_lock);
3666134Scasper if ((p = ckp->crkl_reg) != NULL &&
3676134Scasper p->klpd_indel == 0 &&
3686134Scasper priv_issubset(req, &p->klpd_pset)) {
3696134Scasper klpd_hold(p);
3706134Scasper mutex_exit(&ckp->crkl_lock);
3716134Scasper rv = klpd_do_call(p, req, ap);
3726134Scasper mutex_enter(&ckp->crkl_lock);
3736134Scasper klpd_rele(p);
3746134Scasper mutex_exit(&ckp->crkl_lock);
3756134Scasper if (rv != -1)
3766134Scasper return (rv == 0 ? 0 : -1);
3776134Scasper } else {
3786134Scasper mutex_exit(&ckp->crkl_lock);
3796134Scasper }
3806134Scasper }
3816134Scasper
3826134Scasper /* Try 2: get the project specific klpd */
3836134Scasper mutex_enter(&klpd_mutex);
3846134Scasper
3856134Scasper if ((p = curproj->kpj_klpd) != NULL) {
3866134Scasper klpd_hold(p);
3876134Scasper mutex_exit(&klpd_mutex);
3886134Scasper if (p->klpd_indel == 0 &&
3896134Scasper priv_issubset(req, &p->klpd_pset)) {
3906134Scasper rv = klpd_do_call(p, req, ap);
3916134Scasper }
3926134Scasper mutex_enter(&klpd_mutex);
3936134Scasper klpd_rele(p);
3946134Scasper mutex_exit(&klpd_mutex);
3956134Scasper
3966134Scasper if (rv != -1)
3976134Scasper return (rv == 0 ? 0 : -1);
3986134Scasper } else {
3996134Scasper mutex_exit(&klpd_mutex);
4006134Scasper }
4016134Scasper
4026134Scasper /* Try 3: get the global klpd list */
4036134Scasper ckzone = crgetzone(cr);
4046134Scasper mutex_enter(&klpd_mutex);
4056134Scasper
4066134Scasper for (p = klpd_list; p != NULL; ) {
4076134Scasper zone_t *kkzone = crgetzone(p->klpd_cred);
4086134Scasper if ((kkzone == &zone0 || kkzone == ckzone) &&
4096134Scasper p->klpd_indel == 0 &&
4106134Scasper priv_issubset(req, &p->klpd_pset)) {
4116134Scasper klpd_hold(p);
4126134Scasper mutex_exit(&klpd_mutex);
4136134Scasper rv = klpd_do_call(p, req, ap);
4146134Scasper mutex_enter(&klpd_mutex);
4156134Scasper
4166134Scasper p = klpd_rele_next(p);
4176134Scasper
4186134Scasper if (rv != -1)
4196134Scasper break;
4206134Scasper } else {
4216134Scasper p = p->klpd_next;
4226134Scasper }
4236134Scasper }
4246134Scasper mutex_exit(&klpd_mutex);
4256134Scasper return (rv == 0 ? 0 : -1);
4266134Scasper }
4276134Scasper
4286134Scasper /*
4296134Scasper * Register the klpd.
4306134Scasper * If the pid_t passed in is positive, update the registration for
4316134Scasper * the specific process; that is only possible if the process already
4326134Scasper * has a registration on it. This change of registration will affect
4336134Scasper * all processes which share common ancestry.
4346134Scasper *
4356134Scasper * MY_PID (pid 0) can be used to create or change the context for
4366134Scasper * the current process, typically done after fork().
4376134Scasper *
4386134Scasper * A negative value can be used to register a klpd globally.
4396134Scasper *
4406134Scasper * The per-credential klpd needs to be cleaned up when entering
4416134Scasper * a zone or unsetting the flag.
4426134Scasper */
4436134Scasper int
klpd_reg(int did,idtype_t type,id_t id,priv_set_t * psetbuf)4446134Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
4456134Scasper {
4466134Scasper cred_t *cr = CRED();
4476134Scasper door_handle_t dh;
4486134Scasper klpd_reg_t *kpd;
4496134Scasper priv_set_t pset;
4506134Scasper door_info_t di;
4516134Scasper credklpd_t *ckp = NULL;
4526134Scasper pid_t pid = -1;
4536134Scasper projid_t proj = -1;
4546134Scasper kproject_t *kpp = NULL;
4556134Scasper
4566134Scasper if (CR_FLAGS(cr) & PRIV_XPOLICY)
4576134Scasper return (set_errno(EINVAL));
4586134Scasper
4596134Scasper if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
4606134Scasper return (set_errno(EFAULT));
4616134Scasper
4626134Scasper if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
4636134Scasper return (set_errno(EPERM));
4646134Scasper
4656134Scasper switch (type) {
4666134Scasper case P_PID:
4676134Scasper pid = (pid_t)id;
4686134Scasper if (pid == P_MYPID)
4696134Scasper pid = curproc->p_pid;
4706134Scasper if (pid == curproc->p_pid)
4716134Scasper ckp = crklpd_alloc();
4726134Scasper break;
4736134Scasper case P_PROJID:
4746134Scasper proj = (projid_t)id;
4756134Scasper kpp = project_hold_by_id(proj, crgetzone(cr),
4766134Scasper PROJECT_HOLD_FIND);
4776134Scasper if (kpp == NULL)
4786134Scasper return (set_errno(ESRCH));
4796134Scasper break;
4806134Scasper default:
4816134Scasper return (set_errno(ENOTSUP));
4826134Scasper }
4836134Scasper
4846134Scasper
4856134Scasper /*
4866134Scasper * Verify the door passed in; it must be a door and we won't
4876134Scasper * allow processes to be called on their own behalf.
4886134Scasper */
4896134Scasper dh = door_ki_lookup(did);
4906134Scasper if (dh == NULL || door_ki_info(dh, &di) != 0) {
4916134Scasper if (ckp != NULL)
4926134Scasper crklpd_rele(ckp);
4936134Scasper if (kpp != NULL)
4946134Scasper project_rele(kpp);
4956134Scasper return (set_errno(EBADF));
4966134Scasper }
4976134Scasper if (type == P_PID && pid == di.di_target) {
4986134Scasper if (ckp != NULL)
4996134Scasper crklpd_rele(ckp);
5006134Scasper ASSERT(kpp == NULL);
5016134Scasper return (set_errno(EINVAL));
5026134Scasper }
5036134Scasper
5046134Scasper kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
5056134Scasper crhold(kpd->klpd_cred = cr);
5066134Scasper kpd->klpd_door = dh;
5076134Scasper kpd->klpd_door_pid = di.di_target;
5086134Scasper kpd->klpd_ref = 1;
5096134Scasper kpd->klpd_pset = pset;
5106134Scasper
5116134Scasper if (kpp != NULL) {
5126134Scasper mutex_enter(&klpd_mutex);
5136134Scasper kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
5146134Scasper mutex_exit(&klpd_mutex);
5156134Scasper if (kpd != NULL)
5166134Scasper klpd_rele(kpd);
5176134Scasper project_rele(kpp);
5186134Scasper } else if ((int)pid < 0) {
5196134Scasper /* Global daemon */
5206134Scasper mutex_enter(&klpd_mutex);
5216134Scasper (void) klpd_link(kpd, &klpd_list, B_FALSE);
5226134Scasper mutex_exit(&klpd_mutex);
5236134Scasper } else if (pid == curproc->p_pid) {
5246134Scasper proc_t *p = curproc;
5256134Scasper cred_t *newcr = cralloc();
5266134Scasper
5276134Scasper /* No need to lock, sole reference to ckp */
5286134Scasper kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
5296134Scasper
5306134Scasper if (kpd != NULL)
5316134Scasper klpd_rele(kpd);
5326134Scasper
5336134Scasper mutex_enter(&p->p_crlock);
5346134Scasper cr = p->p_cred;
5356134Scasper crdup_to(cr, newcr);
5366134Scasper crsetcrklpd(newcr, ckp);
5376134Scasper p->p_cred = newcr; /* Already held for p_cred */
5386134Scasper
5396134Scasper crhold(newcr); /* Hold once for the current thread */
5406134Scasper mutex_exit(&p->p_crlock);
5416134Scasper crfree(cr); /* One for the p_cred */
5426134Scasper crset(p, newcr);
5436134Scasper } else {
5446134Scasper proc_t *p;
5456134Scasper cred_t *pcr;
5466134Scasper mutex_enter(&pidlock);
5476134Scasper p = prfind(pid);
5486134Scasper if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
5496134Scasper mutex_exit(&pidlock);
5506134Scasper klpd_rele(kpd);
5516134Scasper return (set_errno(p == NULL ? ESRCH : EPERM));
5526134Scasper }
5536134Scasper mutex_enter(&p->p_crlock);
5546134Scasper crhold(pcr = p->p_cred);
5556134Scasper mutex_exit(&pidlock);
5566134Scasper mutex_exit(&p->p_crlock);
5576134Scasper /*
5586134Scasper * We're going to update the credential's ckp in place;
5596134Scasper * this requires that it exists.
5606134Scasper */
5616134Scasper ckp = crgetcrklpd(pcr);
5626134Scasper if (ckp == NULL) {
5636134Scasper crfree(pcr);
5646134Scasper klpd_rele(kpd);
5656134Scasper return (set_errno(EINVAL));
5666134Scasper }
5676134Scasper crklpd_setreg(ckp, kpd);
5686134Scasper crfree(pcr);
5696134Scasper }
5706134Scasper
5716134Scasper return (0);
5726134Scasper }
5736134Scasper
5746134Scasper static int
klpd_unreg_dh(door_handle_t dh)5756134Scasper klpd_unreg_dh(door_handle_t dh)
5766134Scasper {
5776134Scasper klpd_reg_t *p;
5786134Scasper
5796134Scasper mutex_enter(&klpd_mutex);
5806134Scasper for (p = klpd_list; p != NULL; p = p->klpd_next) {
5816134Scasper if (p->klpd_door == dh)
5826134Scasper break;
5836134Scasper }
5846134Scasper if (p == NULL) {
5856134Scasper mutex_exit(&klpd_mutex);
5866134Scasper return (EINVAL);
5876134Scasper }
5886134Scasper if (p->klpd_indel != 0) {
5896134Scasper mutex_exit(&klpd_mutex);
5906134Scasper return (EAGAIN);
5916134Scasper }
5926134Scasper p->klpd_indel = 1;
5936134Scasper klpd_rele(p);
5946134Scasper mutex_exit(&klpd_mutex);
5956134Scasper return (0);
5966134Scasper }
5976134Scasper
5986134Scasper int
klpd_unreg(int did,idtype_t type,id_t id)5996134Scasper klpd_unreg(int did, idtype_t type, id_t id)
6006134Scasper {
6016134Scasper door_handle_t dh;
6026134Scasper int res = 0;
6036134Scasper proc_t *p;
6046134Scasper pid_t pid;
6056134Scasper projid_t proj;
6066134Scasper kproject_t *kpp = NULL;
6076134Scasper credklpd_t *ckp;
6086134Scasper
6096134Scasper switch (type) {
6106134Scasper case P_PID:
6116134Scasper pid = (pid_t)id;
6126134Scasper break;
6136134Scasper case P_PROJID:
6146134Scasper proj = (projid_t)id;
6156134Scasper kpp = project_hold_by_id(proj, crgetzone(CRED()),
6166134Scasper PROJECT_HOLD_FIND);
6176134Scasper if (kpp == NULL)
6186134Scasper return (set_errno(ESRCH));
6196134Scasper break;
6206134Scasper default:
6216134Scasper return (set_errno(ENOTSUP));
6226134Scasper }
6236134Scasper
6246134Scasper dh = door_ki_lookup(did);
6256134Scasper if (dh == NULL) {
6266134Scasper if (kpp != NULL)
6276134Scasper project_rele(kpp);
6286134Scasper return (set_errno(EINVAL));
6296134Scasper }
6306134Scasper
6316134Scasper if (kpp != NULL) {
6326134Scasper mutex_enter(&klpd_mutex);
6336134Scasper if (kpp->kpj_klpd == NULL)
6346134Scasper res = ESRCH;
6356134Scasper else
636*12273SCasper.Dik@Sun.COM klpd_freelist(&kpp->kpj_klpd);
6376134Scasper mutex_exit(&klpd_mutex);
6386134Scasper project_rele(kpp);
6396134Scasper goto out;
6406134Scasper } else if ((int)pid > 0) {
6416134Scasper mutex_enter(&pidlock);
6426134Scasper p = prfind(pid);
6436134Scasper if (p == NULL) {
6446134Scasper mutex_exit(&pidlock);
6456134Scasper door_ki_rele(dh);
6466134Scasper return (set_errno(ESRCH));
6476134Scasper }
6486134Scasper mutex_enter(&p->p_crlock);
6496134Scasper mutex_exit(&pidlock);
6506134Scasper } else if (pid == 0) {
6516134Scasper p = curproc;
6526134Scasper mutex_enter(&p->p_crlock);
6536134Scasper } else {
6546134Scasper res = klpd_unreg_dh(dh);
6556134Scasper goto out;
6566134Scasper }
6576134Scasper
6586134Scasper ckp = crgetcrklpd(p->p_cred);
6596134Scasper if (ckp != NULL) {
6606134Scasper crklpd_setreg(ckp, NULL);
6616134Scasper } else {
6626134Scasper res = ESRCH;
6636134Scasper }
6646134Scasper mutex_exit(&p->p_crlock);
6656134Scasper
6666134Scasper out:
6676134Scasper door_ki_rele(dh);
6686134Scasper
6696134Scasper if (res != 0)
6706134Scasper return (set_errno(res));
6716134Scasper return (0);
6726134Scasper }
6736134Scasper
6746134Scasper void
crklpd_hold(credklpd_t * crkpd)6756134Scasper crklpd_hold(credklpd_t *crkpd)
6766134Scasper {
6776134Scasper atomic_add_32(&crkpd->crkl_ref, 1);
6786134Scasper }
6796134Scasper
6806134Scasper void
crklpd_rele(credklpd_t * crkpd)6816134Scasper crklpd_rele(credklpd_t *crkpd)
6826134Scasper {
6836134Scasper if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
6846134Scasper if (crkpd->crkl_reg != NULL)
6856134Scasper klpd_rele(crkpd->crkl_reg);
6866134Scasper mutex_destroy(&crkpd->crkl_lock);
6876134Scasper kmem_free(crkpd, sizeof (*crkpd));
6886134Scasper }
6896134Scasper }
6906134Scasper
6916134Scasper static credklpd_t *
crklpd_alloc(void)6926134Scasper crklpd_alloc(void)
6936134Scasper {
6946134Scasper credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
6956134Scasper
6966134Scasper mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
6976134Scasper res->crkl_ref = 1;
6986134Scasper res->crkl_reg = NULL;
6996134Scasper
7006134Scasper return (res);
7016134Scasper }
7026134Scasper
7036134Scasper void
crklpd_setreg(credklpd_t * crk,klpd_reg_t * new)7046134Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
7056134Scasper {
7066134Scasper klpd_reg_t *old;
7076134Scasper
7086134Scasper mutex_enter(&crk->crkl_lock);
7096134Scasper if (new == NULL) {
7106134Scasper old = crk->crkl_reg;
7116134Scasper if (old != NULL)
7126134Scasper klpd_unlink(old);
7136134Scasper } else {
7146134Scasper old = klpd_link(new, &crk->crkl_reg, B_TRUE);
7156134Scasper }
7166134Scasper mutex_exit(&crk->crkl_lock);
7176134Scasper
7186134Scasper if (old != NULL)
7196134Scasper klpd_rele(old);
7206134Scasper }
721*12273SCasper.Dik@Sun.COM
722*12273SCasper.Dik@Sun.COM /* Allocate and register the pfexec specific callback */
723*12273SCasper.Dik@Sun.COM int
pfexec_reg(int did)724*12273SCasper.Dik@Sun.COM pfexec_reg(int did)
725*12273SCasper.Dik@Sun.COM {
726*12273SCasper.Dik@Sun.COM door_handle_t dh;
727*12273SCasper.Dik@Sun.COM int err = secpolicy_pfexec_register(CRED());
728*12273SCasper.Dik@Sun.COM klpd_reg_t *pfx;
729*12273SCasper.Dik@Sun.COM door_info_t di;
730*12273SCasper.Dik@Sun.COM zone_t *myzone = crgetzone(CRED());
731*12273SCasper.Dik@Sun.COM
732*12273SCasper.Dik@Sun.COM if (err != 0)
733*12273SCasper.Dik@Sun.COM return (set_errno(err));
734*12273SCasper.Dik@Sun.COM
735*12273SCasper.Dik@Sun.COM dh = door_ki_lookup(did);
736*12273SCasper.Dik@Sun.COM if (dh == NULL || door_ki_info(dh, &di) != 0)
737*12273SCasper.Dik@Sun.COM return (set_errno(EBADF));
738*12273SCasper.Dik@Sun.COM
739*12273SCasper.Dik@Sun.COM pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
740*12273SCasper.Dik@Sun.COM
741*12273SCasper.Dik@Sun.COM pfx->klpd_door = dh;
742*12273SCasper.Dik@Sun.COM pfx->klpd_door_pid = di.di_target;
743*12273SCasper.Dik@Sun.COM pfx->klpd_ref = 1;
744*12273SCasper.Dik@Sun.COM pfx->klpd_cred = NULL;
745*12273SCasper.Dik@Sun.COM mutex_enter(&myzone->zone_lock);
746*12273SCasper.Dik@Sun.COM pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
747*12273SCasper.Dik@Sun.COM mutex_exit(&myzone->zone_lock);
748*12273SCasper.Dik@Sun.COM if (pfx != NULL)
749*12273SCasper.Dik@Sun.COM klpd_rele(pfx);
750*12273SCasper.Dik@Sun.COM
751*12273SCasper.Dik@Sun.COM return (0);
752*12273SCasper.Dik@Sun.COM }
753*12273SCasper.Dik@Sun.COM
754*12273SCasper.Dik@Sun.COM int
pfexec_unreg(int did)755*12273SCasper.Dik@Sun.COM pfexec_unreg(int did)
756*12273SCasper.Dik@Sun.COM {
757*12273SCasper.Dik@Sun.COM door_handle_t dh;
758*12273SCasper.Dik@Sun.COM int err = 0;
759*12273SCasper.Dik@Sun.COM zone_t *myzone = crgetzone(CRED());
760*12273SCasper.Dik@Sun.COM klpd_reg_t *pfd;
761*12273SCasper.Dik@Sun.COM
762*12273SCasper.Dik@Sun.COM dh = door_ki_lookup(did);
763*12273SCasper.Dik@Sun.COM if (dh == NULL)
764*12273SCasper.Dik@Sun.COM return (set_errno(EBADF));
765*12273SCasper.Dik@Sun.COM
766*12273SCasper.Dik@Sun.COM mutex_enter(&myzone->zone_lock);
767*12273SCasper.Dik@Sun.COM pfd = myzone->zone_pfexecd;
768*12273SCasper.Dik@Sun.COM if (pfd != NULL && pfd->klpd_door == dh) {
769*12273SCasper.Dik@Sun.COM klpd_unlink(pfd);
770*12273SCasper.Dik@Sun.COM } else {
771*12273SCasper.Dik@Sun.COM pfd = NULL;
772*12273SCasper.Dik@Sun.COM err = EINVAL;
773*12273SCasper.Dik@Sun.COM }
774*12273SCasper.Dik@Sun.COM mutex_exit(&myzone->zone_lock);
775*12273SCasper.Dik@Sun.COM door_ki_rele(dh);
776*12273SCasper.Dik@Sun.COM /*
777*12273SCasper.Dik@Sun.COM * crfree() cannot be called with zone_lock held; it is called
778*12273SCasper.Dik@Sun.COM * indirectly through closing the door handle
779*12273SCasper.Dik@Sun.COM */
780*12273SCasper.Dik@Sun.COM if (pfd != NULL)
781*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
782*12273SCasper.Dik@Sun.COM if (err != 0)
783*12273SCasper.Dik@Sun.COM return (set_errno(err));
784*12273SCasper.Dik@Sun.COM return (0);
785*12273SCasper.Dik@Sun.COM }
786*12273SCasper.Dik@Sun.COM
787*12273SCasper.Dik@Sun.COM static int
get_path(char * buf,const char * path,int len)788*12273SCasper.Dik@Sun.COM get_path(char *buf, const char *path, int len)
789*12273SCasper.Dik@Sun.COM {
790*12273SCasper.Dik@Sun.COM size_t lc;
791*12273SCasper.Dik@Sun.COM char *s;
792*12273SCasper.Dik@Sun.COM
793*12273SCasper.Dik@Sun.COM if (len < 0)
794*12273SCasper.Dik@Sun.COM len = strlen(path);
795*12273SCasper.Dik@Sun.COM
796*12273SCasper.Dik@Sun.COM if (*path == '/' && len < MAXPATHLEN) {
797*12273SCasper.Dik@Sun.COM (void) strcpy(buf, path);
798*12273SCasper.Dik@Sun.COM return (0);
799*12273SCasper.Dik@Sun.COM }
800*12273SCasper.Dik@Sun.COM /*
801*12273SCasper.Dik@Sun.COM * Build the pathname using the current directory + resolve pathname.
802*12273SCasper.Dik@Sun.COM * The resolve pathname either starts with a normal component and
803*12273SCasper.Dik@Sun.COM * we can just concatenate them or it starts with one
804*12273SCasper.Dik@Sun.COM * or more ".." component and we can remove those; the
805*12273SCasper.Dik@Sun.COM * last one cannot be a ".." and the current directory has
806*12273SCasper.Dik@Sun.COM * more components than the number of ".." in the resolved pathname.
807*12273SCasper.Dik@Sun.COM */
808*12273SCasper.Dik@Sun.COM if (dogetcwd(buf, MAXPATHLEN) != 0)
809*12273SCasper.Dik@Sun.COM return (-1);
810*12273SCasper.Dik@Sun.COM
811*12273SCasper.Dik@Sun.COM lc = strlen(buf);
812*12273SCasper.Dik@Sun.COM
813*12273SCasper.Dik@Sun.COM while (len > 3 && strncmp("../", path, 3) == 0) {
814*12273SCasper.Dik@Sun.COM len -= 3;
815*12273SCasper.Dik@Sun.COM path += 3;
816*12273SCasper.Dik@Sun.COM
817*12273SCasper.Dik@Sun.COM s = strrchr(buf, '/');
818*12273SCasper.Dik@Sun.COM if (s == NULL || s == buf)
819*12273SCasper.Dik@Sun.COM return (-1);
820*12273SCasper.Dik@Sun.COM
821*12273SCasper.Dik@Sun.COM *s = '\0';
822*12273SCasper.Dik@Sun.COM lc = s - buf;
823*12273SCasper.Dik@Sun.COM }
824*12273SCasper.Dik@Sun.COM /* Add a "/" and a NUL */
825*12273SCasper.Dik@Sun.COM if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
826*12273SCasper.Dik@Sun.COM return (-1);
827*12273SCasper.Dik@Sun.COM
828*12273SCasper.Dik@Sun.COM buf[lc] = '/';
829*12273SCasper.Dik@Sun.COM (void) strcpy(buf + lc + 1, path);
830*12273SCasper.Dik@Sun.COM
831*12273SCasper.Dik@Sun.COM return (0);
832*12273SCasper.Dik@Sun.COM }
833*12273SCasper.Dik@Sun.COM
834*12273SCasper.Dik@Sun.COM /*
835*12273SCasper.Dik@Sun.COM * Perform the pfexec upcall.
836*12273SCasper.Dik@Sun.COM *
837*12273SCasper.Dik@Sun.COM * The pfexec upcall is different from the klpd_upcall in that a failure
838*12273SCasper.Dik@Sun.COM * will lead to a denial of execution.
839*12273SCasper.Dik@Sun.COM */
840*12273SCasper.Dik@Sun.COM int
pfexec_call(const cred_t * cr,struct pathname * rpnp,cred_t ** pfcr,boolean_t * scrub)841*12273SCasper.Dik@Sun.COM pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
842*12273SCasper.Dik@Sun.COM boolean_t *scrub)
843*12273SCasper.Dik@Sun.COM {
844*12273SCasper.Dik@Sun.COM klpd_reg_t *pfd;
845*12273SCasper.Dik@Sun.COM pfexec_arg_t *pap;
846*12273SCasper.Dik@Sun.COM pfexec_reply_t pr, *prp;
847*12273SCasper.Dik@Sun.COM door_arg_t da;
848*12273SCasper.Dik@Sun.COM int dres;
849*12273SCasper.Dik@Sun.COM cred_t *ncr = NULL;
850*12273SCasper.Dik@Sun.COM int err = -1;
851*12273SCasper.Dik@Sun.COM priv_set_t *iset;
852*12273SCasper.Dik@Sun.COM priv_set_t *lset;
853*12273SCasper.Dik@Sun.COM zone_t *myzone = crgetzone(CRED());
854*12273SCasper.Dik@Sun.COM size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
855*12273SCasper.Dik@Sun.COM
856*12273SCasper.Dik@Sun.COM /* Find registration */
857*12273SCasper.Dik@Sun.COM mutex_enter(&myzone->zone_lock);
858*12273SCasper.Dik@Sun.COM if ((pfd = myzone->zone_pfexecd) != NULL)
859*12273SCasper.Dik@Sun.COM klpd_hold(pfd);
860*12273SCasper.Dik@Sun.COM mutex_exit(&myzone->zone_lock);
861*12273SCasper.Dik@Sun.COM
862*12273SCasper.Dik@Sun.COM if (pfd == NULL)
863*12273SCasper.Dik@Sun.COM return (0);
864*12273SCasper.Dik@Sun.COM
865*12273SCasper.Dik@Sun.COM if (pfd->klpd_door_pid == curproc->p_pid) {
866*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
867*12273SCasper.Dik@Sun.COM return (0);
868*12273SCasper.Dik@Sun.COM }
869*12273SCasper.Dik@Sun.COM
870*12273SCasper.Dik@Sun.COM pap = kmem_zalloc(pasize, KM_SLEEP);
871*12273SCasper.Dik@Sun.COM
872*12273SCasper.Dik@Sun.COM if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
873*12273SCasper.Dik@Sun.COM goto out1;
874*12273SCasper.Dik@Sun.COM
875*12273SCasper.Dik@Sun.COM pap->pfa_vers = PFEXEC_ARG_VERS;
876*12273SCasper.Dik@Sun.COM pap->pfa_call = PFEXEC_EXEC_ATTRS;
877*12273SCasper.Dik@Sun.COM pap->pfa_len = pasize;
878*12273SCasper.Dik@Sun.COM pap->pfa_uid = crgetruid(cr);
879*12273SCasper.Dik@Sun.COM
880*12273SCasper.Dik@Sun.COM da.data_ptr = (char *)pap;
881*12273SCasper.Dik@Sun.COM da.data_size = pap->pfa_len;
882*12273SCasper.Dik@Sun.COM da.desc_ptr = NULL;
883*12273SCasper.Dik@Sun.COM da.desc_num = 0;
884*12273SCasper.Dik@Sun.COM da.rbuf = (char *)≺
885*12273SCasper.Dik@Sun.COM da.rsize = sizeof (pr);
886*12273SCasper.Dik@Sun.COM
887*12273SCasper.Dik@Sun.COM while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
888*12273SCasper.Dik@Sun.COM switch (dres) {
889*12273SCasper.Dik@Sun.COM case EAGAIN:
890*12273SCasper.Dik@Sun.COM delay(1);
891*12273SCasper.Dik@Sun.COM continue;
892*12273SCasper.Dik@Sun.COM case EINVAL:
893*12273SCasper.Dik@Sun.COM case EBADF:
894*12273SCasper.Dik@Sun.COM /* FALLTHROUGH */
895*12273SCasper.Dik@Sun.COM case EINTR:
896*12273SCasper.Dik@Sun.COM /* FALLTHROUGH */
897*12273SCasper.Dik@Sun.COM default:
898*12273SCasper.Dik@Sun.COM goto out;
899*12273SCasper.Dik@Sun.COM }
900*12273SCasper.Dik@Sun.COM }
901*12273SCasper.Dik@Sun.COM
902*12273SCasper.Dik@Sun.COM prp = (pfexec_reply_t *)da.rbuf;
903*12273SCasper.Dik@Sun.COM /*
904*12273SCasper.Dik@Sun.COM * Check the size of the result and the alignment of the
905*12273SCasper.Dik@Sun.COM * privilege sets.
906*12273SCasper.Dik@Sun.COM */
907*12273SCasper.Dik@Sun.COM if (da.rsize < sizeof (pr) ||
908*12273SCasper.Dik@Sun.COM prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
909*12273SCasper.Dik@Sun.COM prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
910*12273SCasper.Dik@Sun.COM (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
911*12273SCasper.Dik@Sun.COM (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0)
912*12273SCasper.Dik@Sun.COM goto out;
913*12273SCasper.Dik@Sun.COM
914*12273SCasper.Dik@Sun.COM /*
915*12273SCasper.Dik@Sun.COM * Get results:
916*12273SCasper.Dik@Sun.COM * allow/allow with additional credentials/disallow[*]
917*12273SCasper.Dik@Sun.COM *
918*12273SCasper.Dik@Sun.COM * euid, uid, egid, gid, privs, and limitprivs
919*12273SCasper.Dik@Sun.COM * We now have somewhat more flexibility we could even set E and P
920*12273SCasper.Dik@Sun.COM * judiciously but that would break some currently valid assumptions
921*12273SCasper.Dik@Sun.COM * [*] Disallow is not readily supported by always including
922*12273SCasper.Dik@Sun.COM * the Basic Solaris User profile in all user's profiles.
923*12273SCasper.Dik@Sun.COM */
924*12273SCasper.Dik@Sun.COM
925*12273SCasper.Dik@Sun.COM if (!prp->pfr_allowed) {
926*12273SCasper.Dik@Sun.COM err = EACCES;
927*12273SCasper.Dik@Sun.COM goto out;
928*12273SCasper.Dik@Sun.COM }
929*12273SCasper.Dik@Sun.COM if (!prp->pfr_setcred) {
930*12273SCasper.Dik@Sun.COM err = 0;
931*12273SCasper.Dik@Sun.COM goto out;
932*12273SCasper.Dik@Sun.COM }
933*12273SCasper.Dik@Sun.COM ncr = crdup((cred_t *)cr);
934*12273SCasper.Dik@Sun.COM
935*12273SCasper.Dik@Sun.COM /*
936*12273SCasper.Dik@Sun.COM * Generate the new credential set scrubenv if ruid != euid (or set)
937*12273SCasper.Dik@Sun.COM * the "I'm set-uid flag" but that is not inherited so scrubbing
938*12273SCasper.Dik@Sun.COM * the environment is a requirement.
939*12273SCasper.Dik@Sun.COM */
940*12273SCasper.Dik@Sun.COM /* Set uids or gids, note that -1 will do the right thing */
941*12273SCasper.Dik@Sun.COM if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
942*12273SCasper.Dik@Sun.COM goto out;
943*12273SCasper.Dik@Sun.COM if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
944*12273SCasper.Dik@Sun.COM goto out;
945*12273SCasper.Dik@Sun.COM
946*12273SCasper.Dik@Sun.COM *scrub = prp->pfr_scrubenv;
947*12273SCasper.Dik@Sun.COM
948*12273SCasper.Dik@Sun.COM if (prp->pfr_clearflag)
949*12273SCasper.Dik@Sun.COM CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
950*12273SCasper.Dik@Sun.COM
951*12273SCasper.Dik@Sun.COM /* We cannot exceed our Limit set, no matter what */
952*12273SCasper.Dik@Sun.COM iset = PFEXEC_REPLY_IPRIV(prp);
953*12273SCasper.Dik@Sun.COM
954*12273SCasper.Dik@Sun.COM if (iset != NULL) {
955*12273SCasper.Dik@Sun.COM if (!priv_issubset(iset, &CR_LPRIV(ncr)))
956*12273SCasper.Dik@Sun.COM goto out;
957*12273SCasper.Dik@Sun.COM priv_union(iset, &CR_IPRIV(ncr));
958*12273SCasper.Dik@Sun.COM }
959*12273SCasper.Dik@Sun.COM
960*12273SCasper.Dik@Sun.COM /* Nor can we increate our Limit set itself */
961*12273SCasper.Dik@Sun.COM lset = PFEXEC_REPLY_LPRIV(prp);
962*12273SCasper.Dik@Sun.COM
963*12273SCasper.Dik@Sun.COM if (lset != NULL) {
964*12273SCasper.Dik@Sun.COM if (!priv_issubset(lset, &CR_LPRIV(ncr)))
965*12273SCasper.Dik@Sun.COM goto out;
966*12273SCasper.Dik@Sun.COM CR_LPRIV(ncr) = *lset;
967*12273SCasper.Dik@Sun.COM }
968*12273SCasper.Dik@Sun.COM
969*12273SCasper.Dik@Sun.COM /* Exec will do the standard set operations */
970*12273SCasper.Dik@Sun.COM
971*12273SCasper.Dik@Sun.COM err = 0;
972*12273SCasper.Dik@Sun.COM out:
973*12273SCasper.Dik@Sun.COM if (da.rbuf != (char *)&pr)
974*12273SCasper.Dik@Sun.COM kmem_free(da.rbuf, da.rsize);
975*12273SCasper.Dik@Sun.COM out1:
976*12273SCasper.Dik@Sun.COM kmem_free(pap, pasize);
977*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
978*12273SCasper.Dik@Sun.COM if (ncr != NULL) {
979*12273SCasper.Dik@Sun.COM if (err == 0)
980*12273SCasper.Dik@Sun.COM *pfcr = ncr;
981*12273SCasper.Dik@Sun.COM else
982*12273SCasper.Dik@Sun.COM crfree(ncr);
983*12273SCasper.Dik@Sun.COM }
984*12273SCasper.Dik@Sun.COM return (err);
985*12273SCasper.Dik@Sun.COM }
986*12273SCasper.Dik@Sun.COM
987*12273SCasper.Dik@Sun.COM int
get_forced_privs(const cred_t * cr,const char * respn,priv_set_t * set)988*12273SCasper.Dik@Sun.COM get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
989*12273SCasper.Dik@Sun.COM {
990*12273SCasper.Dik@Sun.COM klpd_reg_t *pfd;
991*12273SCasper.Dik@Sun.COM pfexec_arg_t *pap;
992*12273SCasper.Dik@Sun.COM door_arg_t da;
993*12273SCasper.Dik@Sun.COM int dres;
994*12273SCasper.Dik@Sun.COM int err = -1;
995*12273SCasper.Dik@Sun.COM priv_set_t *fset, pmem;
996*12273SCasper.Dik@Sun.COM cred_t *zkcr;
997*12273SCasper.Dik@Sun.COM zone_t *myzone = crgetzone(cr);
998*12273SCasper.Dik@Sun.COM size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
999*12273SCasper.Dik@Sun.COM
1000*12273SCasper.Dik@Sun.COM mutex_enter(&myzone->zone_lock);
1001*12273SCasper.Dik@Sun.COM if ((pfd = myzone->zone_pfexecd) != NULL)
1002*12273SCasper.Dik@Sun.COM klpd_hold(pfd);
1003*12273SCasper.Dik@Sun.COM mutex_exit(&myzone->zone_lock);
1004*12273SCasper.Dik@Sun.COM
1005*12273SCasper.Dik@Sun.COM if (pfd == NULL)
1006*12273SCasper.Dik@Sun.COM return (-1);
1007*12273SCasper.Dik@Sun.COM
1008*12273SCasper.Dik@Sun.COM if (pfd->klpd_door_pid == curproc->p_pid) {
1009*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
1010*12273SCasper.Dik@Sun.COM return (0);
1011*12273SCasper.Dik@Sun.COM }
1012*12273SCasper.Dik@Sun.COM
1013*12273SCasper.Dik@Sun.COM pap = kmem_zalloc(pasize, KM_SLEEP);
1014*12273SCasper.Dik@Sun.COM
1015*12273SCasper.Dik@Sun.COM if (get_path(pap->pfa_path, respn, -1) == -1)
1016*12273SCasper.Dik@Sun.COM goto out1;
1017*12273SCasper.Dik@Sun.COM
1018*12273SCasper.Dik@Sun.COM pap->pfa_vers = PFEXEC_ARG_VERS;
1019*12273SCasper.Dik@Sun.COM pap->pfa_call = PFEXEC_FORCED_PRIVS;
1020*12273SCasper.Dik@Sun.COM pap->pfa_len = pasize;
1021*12273SCasper.Dik@Sun.COM pap->pfa_uid = (uid_t)-1; /* Not relevant */
1022*12273SCasper.Dik@Sun.COM
1023*12273SCasper.Dik@Sun.COM da.data_ptr = (char *)pap;
1024*12273SCasper.Dik@Sun.COM da.data_size = pap->pfa_len;
1025*12273SCasper.Dik@Sun.COM da.desc_ptr = NULL;
1026*12273SCasper.Dik@Sun.COM da.desc_num = 0;
1027*12273SCasper.Dik@Sun.COM da.rbuf = (char *)&pmem;
1028*12273SCasper.Dik@Sun.COM da.rsize = sizeof (pmem);
1029*12273SCasper.Dik@Sun.COM
1030*12273SCasper.Dik@Sun.COM while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1031*12273SCasper.Dik@Sun.COM switch (dres) {
1032*12273SCasper.Dik@Sun.COM case EAGAIN:
1033*12273SCasper.Dik@Sun.COM delay(1);
1034*12273SCasper.Dik@Sun.COM continue;
1035*12273SCasper.Dik@Sun.COM case EINVAL:
1036*12273SCasper.Dik@Sun.COM case EBADF:
1037*12273SCasper.Dik@Sun.COM case EINTR:
1038*12273SCasper.Dik@Sun.COM default:
1039*12273SCasper.Dik@Sun.COM goto out;
1040*12273SCasper.Dik@Sun.COM }
1041*12273SCasper.Dik@Sun.COM }
1042*12273SCasper.Dik@Sun.COM
1043*12273SCasper.Dik@Sun.COM /*
1044*12273SCasper.Dik@Sun.COM * Check the size of the result, it's a privilege set.
1045*12273SCasper.Dik@Sun.COM */
1046*12273SCasper.Dik@Sun.COM if (da.rsize != sizeof (priv_set_t))
1047*12273SCasper.Dik@Sun.COM goto out;
1048*12273SCasper.Dik@Sun.COM
1049*12273SCasper.Dik@Sun.COM fset = (priv_set_t *)da.rbuf;
1050*12273SCasper.Dik@Sun.COM
1051*12273SCasper.Dik@Sun.COM /*
1052*12273SCasper.Dik@Sun.COM * We restrict the forced privileges with whatever is available in
1053*12273SCasper.Dik@Sun.COM * the current zone.
1054*12273SCasper.Dik@Sun.COM */
1055*12273SCasper.Dik@Sun.COM zkcr = zone_kcred();
1056*12273SCasper.Dik@Sun.COM priv_intersect(&CR_LPRIV(zkcr), fset);
1057*12273SCasper.Dik@Sun.COM
1058*12273SCasper.Dik@Sun.COM /*
1059*12273SCasper.Dik@Sun.COM * But we fail if the forced privileges are not found in the current
1060*12273SCasper.Dik@Sun.COM * Limit set.
1061*12273SCasper.Dik@Sun.COM */
1062*12273SCasper.Dik@Sun.COM if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1063*12273SCasper.Dik@Sun.COM err = EACCES;
1064*12273SCasper.Dik@Sun.COM } else if (!priv_isemptyset(fset)) {
1065*12273SCasper.Dik@Sun.COM err = 0;
1066*12273SCasper.Dik@Sun.COM *set = *fset;
1067*12273SCasper.Dik@Sun.COM }
1068*12273SCasper.Dik@Sun.COM out:
1069*12273SCasper.Dik@Sun.COM if (da.rbuf != (char *)&pmem)
1070*12273SCasper.Dik@Sun.COM kmem_free(da.rbuf, da.rsize);
1071*12273SCasper.Dik@Sun.COM out1:
1072*12273SCasper.Dik@Sun.COM kmem_free(pap, pasize);
1073*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
1074*12273SCasper.Dik@Sun.COM return (err);
1075*12273SCasper.Dik@Sun.COM }
1076*12273SCasper.Dik@Sun.COM
1077*12273SCasper.Dik@Sun.COM int
check_user_privs(const cred_t * cr,const priv_set_t * set)1078*12273SCasper.Dik@Sun.COM check_user_privs(const cred_t *cr, const priv_set_t *set)
1079*12273SCasper.Dik@Sun.COM {
1080*12273SCasper.Dik@Sun.COM klpd_reg_t *pfd;
1081*12273SCasper.Dik@Sun.COM pfexec_arg_t *pap;
1082*12273SCasper.Dik@Sun.COM door_arg_t da;
1083*12273SCasper.Dik@Sun.COM int dres;
1084*12273SCasper.Dik@Sun.COM int err = -1;
1085*12273SCasper.Dik@Sun.COM zone_t *myzone = crgetzone(cr);
1086*12273SCasper.Dik@Sun.COM size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1087*12273SCasper.Dik@Sun.COM uint32_t res;
1088*12273SCasper.Dik@Sun.COM
1089*12273SCasper.Dik@Sun.COM mutex_enter(&myzone->zone_lock);
1090*12273SCasper.Dik@Sun.COM if ((pfd = myzone->zone_pfexecd) != NULL)
1091*12273SCasper.Dik@Sun.COM klpd_hold(pfd);
1092*12273SCasper.Dik@Sun.COM mutex_exit(&myzone->zone_lock);
1093*12273SCasper.Dik@Sun.COM
1094*12273SCasper.Dik@Sun.COM if (pfd == NULL)
1095*12273SCasper.Dik@Sun.COM return (-1);
1096*12273SCasper.Dik@Sun.COM
1097*12273SCasper.Dik@Sun.COM if (pfd->klpd_door_pid == curproc->p_pid) {
1098*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
1099*12273SCasper.Dik@Sun.COM return (0);
1100*12273SCasper.Dik@Sun.COM }
1101*12273SCasper.Dik@Sun.COM
1102*12273SCasper.Dik@Sun.COM pap = kmem_zalloc(pasize, KM_SLEEP);
1103*12273SCasper.Dik@Sun.COM
1104*12273SCasper.Dik@Sun.COM *(priv_set_t *)&pap->pfa_buf = *set;
1105*12273SCasper.Dik@Sun.COM
1106*12273SCasper.Dik@Sun.COM pap->pfa_vers = PFEXEC_ARG_VERS;
1107*12273SCasper.Dik@Sun.COM pap->pfa_call = PFEXEC_USER_PRIVS;
1108*12273SCasper.Dik@Sun.COM pap->pfa_len = pasize;
1109*12273SCasper.Dik@Sun.COM pap->pfa_uid = crgetruid(cr);
1110*12273SCasper.Dik@Sun.COM
1111*12273SCasper.Dik@Sun.COM da.data_ptr = (char *)pap;
1112*12273SCasper.Dik@Sun.COM da.data_size = pap->pfa_len;
1113*12273SCasper.Dik@Sun.COM da.desc_ptr = NULL;
1114*12273SCasper.Dik@Sun.COM da.desc_num = 0;
1115*12273SCasper.Dik@Sun.COM da.rbuf = (char *)&res;
1116*12273SCasper.Dik@Sun.COM da.rsize = sizeof (res);
1117*12273SCasper.Dik@Sun.COM
1118*12273SCasper.Dik@Sun.COM while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1119*12273SCasper.Dik@Sun.COM switch (dres) {
1120*12273SCasper.Dik@Sun.COM case EAGAIN:
1121*12273SCasper.Dik@Sun.COM delay(1);
1122*12273SCasper.Dik@Sun.COM continue;
1123*12273SCasper.Dik@Sun.COM case EINVAL:
1124*12273SCasper.Dik@Sun.COM case EBADF:
1125*12273SCasper.Dik@Sun.COM case EINTR:
1126*12273SCasper.Dik@Sun.COM default:
1127*12273SCasper.Dik@Sun.COM goto out;
1128*12273SCasper.Dik@Sun.COM }
1129*12273SCasper.Dik@Sun.COM }
1130*12273SCasper.Dik@Sun.COM
1131*12273SCasper.Dik@Sun.COM /*
1132*12273SCasper.Dik@Sun.COM * Check the size of the result.
1133*12273SCasper.Dik@Sun.COM */
1134*12273SCasper.Dik@Sun.COM if (da.rsize != sizeof (res))
1135*12273SCasper.Dik@Sun.COM goto out;
1136*12273SCasper.Dik@Sun.COM
1137*12273SCasper.Dik@Sun.COM if (*(uint32_t *)da.rbuf == 1)
1138*12273SCasper.Dik@Sun.COM err = 0;
1139*12273SCasper.Dik@Sun.COM out:
1140*12273SCasper.Dik@Sun.COM if (da.rbuf != (char *)&res)
1141*12273SCasper.Dik@Sun.COM kmem_free(da.rbuf, da.rsize);
1142*12273SCasper.Dik@Sun.COM out1:
1143*12273SCasper.Dik@Sun.COM kmem_free(pap, pasize);
1144*12273SCasper.Dik@Sun.COM klpd_rele(pfd);
1145*12273SCasper.Dik@Sun.COM return (err);
1146*12273SCasper.Dik@Sun.COM }
1147