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.
24*b01b59e3SRobert Mustacchi * Copyright 2015, Joyent, Inc.
25ddf7fe95Scasper */
26ddf7fe95Scasper
27ddf7fe95Scasper #include <sys/atomic.h>
28ddf7fe95Scasper #include <sys/door.h>
29ddf7fe95Scasper #include <sys/proc.h>
30ddf7fe95Scasper #include <sys/cred_impl.h>
31ddf7fe95Scasper #include <sys/policy.h>
32ddf7fe95Scasper #include <sys/priv.h>
33ddf7fe95Scasper #include <sys/klpd.h>
34ddf7fe95Scasper #include <sys/errno.h>
35ddf7fe95Scasper #include <sys/kmem.h>
36ddf7fe95Scasper #include <sys/project.h>
37ddf7fe95Scasper #include <sys/systm.h>
38ddf7fe95Scasper #include <sys/sysmacros.h>
39ddf7fe95Scasper #include <sys/pathname.h>
40ddf7fe95Scasper #include <sys/varargs.h>
41ddf7fe95Scasper #include <sys/zone.h>
42ddf7fe95Scasper #include <netinet/in.h>
43ddf7fe95Scasper
44ddf7fe95Scasper #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
45ddf7fe95Scasper
46ddf7fe95Scasper static kmutex_t klpd_mutex;
47ddf7fe95Scasper
48ddf7fe95Scasper typedef struct klpd_reg {
49ddf7fe95Scasper struct klpd_reg *klpd_next;
50ddf7fe95Scasper struct klpd_reg **klpd_refp;
51ddf7fe95Scasper door_handle_t klpd_door;
52ddf7fe95Scasper pid_t klpd_door_pid;
53ddf7fe95Scasper priv_set_t klpd_pset;
54ddf7fe95Scasper cred_t *klpd_cred;
55ddf7fe95Scasper int klpd_indel; /* Disabled */
56ddf7fe95Scasper uint32_t klpd_ref;
57ddf7fe95Scasper } klpd_reg_t;
58ddf7fe95Scasper
59ddf7fe95Scasper
60ddf7fe95Scasper /*
61ddf7fe95Scasper * This data structure hangs off the credential of a process; the
62ddf7fe95Scasper * credential is finalized and cannot be changed; but this structure
63ddf7fe95Scasper * can be changed when a new door server for the particular group
64ddf7fe95Scasper * needs to be registered. It is refcounted and shared between
65ddf7fe95Scasper * processes with common ancestry.
66ddf7fe95Scasper *
67ddf7fe95Scasper * The reference count is atomically updated.
68ddf7fe95Scasper *
69ddf7fe95Scasper * But the registration probably needs to be updated under a lock.
70ddf7fe95Scasper */
71ddf7fe95Scasper typedef struct credklpd {
72ddf7fe95Scasper kmutex_t crkl_lock;
73ddf7fe95Scasper klpd_reg_t *crkl_reg;
74ddf7fe95Scasper uint32_t crkl_ref;
75ddf7fe95Scasper } credklpd_t;
76ddf7fe95Scasper
77ddf7fe95Scasper klpd_reg_t *klpd_list;
78ddf7fe95Scasper
79ddf7fe95Scasper static void klpd_unlink(klpd_reg_t *);
80ddf7fe95Scasper static int klpd_unreg_dh(door_handle_t);
81ddf7fe95Scasper
82ddf7fe95Scasper static credklpd_t *crklpd_alloc(void);
83ddf7fe95Scasper
84ddf7fe95Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
85ddf7fe95Scasper
86ddf7fe95Scasper extern size_t max_vnode_path;
87ddf7fe95Scasper
88ddf7fe95Scasper void
klpd_rele(klpd_reg_t * p)89ddf7fe95Scasper klpd_rele(klpd_reg_t *p)
90ddf7fe95Scasper {
911a5e258fSJosef 'Jeff' Sipek if (atomic_dec_32_nv(&p->klpd_ref) == 0) {
92ddf7fe95Scasper if (p->klpd_refp != NULL)
93ddf7fe95Scasper klpd_unlink(p);
94ddf7fe95Scasper if (p->klpd_cred != NULL)
95ddf7fe95Scasper crfree(p->klpd_cred);
96ddf7fe95Scasper door_ki_rele(p->klpd_door);
97ddf7fe95Scasper kmem_free(p, sizeof (*p));
98ddf7fe95Scasper }
99ddf7fe95Scasper }
100ddf7fe95Scasper
101ddf7fe95Scasper /*
102ddf7fe95Scasper * In order to be able to walk the lists, we can't unlink the entry
103ddf7fe95Scasper * until the reference count drops to 0. If we remove it too soon,
104ddf7fe95Scasper * list walkers will terminate when they happen to call a now orphaned
105ddf7fe95Scasper * entry.
106ddf7fe95Scasper */
107ddf7fe95Scasper static klpd_reg_t *
klpd_rele_next(klpd_reg_t * p)108ddf7fe95Scasper klpd_rele_next(klpd_reg_t *p)
109ddf7fe95Scasper {
110ddf7fe95Scasper klpd_reg_t *r = p->klpd_next;
111ddf7fe95Scasper
112ddf7fe95Scasper klpd_rele(p);
113ddf7fe95Scasper return (r);
114ddf7fe95Scasper }
115ddf7fe95Scasper
116ddf7fe95Scasper
117ddf7fe95Scasper static void
klpd_hold(klpd_reg_t * p)118ddf7fe95Scasper klpd_hold(klpd_reg_t *p)
119ddf7fe95Scasper {
1201a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&p->klpd_ref);
121ddf7fe95Scasper }
122ddf7fe95Scasper
123ddf7fe95Scasper /*
124ddf7fe95Scasper * Remove registration from where it is registered. Returns next in list.
125ddf7fe95Scasper */
126ddf7fe95Scasper static void
klpd_unlink(klpd_reg_t * p)127ddf7fe95Scasper klpd_unlink(klpd_reg_t *p)
128ddf7fe95Scasper {
129ddf7fe95Scasper ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
130ddf7fe95Scasper
131ddf7fe95Scasper if (p->klpd_refp != NULL)
132ddf7fe95Scasper *p->klpd_refp = p->klpd_next;
133ddf7fe95Scasper
134ddf7fe95Scasper if (p->klpd_next != NULL)
135ddf7fe95Scasper p->klpd_next->klpd_refp = p->klpd_refp;
136ddf7fe95Scasper p->klpd_refp = NULL;
137ddf7fe95Scasper }
138ddf7fe95Scasper
139ddf7fe95Scasper /*
140134a1f4eSCasper H.S. Dik * Remove all elements of the klpd list and decrement their refcnts.
141ddf7fe95Scasper * The lock guarding the list should be held; this function is
142134a1f4eSCasper H.S. Dik * called when we are sure we want to destroy the list completely
143134a1f4eSCasper H.S. Dik * list but not so sure that the reference counts of all elements have
144134a1f4eSCasper H.S. Dik * dropped back to 1.
145ddf7fe95Scasper */
146ddf7fe95Scasper void
klpd_freelist(klpd_reg_t ** pp)147134a1f4eSCasper H.S. Dik klpd_freelist(klpd_reg_t **pp)
148ddf7fe95Scasper {
149134a1f4eSCasper H.S. Dik klpd_reg_t *p;
150134a1f4eSCasper H.S. Dik
151134a1f4eSCasper H.S. Dik while ((p = *pp) != NULL) {
152ddf7fe95Scasper klpd_unlink(p);
153ddf7fe95Scasper klpd_rele(p);
154ddf7fe95Scasper }
155134a1f4eSCasper H.S. Dik }
156ddf7fe95Scasper
157ddf7fe95Scasper /*
158ddf7fe95Scasper * Link new entry in list. The Boolean argument specifies whether this
159ddf7fe95Scasper * list can contain only a single item or multiple items.
160ddf7fe95Scasper * Returns the entry which needs to be released if single is B_TRUE.
161ddf7fe95Scasper */
162ddf7fe95Scasper static klpd_reg_t *
klpd_link(klpd_reg_t * p,klpd_reg_t ** listp,boolean_t single)163ddf7fe95Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
164ddf7fe95Scasper {
165ddf7fe95Scasper klpd_reg_t *old = *listp;
166ddf7fe95Scasper
167ddf7fe95Scasper ASSERT(p->klpd_ref == 1);
168ddf7fe95Scasper
169ddf7fe95Scasper ASSERT(old == NULL || *old->klpd_refp == old);
170ddf7fe95Scasper p->klpd_refp = listp;
171ddf7fe95Scasper p->klpd_next = single ? NULL : old;
172ddf7fe95Scasper *listp = p;
173ddf7fe95Scasper if (old != NULL) {
174ddf7fe95Scasper if (single) {
175ddf7fe95Scasper ASSERT(old->klpd_next == NULL);
176ddf7fe95Scasper old->klpd_refp = NULL;
177ddf7fe95Scasper return (old);
178ddf7fe95Scasper } else
179ddf7fe95Scasper old->klpd_refp = &p->klpd_next;
180ddf7fe95Scasper }
181ddf7fe95Scasper return (NULL);
182ddf7fe95Scasper }
183ddf7fe95Scasper
184ddf7fe95Scasper /*
185ddf7fe95Scasper * The typical call consists of:
186ddf7fe95Scasper * - priv_set_t
187ddf7fe95Scasper * - some integer data (type, value)
188ddf7fe95Scasper * for now, it's just one bit.
189ddf7fe95Scasper */
190ddf7fe95Scasper static klpd_head_t *
klpd_marshall(klpd_reg_t * p,const priv_set_t * rq,va_list ap)191ddf7fe95Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
192ddf7fe95Scasper {
193134a1f4eSCasper H.S. Dik char *tmp;
194ddf7fe95Scasper uint_t type;
195ddf7fe95Scasper vnode_t *vp;
196ddf7fe95Scasper size_t len = sizeof (priv_set_t) + sizeof (klpd_head_t);
197ddf7fe95Scasper size_t plen, clen;
198ddf7fe95Scasper int proto;
199ddf7fe95Scasper
200ddf7fe95Scasper klpd_arg_t *kap = NULL;
201ddf7fe95Scasper klpd_head_t *khp;
202ddf7fe95Scasper
203ddf7fe95Scasper type = va_arg(ap, uint_t);
204ddf7fe95Scasper switch (type) {
205ddf7fe95Scasper case KLPDARG_NOMORE:
206ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP);
207ddf7fe95Scasper khp->klh_argoff = 0;
208ddf7fe95Scasper break;
209ddf7fe95Scasper case KLPDARG_VNODE:
210ddf7fe95Scasper len += offsetof(klpd_arg_t, kla_str);
211ddf7fe95Scasper vp = va_arg(ap, vnode_t *);
212ddf7fe95Scasper if (vp == NULL)
213ddf7fe95Scasper return (NULL);
214ddf7fe95Scasper
215134a1f4eSCasper H.S. Dik tmp = va_arg(ap, char *);
216ddf7fe95Scasper
217134a1f4eSCasper H.S. Dik if (tmp != NULL && *tmp != '\0')
218134a1f4eSCasper H.S. Dik clen = strlen(tmp) + 1;
219ddf7fe95Scasper else
220ddf7fe95Scasper clen = 0;
221ddf7fe95Scasper
222ddf7fe95Scasper len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
223ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP);
224ddf7fe95Scasper
225ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
226ddf7fe95Scasper kap = KLH_ARG(khp);
227ddf7fe95Scasper
228ddf7fe95Scasper if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
229ddf7fe95Scasper vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
230ddf7fe95Scasper kmem_free(khp, len);
231ddf7fe95Scasper return (NULL);
232ddf7fe95Scasper }
233ddf7fe95Scasper if (clen != 0) {
234ddf7fe95Scasper plen = strlen(kap->kla_str);
235ddf7fe95Scasper if (plen + clen + 1 >= MAXPATHLEN) {
236ddf7fe95Scasper kmem_free(khp, len);
237ddf7fe95Scasper return (NULL);
238ddf7fe95Scasper }
239ddf7fe95Scasper /* Don't make root into a double "/" */
240ddf7fe95Scasper if (plen <= 2)
241ddf7fe95Scasper plen = 0;
242ddf7fe95Scasper kap->kla_str[plen] = '/';
243134a1f4eSCasper H.S. Dik bcopy(tmp, &kap->kla_str[plen + 1], clen);
244ddf7fe95Scasper }
245ddf7fe95Scasper break;
246ddf7fe95Scasper case KLPDARG_PORT:
247ddf7fe95Scasper proto = va_arg(ap, int);
248ddf7fe95Scasper switch (proto) {
249ddf7fe95Scasper case IPPROTO_TCP: type = KLPDARG_TCPPORT;
250ddf7fe95Scasper break;
251ddf7fe95Scasper case IPPROTO_UDP: type = KLPDARG_UDPPORT;
252ddf7fe95Scasper break;
253ddf7fe95Scasper case IPPROTO_SCTP: type = KLPDARG_SCTPPORT;
254ddf7fe95Scasper break;
255ddf7fe95Scasper case PROTO_SDP: type = KLPDARG_SDPPORT;
256ddf7fe95Scasper break;
257ddf7fe95Scasper }
258ddf7fe95Scasper /* FALLTHROUGH */
259ddf7fe95Scasper case KLPDARG_INT:
260ddf7fe95Scasper case KLPDARG_TCPPORT:
261ddf7fe95Scasper case KLPDARG_UDPPORT:
262ddf7fe95Scasper case KLPDARG_SCTPPORT:
263ddf7fe95Scasper case KLPDARG_SDPPORT:
264ddf7fe95Scasper len += sizeof (*kap);
265ddf7fe95Scasper khp = kmem_zalloc(len, KM_SLEEP);
266ddf7fe95Scasper khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
267ddf7fe95Scasper kap = KLH_ARG(khp);
268ddf7fe95Scasper kap->kla_int = va_arg(ap, int);
269ddf7fe95Scasper break;
270ddf7fe95Scasper default:
271ddf7fe95Scasper return (NULL);
272ddf7fe95Scasper }
273ddf7fe95Scasper khp->klh_vers = KLPDCALL_VERS;
274ddf7fe95Scasper khp->klh_len = len;
275ddf7fe95Scasper khp->klh_privoff = sizeof (*khp);
276ddf7fe95Scasper *KLH_PRIVSET(khp) = *rq;
277ddf7fe95Scasper if (kap != NULL) {
278ddf7fe95Scasper kap->kla_type = type;
279ddf7fe95Scasper kap->kla_dlen = len - khp->klh_argoff;
280ddf7fe95Scasper }
281ddf7fe95Scasper return (khp);
282ddf7fe95Scasper }
283ddf7fe95Scasper
284ddf7fe95Scasper static int
klpd_do_call(klpd_reg_t * p,const priv_set_t * req,va_list ap)285ddf7fe95Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
286ddf7fe95Scasper {
287ddf7fe95Scasper door_arg_t da;
288ddf7fe95Scasper int res;
289ddf7fe95Scasper int dres;
290ddf7fe95Scasper klpd_head_t *klh;
291ddf7fe95Scasper
292ddf7fe95Scasper if (p->klpd_door_pid == curproc->p_pid)
293ddf7fe95Scasper return (-1);
294ddf7fe95Scasper
295ddf7fe95Scasper klh = klpd_marshall(p, req, ap);
296ddf7fe95Scasper
297ddf7fe95Scasper if (klh == NULL)
298ddf7fe95Scasper return (-1);
299ddf7fe95Scasper
300ddf7fe95Scasper da.data_ptr = (char *)klh;
301ddf7fe95Scasper da.data_size = klh->klh_len;
302ddf7fe95Scasper da.desc_ptr = NULL;
303ddf7fe95Scasper da.desc_num = 0;
304ddf7fe95Scasper da.rbuf = (char *)&res;
305ddf7fe95Scasper da.rsize = sizeof (res);
306ddf7fe95Scasper
307323a81d9Sjwadams while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
308323a81d9Sjwadams SIZE_MAX, 0)) != 0) {
309ddf7fe95Scasper switch (dres) {
310ddf7fe95Scasper case EAGAIN:
311ddf7fe95Scasper delay(1);
312ddf7fe95Scasper continue;
313ddf7fe95Scasper case EINVAL:
314ddf7fe95Scasper case EBADF:
315ddf7fe95Scasper /* Bad door, don't call it again. */
316ddf7fe95Scasper (void) klpd_unreg_dh(p->klpd_door);
317ddf7fe95Scasper /* FALLTHROUGH */
318ddf7fe95Scasper case EINTR:
319ddf7fe95Scasper /* Pending signal, nothing we can do. */
320ddf7fe95Scasper /* FALLTHROUGH */
321ddf7fe95Scasper default:
322ddf7fe95Scasper kmem_free(klh, klh->klh_len);
323ddf7fe95Scasper return (-1);
324ddf7fe95Scasper }
325ddf7fe95Scasper }
326ddf7fe95Scasper kmem_free(klh, klh->klh_len);
327ddf7fe95Scasper /* Bogus return value, must be a failure */
328ddf7fe95Scasper if (da.rbuf != (char *)&res) {
329ddf7fe95Scasper kmem_free(da.rbuf, da.rsize);
330ddf7fe95Scasper return (-1);
331ddf7fe95Scasper }
332ddf7fe95Scasper return (res);
333ddf7fe95Scasper }
334ddf7fe95Scasper
335ddf7fe95Scasper uint32_t klpd_bad_locks;
336ddf7fe95Scasper
337ddf7fe95Scasper int
klpd_call(const cred_t * cr,const priv_set_t * req,va_list ap)338ddf7fe95Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
339ddf7fe95Scasper {
340ddf7fe95Scasper klpd_reg_t *p;
341ddf7fe95Scasper int rv = -1;
342ddf7fe95Scasper credklpd_t *ckp;
343ddf7fe95Scasper zone_t *ckzone;
344ddf7fe95Scasper
345ddf7fe95Scasper /*
346ddf7fe95Scasper * These locks must not be held when this code is called;
347ddf7fe95Scasper * callbacks to userland with these locks held will result
348ddf7fe95Scasper * in issues. That said, the code at the call sides was
349ddf7fe95Scasper * restructured not to call with any of the locks held and
350ddf7fe95Scasper * no policies operate by default on most processes.
351ddf7fe95Scasper */
352ddf7fe95Scasper if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
353ddf7fe95Scasper mutex_owned(&curproc->p_crlock)) {
3541a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&klpd_bad_locks);
355ddf7fe95Scasper return (-1);
356ddf7fe95Scasper }
357ddf7fe95Scasper
358ddf7fe95Scasper /*
359ddf7fe95Scasper * Enforce the limit set for the call process (still).
360ddf7fe95Scasper */
361ddf7fe95Scasper if (!priv_issubset(req, &CR_LPRIV(cr)))
362ddf7fe95Scasper return (-1);
363ddf7fe95Scasper
364ddf7fe95Scasper /* Try 1: get the credential specific klpd */
365ddf7fe95Scasper if ((ckp = crgetcrklpd(cr)) != NULL) {
366ddf7fe95Scasper mutex_enter(&ckp->crkl_lock);
367ddf7fe95Scasper if ((p = ckp->crkl_reg) != NULL &&
368ddf7fe95Scasper p->klpd_indel == 0 &&
369ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) {
370ddf7fe95Scasper klpd_hold(p);
371ddf7fe95Scasper mutex_exit(&ckp->crkl_lock);
372ddf7fe95Scasper rv = klpd_do_call(p, req, ap);
373ddf7fe95Scasper mutex_enter(&ckp->crkl_lock);
374ddf7fe95Scasper klpd_rele(p);
375ddf7fe95Scasper mutex_exit(&ckp->crkl_lock);
376ddf7fe95Scasper if (rv != -1)
377ddf7fe95Scasper return (rv == 0 ? 0 : -1);
378ddf7fe95Scasper } else {
379ddf7fe95Scasper mutex_exit(&ckp->crkl_lock);
380ddf7fe95Scasper }
381ddf7fe95Scasper }
382ddf7fe95Scasper
383ddf7fe95Scasper /* Try 2: get the project specific klpd */
384ddf7fe95Scasper mutex_enter(&klpd_mutex);
385ddf7fe95Scasper
386ddf7fe95Scasper if ((p = curproj->kpj_klpd) != NULL) {
387ddf7fe95Scasper klpd_hold(p);
388ddf7fe95Scasper mutex_exit(&klpd_mutex);
389ddf7fe95Scasper if (p->klpd_indel == 0 &&
390ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) {
391ddf7fe95Scasper rv = klpd_do_call(p, req, ap);
392ddf7fe95Scasper }
393ddf7fe95Scasper mutex_enter(&klpd_mutex);
394ddf7fe95Scasper klpd_rele(p);
395ddf7fe95Scasper mutex_exit(&klpd_mutex);
396ddf7fe95Scasper
397ddf7fe95Scasper if (rv != -1)
398ddf7fe95Scasper return (rv == 0 ? 0 : -1);
399ddf7fe95Scasper } else {
400ddf7fe95Scasper mutex_exit(&klpd_mutex);
401ddf7fe95Scasper }
402ddf7fe95Scasper
403ddf7fe95Scasper /* Try 3: get the global klpd list */
404ddf7fe95Scasper ckzone = crgetzone(cr);
405ddf7fe95Scasper mutex_enter(&klpd_mutex);
406ddf7fe95Scasper
407ddf7fe95Scasper for (p = klpd_list; p != NULL; ) {
408ddf7fe95Scasper zone_t *kkzone = crgetzone(p->klpd_cred);
409ddf7fe95Scasper if ((kkzone == &zone0 || kkzone == ckzone) &&
410ddf7fe95Scasper p->klpd_indel == 0 &&
411ddf7fe95Scasper priv_issubset(req, &p->klpd_pset)) {
412ddf7fe95Scasper klpd_hold(p);
413ddf7fe95Scasper mutex_exit(&klpd_mutex);
414ddf7fe95Scasper rv = klpd_do_call(p, req, ap);
415ddf7fe95Scasper mutex_enter(&klpd_mutex);
416ddf7fe95Scasper
417ddf7fe95Scasper p = klpd_rele_next(p);
418ddf7fe95Scasper
419ddf7fe95Scasper if (rv != -1)
420ddf7fe95Scasper break;
421ddf7fe95Scasper } else {
422ddf7fe95Scasper p = p->klpd_next;
423ddf7fe95Scasper }
424ddf7fe95Scasper }
425ddf7fe95Scasper mutex_exit(&klpd_mutex);
426ddf7fe95Scasper return (rv == 0 ? 0 : -1);
427ddf7fe95Scasper }
428ddf7fe95Scasper
429ddf7fe95Scasper /*
430ddf7fe95Scasper * Register the klpd.
431ddf7fe95Scasper * If the pid_t passed in is positive, update the registration for
432ddf7fe95Scasper * the specific process; that is only possible if the process already
433ddf7fe95Scasper * has a registration on it. This change of registration will affect
434ddf7fe95Scasper * all processes which share common ancestry.
435ddf7fe95Scasper *
436ddf7fe95Scasper * MY_PID (pid 0) can be used to create or change the context for
437ddf7fe95Scasper * the current process, typically done after fork().
438ddf7fe95Scasper *
439ddf7fe95Scasper * A negative value can be used to register a klpd globally.
440ddf7fe95Scasper *
441ddf7fe95Scasper * The per-credential klpd needs to be cleaned up when entering
442ddf7fe95Scasper * a zone or unsetting the flag.
443ddf7fe95Scasper */
444ddf7fe95Scasper int
klpd_reg(int did,idtype_t type,id_t id,priv_set_t * psetbuf)445ddf7fe95Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
446ddf7fe95Scasper {
447ddf7fe95Scasper cred_t *cr = CRED();
448ddf7fe95Scasper door_handle_t dh;
449ddf7fe95Scasper klpd_reg_t *kpd;
450ddf7fe95Scasper priv_set_t pset;
451ddf7fe95Scasper door_info_t di;
452ddf7fe95Scasper credklpd_t *ckp = NULL;
453ddf7fe95Scasper pid_t pid = -1;
454ddf7fe95Scasper projid_t proj = -1;
455ddf7fe95Scasper kproject_t *kpp = NULL;
456ddf7fe95Scasper
457ddf7fe95Scasper if (CR_FLAGS(cr) & PRIV_XPOLICY)
458ddf7fe95Scasper return (set_errno(EINVAL));
459ddf7fe95Scasper
460ddf7fe95Scasper if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
461ddf7fe95Scasper return (set_errno(EFAULT));
462ddf7fe95Scasper
463ddf7fe95Scasper if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
464ddf7fe95Scasper return (set_errno(EPERM));
465ddf7fe95Scasper
466ddf7fe95Scasper switch (type) {
467ddf7fe95Scasper case P_PID:
468ddf7fe95Scasper pid = (pid_t)id;
469ddf7fe95Scasper if (pid == P_MYPID)
470ddf7fe95Scasper pid = curproc->p_pid;
471ddf7fe95Scasper if (pid == curproc->p_pid)
472ddf7fe95Scasper ckp = crklpd_alloc();
473ddf7fe95Scasper break;
474ddf7fe95Scasper case P_PROJID:
475ddf7fe95Scasper proj = (projid_t)id;
476ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(cr),
477ddf7fe95Scasper PROJECT_HOLD_FIND);
478ddf7fe95Scasper if (kpp == NULL)
479ddf7fe95Scasper return (set_errno(ESRCH));
480ddf7fe95Scasper break;
481ddf7fe95Scasper default:
482ddf7fe95Scasper return (set_errno(ENOTSUP));
483ddf7fe95Scasper }
484ddf7fe95Scasper
485ddf7fe95Scasper
486ddf7fe95Scasper /*
487ddf7fe95Scasper * Verify the door passed in; it must be a door and we won't
488ddf7fe95Scasper * allow processes to be called on their own behalf.
489ddf7fe95Scasper */
490ddf7fe95Scasper dh = door_ki_lookup(did);
491ddf7fe95Scasper if (dh == NULL || door_ki_info(dh, &di) != 0) {
492ddf7fe95Scasper if (ckp != NULL)
493ddf7fe95Scasper crklpd_rele(ckp);
494ddf7fe95Scasper if (kpp != NULL)
495ddf7fe95Scasper project_rele(kpp);
496ddf7fe95Scasper return (set_errno(EBADF));
497ddf7fe95Scasper }
498ddf7fe95Scasper if (type == P_PID && pid == di.di_target) {
499ddf7fe95Scasper if (ckp != NULL)
500ddf7fe95Scasper crklpd_rele(ckp);
501ddf7fe95Scasper ASSERT(kpp == NULL);
502ddf7fe95Scasper return (set_errno(EINVAL));
503ddf7fe95Scasper }
504ddf7fe95Scasper
505ddf7fe95Scasper kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
506ddf7fe95Scasper crhold(kpd->klpd_cred = cr);
507ddf7fe95Scasper kpd->klpd_door = dh;
508ddf7fe95Scasper kpd->klpd_door_pid = di.di_target;
509ddf7fe95Scasper kpd->klpd_ref = 1;
510ddf7fe95Scasper kpd->klpd_pset = pset;
511ddf7fe95Scasper
512ddf7fe95Scasper if (kpp != NULL) {
513ddf7fe95Scasper mutex_enter(&klpd_mutex);
514ddf7fe95Scasper kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
515ddf7fe95Scasper mutex_exit(&klpd_mutex);
516ddf7fe95Scasper if (kpd != NULL)
517ddf7fe95Scasper klpd_rele(kpd);
518ddf7fe95Scasper project_rele(kpp);
519ddf7fe95Scasper } else if ((int)pid < 0) {
520ddf7fe95Scasper /* Global daemon */
521ddf7fe95Scasper mutex_enter(&klpd_mutex);
522ddf7fe95Scasper (void) klpd_link(kpd, &klpd_list, B_FALSE);
523ddf7fe95Scasper mutex_exit(&klpd_mutex);
524ddf7fe95Scasper } else if (pid == curproc->p_pid) {
525ddf7fe95Scasper proc_t *p = curproc;
526ddf7fe95Scasper cred_t *newcr = cralloc();
527ddf7fe95Scasper
528ddf7fe95Scasper /* No need to lock, sole reference to ckp */
529ddf7fe95Scasper kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
530ddf7fe95Scasper
531ddf7fe95Scasper if (kpd != NULL)
532ddf7fe95Scasper klpd_rele(kpd);
533ddf7fe95Scasper
534ddf7fe95Scasper mutex_enter(&p->p_crlock);
535ddf7fe95Scasper cr = p->p_cred;
536ddf7fe95Scasper crdup_to(cr, newcr);
537ddf7fe95Scasper crsetcrklpd(newcr, ckp);
538ddf7fe95Scasper p->p_cred = newcr; /* Already held for p_cred */
539ddf7fe95Scasper
540ddf7fe95Scasper crhold(newcr); /* Hold once for the current thread */
541ddf7fe95Scasper mutex_exit(&p->p_crlock);
542ddf7fe95Scasper crfree(cr); /* One for the p_cred */
543ddf7fe95Scasper crset(p, newcr);
544ddf7fe95Scasper } else {
545ddf7fe95Scasper proc_t *p;
546ddf7fe95Scasper cred_t *pcr;
547ddf7fe95Scasper mutex_enter(&pidlock);
548ddf7fe95Scasper p = prfind(pid);
549ddf7fe95Scasper if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
550ddf7fe95Scasper mutex_exit(&pidlock);
551ddf7fe95Scasper klpd_rele(kpd);
552ddf7fe95Scasper return (set_errno(p == NULL ? ESRCH : EPERM));
553ddf7fe95Scasper }
554ddf7fe95Scasper mutex_enter(&p->p_crlock);
555ddf7fe95Scasper crhold(pcr = p->p_cred);
556ddf7fe95Scasper mutex_exit(&pidlock);
557ddf7fe95Scasper mutex_exit(&p->p_crlock);
558ddf7fe95Scasper /*
559ddf7fe95Scasper * We're going to update the credential's ckp in place;
560ddf7fe95Scasper * this requires that it exists.
561ddf7fe95Scasper */
562ddf7fe95Scasper ckp = crgetcrklpd(pcr);
563ddf7fe95Scasper if (ckp == NULL) {
564ddf7fe95Scasper crfree(pcr);
565ddf7fe95Scasper klpd_rele(kpd);
566ddf7fe95Scasper return (set_errno(EINVAL));
567ddf7fe95Scasper }
568ddf7fe95Scasper crklpd_setreg(ckp, kpd);
569ddf7fe95Scasper crfree(pcr);
570ddf7fe95Scasper }
571ddf7fe95Scasper
572ddf7fe95Scasper return (0);
573ddf7fe95Scasper }
574ddf7fe95Scasper
575ddf7fe95Scasper static int
klpd_unreg_dh(door_handle_t dh)576ddf7fe95Scasper klpd_unreg_dh(door_handle_t dh)
577ddf7fe95Scasper {
578ddf7fe95Scasper klpd_reg_t *p;
579ddf7fe95Scasper
580ddf7fe95Scasper mutex_enter(&klpd_mutex);
581ddf7fe95Scasper for (p = klpd_list; p != NULL; p = p->klpd_next) {
582ddf7fe95Scasper if (p->klpd_door == dh)
583ddf7fe95Scasper break;
584ddf7fe95Scasper }
585ddf7fe95Scasper if (p == NULL) {
586ddf7fe95Scasper mutex_exit(&klpd_mutex);
587ddf7fe95Scasper return (EINVAL);
588ddf7fe95Scasper }
589ddf7fe95Scasper if (p->klpd_indel != 0) {
590ddf7fe95Scasper mutex_exit(&klpd_mutex);
591ddf7fe95Scasper return (EAGAIN);
592ddf7fe95Scasper }
593ddf7fe95Scasper p->klpd_indel = 1;
594ddf7fe95Scasper klpd_rele(p);
595ddf7fe95Scasper mutex_exit(&klpd_mutex);
596ddf7fe95Scasper return (0);
597ddf7fe95Scasper }
598ddf7fe95Scasper
599ddf7fe95Scasper int
klpd_unreg(int did,idtype_t type,id_t id)600ddf7fe95Scasper klpd_unreg(int did, idtype_t type, id_t id)
601ddf7fe95Scasper {
602ddf7fe95Scasper door_handle_t dh;
603ddf7fe95Scasper int res = 0;
604ddf7fe95Scasper proc_t *p;
605ddf7fe95Scasper pid_t pid;
606ddf7fe95Scasper projid_t proj;
607ddf7fe95Scasper kproject_t *kpp = NULL;
608ddf7fe95Scasper credklpd_t *ckp;
609ddf7fe95Scasper
610ddf7fe95Scasper switch (type) {
611ddf7fe95Scasper case P_PID:
612ddf7fe95Scasper pid = (pid_t)id;
613ddf7fe95Scasper break;
614ddf7fe95Scasper case P_PROJID:
615ddf7fe95Scasper proj = (projid_t)id;
616ddf7fe95Scasper kpp = project_hold_by_id(proj, crgetzone(CRED()),
617ddf7fe95Scasper PROJECT_HOLD_FIND);
618ddf7fe95Scasper if (kpp == NULL)
619ddf7fe95Scasper return (set_errno(ESRCH));
620ddf7fe95Scasper break;
621ddf7fe95Scasper default:
622ddf7fe95Scasper return (set_errno(ENOTSUP));
623ddf7fe95Scasper }
624ddf7fe95Scasper
625ddf7fe95Scasper dh = door_ki_lookup(did);
626ddf7fe95Scasper if (dh == NULL) {
627ddf7fe95Scasper if (kpp != NULL)
628ddf7fe95Scasper project_rele(kpp);
629ddf7fe95Scasper return (set_errno(EINVAL));
630ddf7fe95Scasper }
631ddf7fe95Scasper
632ddf7fe95Scasper if (kpp != NULL) {
633ddf7fe95Scasper mutex_enter(&klpd_mutex);
634ddf7fe95Scasper if (kpp->kpj_klpd == NULL)
635ddf7fe95Scasper res = ESRCH;
636ddf7fe95Scasper else
637134a1f4eSCasper H.S. Dik klpd_freelist(&kpp->kpj_klpd);
638ddf7fe95Scasper mutex_exit(&klpd_mutex);
639ddf7fe95Scasper project_rele(kpp);
640ddf7fe95Scasper goto out;
641ddf7fe95Scasper } else if ((int)pid > 0) {
642ddf7fe95Scasper mutex_enter(&pidlock);
643ddf7fe95Scasper p = prfind(pid);
644ddf7fe95Scasper if (p == NULL) {
645ddf7fe95Scasper mutex_exit(&pidlock);
646ddf7fe95Scasper door_ki_rele(dh);
647ddf7fe95Scasper return (set_errno(ESRCH));
648ddf7fe95Scasper }
649ddf7fe95Scasper mutex_enter(&p->p_crlock);
650ddf7fe95Scasper mutex_exit(&pidlock);
651ddf7fe95Scasper } else if (pid == 0) {
652ddf7fe95Scasper p = curproc;
653ddf7fe95Scasper mutex_enter(&p->p_crlock);
654ddf7fe95Scasper } else {
655ddf7fe95Scasper res = klpd_unreg_dh(dh);
656ddf7fe95Scasper goto out;
657ddf7fe95Scasper }
658ddf7fe95Scasper
659ddf7fe95Scasper ckp = crgetcrklpd(p->p_cred);
660ddf7fe95Scasper if (ckp != NULL) {
661ddf7fe95Scasper crklpd_setreg(ckp, NULL);
662ddf7fe95Scasper } else {
663ddf7fe95Scasper res = ESRCH;
664ddf7fe95Scasper }
665ddf7fe95Scasper mutex_exit(&p->p_crlock);
666ddf7fe95Scasper
667ddf7fe95Scasper out:
668ddf7fe95Scasper door_ki_rele(dh);
669ddf7fe95Scasper
670ddf7fe95Scasper if (res != 0)
671ddf7fe95Scasper return (set_errno(res));
672ddf7fe95Scasper return (0);
673ddf7fe95Scasper }
674ddf7fe95Scasper
675ddf7fe95Scasper void
crklpd_hold(credklpd_t * crkpd)676ddf7fe95Scasper crklpd_hold(credklpd_t *crkpd)
677ddf7fe95Scasper {
6781a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&crkpd->crkl_ref);
679ddf7fe95Scasper }
680ddf7fe95Scasper
681ddf7fe95Scasper void
crklpd_rele(credklpd_t * crkpd)682ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd)
683ddf7fe95Scasper {
6841a5e258fSJosef 'Jeff' Sipek if (atomic_dec_32_nv(&crkpd->crkl_ref) == 0) {
685ddf7fe95Scasper if (crkpd->crkl_reg != NULL)
686ddf7fe95Scasper klpd_rele(crkpd->crkl_reg);
687ddf7fe95Scasper mutex_destroy(&crkpd->crkl_lock);
688ddf7fe95Scasper kmem_free(crkpd, sizeof (*crkpd));
689ddf7fe95Scasper }
690ddf7fe95Scasper }
691ddf7fe95Scasper
692ddf7fe95Scasper static credklpd_t *
crklpd_alloc(void)693ddf7fe95Scasper crklpd_alloc(void)
694ddf7fe95Scasper {
695ddf7fe95Scasper credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
696ddf7fe95Scasper
697ddf7fe95Scasper mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
698ddf7fe95Scasper res->crkl_ref = 1;
699ddf7fe95Scasper res->crkl_reg = NULL;
700ddf7fe95Scasper
701ddf7fe95Scasper return (res);
702ddf7fe95Scasper }
703ddf7fe95Scasper
704ddf7fe95Scasper void
crklpd_setreg(credklpd_t * crk,klpd_reg_t * new)705ddf7fe95Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
706ddf7fe95Scasper {
707ddf7fe95Scasper klpd_reg_t *old;
708ddf7fe95Scasper
709ddf7fe95Scasper mutex_enter(&crk->crkl_lock);
710ddf7fe95Scasper if (new == NULL) {
711ddf7fe95Scasper old = crk->crkl_reg;
712ddf7fe95Scasper if (old != NULL)
713ddf7fe95Scasper klpd_unlink(old);
714ddf7fe95Scasper } else {
715ddf7fe95Scasper old = klpd_link(new, &crk->crkl_reg, B_TRUE);
716ddf7fe95Scasper }
717ddf7fe95Scasper mutex_exit(&crk->crkl_lock);
718ddf7fe95Scasper
719ddf7fe95Scasper if (old != NULL)
720ddf7fe95Scasper klpd_rele(old);
721ddf7fe95Scasper }
722134a1f4eSCasper H.S. Dik
723134a1f4eSCasper H.S. Dik /* Allocate and register the pfexec specific callback */
724134a1f4eSCasper H.S. Dik int
pfexec_reg(int did)725134a1f4eSCasper H.S. Dik pfexec_reg(int did)
726134a1f4eSCasper H.S. Dik {
727134a1f4eSCasper H.S. Dik door_handle_t dh;
728134a1f4eSCasper H.S. Dik int err = secpolicy_pfexec_register(CRED());
729134a1f4eSCasper H.S. Dik klpd_reg_t *pfx;
730134a1f4eSCasper H.S. Dik door_info_t di;
731134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED());
732134a1f4eSCasper H.S. Dik
733134a1f4eSCasper H.S. Dik if (err != 0)
734134a1f4eSCasper H.S. Dik return (set_errno(err));
735134a1f4eSCasper H.S. Dik
736134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did);
737134a1f4eSCasper H.S. Dik if (dh == NULL || door_ki_info(dh, &di) != 0)
738134a1f4eSCasper H.S. Dik return (set_errno(EBADF));
739134a1f4eSCasper H.S. Dik
740134a1f4eSCasper H.S. Dik pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
741134a1f4eSCasper H.S. Dik
742134a1f4eSCasper H.S. Dik pfx->klpd_door = dh;
743134a1f4eSCasper H.S. Dik pfx->klpd_door_pid = di.di_target;
744134a1f4eSCasper H.S. Dik pfx->klpd_ref = 1;
745134a1f4eSCasper H.S. Dik pfx->klpd_cred = NULL;
746134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock);
747134a1f4eSCasper H.S. Dik pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
748134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock);
749134a1f4eSCasper H.S. Dik if (pfx != NULL)
750134a1f4eSCasper H.S. Dik klpd_rele(pfx);
751134a1f4eSCasper H.S. Dik
752134a1f4eSCasper H.S. Dik return (0);
753134a1f4eSCasper H.S. Dik }
754134a1f4eSCasper H.S. Dik
755134a1f4eSCasper H.S. Dik int
pfexec_unreg(int did)756134a1f4eSCasper H.S. Dik pfexec_unreg(int did)
757134a1f4eSCasper H.S. Dik {
758134a1f4eSCasper H.S. Dik door_handle_t dh;
759134a1f4eSCasper H.S. Dik int err = 0;
760134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED());
761134a1f4eSCasper H.S. Dik klpd_reg_t *pfd;
762134a1f4eSCasper H.S. Dik
763134a1f4eSCasper H.S. Dik dh = door_ki_lookup(did);
764134a1f4eSCasper H.S. Dik if (dh == NULL)
765134a1f4eSCasper H.S. Dik return (set_errno(EBADF));
766134a1f4eSCasper H.S. Dik
767134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock);
768134a1f4eSCasper H.S. Dik pfd = myzone->zone_pfexecd;
769134a1f4eSCasper H.S. Dik if (pfd != NULL && pfd->klpd_door == dh) {
770134a1f4eSCasper H.S. Dik klpd_unlink(pfd);
771134a1f4eSCasper H.S. Dik } else {
772134a1f4eSCasper H.S. Dik pfd = NULL;
773134a1f4eSCasper H.S. Dik err = EINVAL;
774134a1f4eSCasper H.S. Dik }
775134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock);
776134a1f4eSCasper H.S. Dik door_ki_rele(dh);
777134a1f4eSCasper H.S. Dik /*
778134a1f4eSCasper H.S. Dik * crfree() cannot be called with zone_lock held; it is called
779134a1f4eSCasper H.S. Dik * indirectly through closing the door handle
780134a1f4eSCasper H.S. Dik */
781134a1f4eSCasper H.S. Dik if (pfd != NULL)
782134a1f4eSCasper H.S. Dik klpd_rele(pfd);
783134a1f4eSCasper H.S. Dik if (err != 0)
784134a1f4eSCasper H.S. Dik return (set_errno(err));
785134a1f4eSCasper H.S. Dik return (0);
786134a1f4eSCasper H.S. Dik }
787134a1f4eSCasper H.S. Dik
788134a1f4eSCasper H.S. Dik static int
get_path(char * buf,const char * path,int len)789134a1f4eSCasper H.S. Dik get_path(char *buf, const char *path, int len)
790134a1f4eSCasper H.S. Dik {
791134a1f4eSCasper H.S. Dik size_t lc;
792134a1f4eSCasper H.S. Dik char *s;
793134a1f4eSCasper H.S. Dik
794134a1f4eSCasper H.S. Dik if (len < 0)
795134a1f4eSCasper H.S. Dik len = strlen(path);
796134a1f4eSCasper H.S. Dik
797134a1f4eSCasper H.S. Dik if (*path == '/' && len < MAXPATHLEN) {
798134a1f4eSCasper H.S. Dik (void) strcpy(buf, path);
799134a1f4eSCasper H.S. Dik return (0);
800134a1f4eSCasper H.S. Dik }
801134a1f4eSCasper H.S. Dik /*
802134a1f4eSCasper H.S. Dik * Build the pathname using the current directory + resolve pathname.
803134a1f4eSCasper H.S. Dik * The resolve pathname either starts with a normal component and
804134a1f4eSCasper H.S. Dik * we can just concatenate them or it starts with one
805134a1f4eSCasper H.S. Dik * or more ".." component and we can remove those; the
806134a1f4eSCasper H.S. Dik * last one cannot be a ".." and the current directory has
807134a1f4eSCasper H.S. Dik * more components than the number of ".." in the resolved pathname.
808134a1f4eSCasper H.S. Dik */
809134a1f4eSCasper H.S. Dik if (dogetcwd(buf, MAXPATHLEN) != 0)
810134a1f4eSCasper H.S. Dik return (-1);
811134a1f4eSCasper H.S. Dik
812134a1f4eSCasper H.S. Dik lc = strlen(buf);
813134a1f4eSCasper H.S. Dik
814134a1f4eSCasper H.S. Dik while (len > 3 && strncmp("../", path, 3) == 0) {
815134a1f4eSCasper H.S. Dik len -= 3;
816134a1f4eSCasper H.S. Dik path += 3;
817134a1f4eSCasper H.S. Dik
818134a1f4eSCasper H.S. Dik s = strrchr(buf, '/');
819134a1f4eSCasper H.S. Dik if (s == NULL || s == buf)
820134a1f4eSCasper H.S. Dik return (-1);
821134a1f4eSCasper H.S. Dik
822134a1f4eSCasper H.S. Dik *s = '\0';
823134a1f4eSCasper H.S. Dik lc = s - buf;
824134a1f4eSCasper H.S. Dik }
825134a1f4eSCasper H.S. Dik /* Add a "/" and a NUL */
826134a1f4eSCasper H.S. Dik if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
827134a1f4eSCasper H.S. Dik return (-1);
828134a1f4eSCasper H.S. Dik
829134a1f4eSCasper H.S. Dik buf[lc] = '/';
830134a1f4eSCasper H.S. Dik (void) strcpy(buf + lc + 1, path);
831134a1f4eSCasper H.S. Dik
832134a1f4eSCasper H.S. Dik return (0);
833134a1f4eSCasper H.S. Dik }
834134a1f4eSCasper H.S. Dik
835134a1f4eSCasper H.S. Dik /*
836134a1f4eSCasper H.S. Dik * Perform the pfexec upcall.
837134a1f4eSCasper H.S. Dik *
838134a1f4eSCasper H.S. Dik * The pfexec upcall is different from the klpd_upcall in that a failure
839134a1f4eSCasper H.S. Dik * will lead to a denial of execution.
840134a1f4eSCasper H.S. Dik */
841134a1f4eSCasper H.S. Dik int
pfexec_call(const cred_t * cr,struct pathname * rpnp,cred_t ** pfcr,boolean_t * scrub)842134a1f4eSCasper H.S. Dik pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
843134a1f4eSCasper H.S. Dik boolean_t *scrub)
844134a1f4eSCasper H.S. Dik {
845134a1f4eSCasper H.S. Dik klpd_reg_t *pfd;
846134a1f4eSCasper H.S. Dik pfexec_arg_t *pap;
847134a1f4eSCasper H.S. Dik pfexec_reply_t pr, *prp;
848134a1f4eSCasper H.S. Dik door_arg_t da;
849134a1f4eSCasper H.S. Dik int dres;
850134a1f4eSCasper H.S. Dik cred_t *ncr = NULL;
851*b01b59e3SRobert Mustacchi int err = EACCES;
852134a1f4eSCasper H.S. Dik priv_set_t *iset;
853134a1f4eSCasper H.S. Dik priv_set_t *lset;
854134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(CRED());
855134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
856134a1f4eSCasper H.S. Dik
857134a1f4eSCasper H.S. Dik /* Find registration */
858134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock);
859134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL)
860134a1f4eSCasper H.S. Dik klpd_hold(pfd);
861134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock);
862134a1f4eSCasper H.S. Dik
863134a1f4eSCasper H.S. Dik if (pfd == NULL)
864134a1f4eSCasper H.S. Dik return (0);
865134a1f4eSCasper H.S. Dik
866134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) {
867134a1f4eSCasper H.S. Dik klpd_rele(pfd);
868134a1f4eSCasper H.S. Dik return (0);
869134a1f4eSCasper H.S. Dik }
870134a1f4eSCasper H.S. Dik
871134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP);
872134a1f4eSCasper H.S. Dik
873134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
874134a1f4eSCasper H.S. Dik goto out1;
875134a1f4eSCasper H.S. Dik
876134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS;
877134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_EXEC_ATTRS;
878134a1f4eSCasper H.S. Dik pap->pfa_len = pasize;
879134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr);
880134a1f4eSCasper H.S. Dik
881134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap;
882134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len;
883134a1f4eSCasper H.S. Dik da.desc_ptr = NULL;
884134a1f4eSCasper H.S. Dik da.desc_num = 0;
885134a1f4eSCasper H.S. Dik da.rbuf = (char *)≺
886134a1f4eSCasper H.S. Dik da.rsize = sizeof (pr);
887134a1f4eSCasper H.S. Dik
888134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
889134a1f4eSCasper H.S. Dik switch (dres) {
890134a1f4eSCasper H.S. Dik case EAGAIN:
891134a1f4eSCasper H.S. Dik delay(1);
892134a1f4eSCasper H.S. Dik continue;
893134a1f4eSCasper H.S. Dik case EINVAL:
894134a1f4eSCasper H.S. Dik case EBADF:
895134a1f4eSCasper H.S. Dik /* FALLTHROUGH */
896134a1f4eSCasper H.S. Dik case EINTR:
897134a1f4eSCasper H.S. Dik /* FALLTHROUGH */
898134a1f4eSCasper H.S. Dik default:
899134a1f4eSCasper H.S. Dik goto out;
900134a1f4eSCasper H.S. Dik }
901134a1f4eSCasper H.S. Dik }
902134a1f4eSCasper H.S. Dik
903134a1f4eSCasper H.S. Dik prp = (pfexec_reply_t *)da.rbuf;
904134a1f4eSCasper H.S. Dik /*
905134a1f4eSCasper H.S. Dik * Check the size of the result and the alignment of the
906134a1f4eSCasper H.S. Dik * privilege sets.
907134a1f4eSCasper H.S. Dik */
908134a1f4eSCasper H.S. Dik if (da.rsize < sizeof (pr) ||
909134a1f4eSCasper H.S. Dik prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
910134a1f4eSCasper H.S. Dik prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
911134a1f4eSCasper H.S. Dik (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
912*b01b59e3SRobert Mustacchi (prp->pfr_ioff & (sizeof (priv_chunk_t) - 1)) != 0)
913134a1f4eSCasper H.S. Dik goto out;
914134a1f4eSCasper H.S. Dik
915134a1f4eSCasper H.S. Dik /*
916134a1f4eSCasper H.S. Dik * Get results:
917134a1f4eSCasper H.S. Dik * allow/allow with additional credentials/disallow[*]
918134a1f4eSCasper H.S. Dik *
919134a1f4eSCasper H.S. Dik * euid, uid, egid, gid, privs, and limitprivs
920134a1f4eSCasper H.S. Dik * We now have somewhat more flexibility we could even set E and P
921134a1f4eSCasper H.S. Dik * judiciously but that would break some currently valid assumptions
922134a1f4eSCasper H.S. Dik * [*] Disallow is not readily supported by always including
923134a1f4eSCasper H.S. Dik * the Basic Solaris User profile in all user's profiles.
924134a1f4eSCasper H.S. Dik */
925134a1f4eSCasper H.S. Dik
926134a1f4eSCasper H.S. Dik if (!prp->pfr_allowed) {
927134a1f4eSCasper H.S. Dik err = EACCES;
928134a1f4eSCasper H.S. Dik goto out;
929134a1f4eSCasper H.S. Dik }
930134a1f4eSCasper H.S. Dik if (!prp->pfr_setcred) {
931134a1f4eSCasper H.S. Dik err = 0;
932134a1f4eSCasper H.S. Dik goto out;
933134a1f4eSCasper H.S. Dik }
934134a1f4eSCasper H.S. Dik ncr = crdup((cred_t *)cr);
935134a1f4eSCasper H.S. Dik
936134a1f4eSCasper H.S. Dik /*
937134a1f4eSCasper H.S. Dik * Generate the new credential set scrubenv if ruid != euid (or set)
938134a1f4eSCasper H.S. Dik * the "I'm set-uid flag" but that is not inherited so scrubbing
939134a1f4eSCasper H.S. Dik * the environment is a requirement.
940134a1f4eSCasper H.S. Dik */
941134a1f4eSCasper H.S. Dik /* Set uids or gids, note that -1 will do the right thing */
942134a1f4eSCasper H.S. Dik if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
943134a1f4eSCasper H.S. Dik goto out;
944134a1f4eSCasper H.S. Dik if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
945134a1f4eSCasper H.S. Dik goto out;
946134a1f4eSCasper H.S. Dik
947134a1f4eSCasper H.S. Dik *scrub = prp->pfr_scrubenv;
948134a1f4eSCasper H.S. Dik
949134a1f4eSCasper H.S. Dik if (prp->pfr_clearflag)
950134a1f4eSCasper H.S. Dik CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
951134a1f4eSCasper H.S. Dik
952134a1f4eSCasper H.S. Dik /* We cannot exceed our Limit set, no matter what */
953134a1f4eSCasper H.S. Dik iset = PFEXEC_REPLY_IPRIV(prp);
954134a1f4eSCasper H.S. Dik
955134a1f4eSCasper H.S. Dik if (iset != NULL) {
956134a1f4eSCasper H.S. Dik if (!priv_issubset(iset, &CR_LPRIV(ncr)))
957134a1f4eSCasper H.S. Dik goto out;
958134a1f4eSCasper H.S. Dik priv_union(iset, &CR_IPRIV(ncr));
959134a1f4eSCasper H.S. Dik }
960134a1f4eSCasper H.S. Dik
961134a1f4eSCasper H.S. Dik /* Nor can we increate our Limit set itself */
962134a1f4eSCasper H.S. Dik lset = PFEXEC_REPLY_LPRIV(prp);
963134a1f4eSCasper H.S. Dik
964134a1f4eSCasper H.S. Dik if (lset != NULL) {
965134a1f4eSCasper H.S. Dik if (!priv_issubset(lset, &CR_LPRIV(ncr)))
966134a1f4eSCasper H.S. Dik goto out;
967134a1f4eSCasper H.S. Dik CR_LPRIV(ncr) = *lset;
968134a1f4eSCasper H.S. Dik }
969134a1f4eSCasper H.S. Dik
970134a1f4eSCasper H.S. Dik /* Exec will do the standard set operations */
971134a1f4eSCasper H.S. Dik
972134a1f4eSCasper H.S. Dik err = 0;
973134a1f4eSCasper H.S. Dik out:
974134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pr)
975134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize);
976134a1f4eSCasper H.S. Dik out1:
977134a1f4eSCasper H.S. Dik kmem_free(pap, pasize);
978134a1f4eSCasper H.S. Dik klpd_rele(pfd);
979134a1f4eSCasper H.S. Dik if (ncr != NULL) {
980134a1f4eSCasper H.S. Dik if (err == 0)
981134a1f4eSCasper H.S. Dik *pfcr = ncr;
982134a1f4eSCasper H.S. Dik else
983134a1f4eSCasper H.S. Dik crfree(ncr);
984134a1f4eSCasper H.S. Dik }
985134a1f4eSCasper H.S. Dik return (err);
986134a1f4eSCasper H.S. Dik }
987134a1f4eSCasper H.S. Dik
988134a1f4eSCasper H.S. Dik int
get_forced_privs(const cred_t * cr,const char * respn,priv_set_t * set)989134a1f4eSCasper H.S. Dik get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
990134a1f4eSCasper H.S. Dik {
991134a1f4eSCasper H.S. Dik klpd_reg_t *pfd;
992134a1f4eSCasper H.S. Dik pfexec_arg_t *pap;
993134a1f4eSCasper H.S. Dik door_arg_t da;
994134a1f4eSCasper H.S. Dik int dres;
995134a1f4eSCasper H.S. Dik int err = -1;
996134a1f4eSCasper H.S. Dik priv_set_t *fset, pmem;
997134a1f4eSCasper H.S. Dik cred_t *zkcr;
998134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr);
999134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
1000134a1f4eSCasper H.S. Dik
1001134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock);
1002134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL)
1003134a1f4eSCasper H.S. Dik klpd_hold(pfd);
1004134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock);
1005134a1f4eSCasper H.S. Dik
1006134a1f4eSCasper H.S. Dik if (pfd == NULL)
1007134a1f4eSCasper H.S. Dik return (-1);
1008134a1f4eSCasper H.S. Dik
1009134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) {
1010134a1f4eSCasper H.S. Dik klpd_rele(pfd);
1011134a1f4eSCasper H.S. Dik return (0);
1012134a1f4eSCasper H.S. Dik }
1013134a1f4eSCasper H.S. Dik
1014134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP);
1015134a1f4eSCasper H.S. Dik
1016134a1f4eSCasper H.S. Dik if (get_path(pap->pfa_path, respn, -1) == -1)
1017134a1f4eSCasper H.S. Dik goto out1;
1018134a1f4eSCasper H.S. Dik
1019134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS;
1020134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_FORCED_PRIVS;
1021134a1f4eSCasper H.S. Dik pap->pfa_len = pasize;
1022134a1f4eSCasper H.S. Dik pap->pfa_uid = (uid_t)-1; /* Not relevant */
1023134a1f4eSCasper H.S. Dik
1024134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap;
1025134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len;
1026134a1f4eSCasper H.S. Dik da.desc_ptr = NULL;
1027134a1f4eSCasper H.S. Dik da.desc_num = 0;
1028134a1f4eSCasper H.S. Dik da.rbuf = (char *)&pmem;
1029134a1f4eSCasper H.S. Dik da.rsize = sizeof (pmem);
1030134a1f4eSCasper H.S. Dik
1031134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1032134a1f4eSCasper H.S. Dik switch (dres) {
1033134a1f4eSCasper H.S. Dik case EAGAIN:
1034134a1f4eSCasper H.S. Dik delay(1);
1035134a1f4eSCasper H.S. Dik continue;
1036134a1f4eSCasper H.S. Dik case EINVAL:
1037134a1f4eSCasper H.S. Dik case EBADF:
1038134a1f4eSCasper H.S. Dik case EINTR:
1039134a1f4eSCasper H.S. Dik default:
1040134a1f4eSCasper H.S. Dik goto out;
1041134a1f4eSCasper H.S. Dik }
1042134a1f4eSCasper H.S. Dik }
1043134a1f4eSCasper H.S. Dik
1044134a1f4eSCasper H.S. Dik /*
1045134a1f4eSCasper H.S. Dik * Check the size of the result, it's a privilege set.
1046134a1f4eSCasper H.S. Dik */
1047134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (priv_set_t))
1048134a1f4eSCasper H.S. Dik goto out;
1049134a1f4eSCasper H.S. Dik
1050134a1f4eSCasper H.S. Dik fset = (priv_set_t *)da.rbuf;
1051134a1f4eSCasper H.S. Dik
1052134a1f4eSCasper H.S. Dik /*
1053134a1f4eSCasper H.S. Dik * We restrict the forced privileges with whatever is available in
1054134a1f4eSCasper H.S. Dik * the current zone.
1055134a1f4eSCasper H.S. Dik */
1056134a1f4eSCasper H.S. Dik zkcr = zone_kcred();
1057134a1f4eSCasper H.S. Dik priv_intersect(&CR_LPRIV(zkcr), fset);
1058134a1f4eSCasper H.S. Dik
1059134a1f4eSCasper H.S. Dik /*
1060134a1f4eSCasper H.S. Dik * But we fail if the forced privileges are not found in the current
1061134a1f4eSCasper H.S. Dik * Limit set.
1062134a1f4eSCasper H.S. Dik */
1063134a1f4eSCasper H.S. Dik if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1064134a1f4eSCasper H.S. Dik err = EACCES;
1065134a1f4eSCasper H.S. Dik } else if (!priv_isemptyset(fset)) {
1066134a1f4eSCasper H.S. Dik err = 0;
1067134a1f4eSCasper H.S. Dik *set = *fset;
1068134a1f4eSCasper H.S. Dik }
1069134a1f4eSCasper H.S. Dik out:
1070134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&pmem)
1071134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize);
1072134a1f4eSCasper H.S. Dik out1:
1073134a1f4eSCasper H.S. Dik kmem_free(pap, pasize);
1074134a1f4eSCasper H.S. Dik klpd_rele(pfd);
1075134a1f4eSCasper H.S. Dik return (err);
1076134a1f4eSCasper H.S. Dik }
1077134a1f4eSCasper H.S. Dik
1078134a1f4eSCasper H.S. Dik int
check_user_privs(const cred_t * cr,const priv_set_t * set)1079134a1f4eSCasper H.S. Dik check_user_privs(const cred_t *cr, const priv_set_t *set)
1080134a1f4eSCasper H.S. Dik {
1081134a1f4eSCasper H.S. Dik klpd_reg_t *pfd;
1082134a1f4eSCasper H.S. Dik pfexec_arg_t *pap;
1083134a1f4eSCasper H.S. Dik door_arg_t da;
1084134a1f4eSCasper H.S. Dik int dres;
1085134a1f4eSCasper H.S. Dik int err = -1;
1086134a1f4eSCasper H.S. Dik zone_t *myzone = crgetzone(cr);
1087134a1f4eSCasper H.S. Dik size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1088134a1f4eSCasper H.S. Dik uint32_t res;
1089134a1f4eSCasper H.S. Dik
1090134a1f4eSCasper H.S. Dik mutex_enter(&myzone->zone_lock);
1091134a1f4eSCasper H.S. Dik if ((pfd = myzone->zone_pfexecd) != NULL)
1092134a1f4eSCasper H.S. Dik klpd_hold(pfd);
1093134a1f4eSCasper H.S. Dik mutex_exit(&myzone->zone_lock);
1094134a1f4eSCasper H.S. Dik
1095134a1f4eSCasper H.S. Dik if (pfd == NULL)
1096134a1f4eSCasper H.S. Dik return (-1);
1097134a1f4eSCasper H.S. Dik
1098134a1f4eSCasper H.S. Dik if (pfd->klpd_door_pid == curproc->p_pid) {
1099134a1f4eSCasper H.S. Dik klpd_rele(pfd);
1100134a1f4eSCasper H.S. Dik return (0);
1101134a1f4eSCasper H.S. Dik }
1102134a1f4eSCasper H.S. Dik
1103134a1f4eSCasper H.S. Dik pap = kmem_zalloc(pasize, KM_SLEEP);
1104134a1f4eSCasper H.S. Dik
1105134a1f4eSCasper H.S. Dik *(priv_set_t *)&pap->pfa_buf = *set;
1106134a1f4eSCasper H.S. Dik
1107134a1f4eSCasper H.S. Dik pap->pfa_vers = PFEXEC_ARG_VERS;
1108134a1f4eSCasper H.S. Dik pap->pfa_call = PFEXEC_USER_PRIVS;
1109134a1f4eSCasper H.S. Dik pap->pfa_len = pasize;
1110134a1f4eSCasper H.S. Dik pap->pfa_uid = crgetruid(cr);
1111134a1f4eSCasper H.S. Dik
1112134a1f4eSCasper H.S. Dik da.data_ptr = (char *)pap;
1113134a1f4eSCasper H.S. Dik da.data_size = pap->pfa_len;
1114134a1f4eSCasper H.S. Dik da.desc_ptr = NULL;
1115134a1f4eSCasper H.S. Dik da.desc_num = 0;
1116134a1f4eSCasper H.S. Dik da.rbuf = (char *)&res;
1117134a1f4eSCasper H.S. Dik da.rsize = sizeof (res);
1118134a1f4eSCasper H.S. Dik
1119134a1f4eSCasper H.S. Dik while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1120134a1f4eSCasper H.S. Dik switch (dres) {
1121134a1f4eSCasper H.S. Dik case EAGAIN:
1122134a1f4eSCasper H.S. Dik delay(1);
1123134a1f4eSCasper H.S. Dik continue;
1124134a1f4eSCasper H.S. Dik case EINVAL:
1125134a1f4eSCasper H.S. Dik case EBADF:
1126134a1f4eSCasper H.S. Dik case EINTR:
1127134a1f4eSCasper H.S. Dik default:
1128134a1f4eSCasper H.S. Dik goto out;
1129134a1f4eSCasper H.S. Dik }
1130134a1f4eSCasper H.S. Dik }
1131134a1f4eSCasper H.S. Dik
1132134a1f4eSCasper H.S. Dik /*
1133134a1f4eSCasper H.S. Dik * Check the size of the result.
1134134a1f4eSCasper H.S. Dik */
1135134a1f4eSCasper H.S. Dik if (da.rsize != sizeof (res))
1136134a1f4eSCasper H.S. Dik goto out;
1137134a1f4eSCasper H.S. Dik
1138134a1f4eSCasper H.S. Dik if (*(uint32_t *)da.rbuf == 1)
1139134a1f4eSCasper H.S. Dik err = 0;
1140134a1f4eSCasper H.S. Dik out:
1141134a1f4eSCasper H.S. Dik if (da.rbuf != (char *)&res)
1142134a1f4eSCasper H.S. Dik kmem_free(da.rbuf, da.rsize);
1143134a1f4eSCasper H.S. Dik out1:
1144134a1f4eSCasper H.S. Dik kmem_free(pap, pasize);
1145134a1f4eSCasper H.S. Dik klpd_rele(pfd);
1146134a1f4eSCasper H.S. Dik return (err);
1147134a1f4eSCasper H.S. Dik }
1148