Note that Jeff indicated to me that Jonathan Lemon gave his permission to
[dragonfly.git] / sys / kern / lwkt_caps.c
CommitLineData
f6bf3af1
MD
1/*
2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
7ed63b6c 26 * $DragonFly: src/sys/kern/lwkt_caps.c,v 1.4 2004/04/26 17:06:18 dillon Exp $
f6bf3af1
MD
27 */
28
29/*
30 * This module implements the DragonFly LWKT IPC rendezvous and message
31 * passing API which operates between userland processes, between userland
32 * threads, and between userland processes and kernel threads. This API
33 * is known as the CAPS interface.
34 *
35 * Generally speaking this module abstracts the LWKT message port interface
36 * into userland Clients and Servers rendezvous through ports named
37 * by or wildcarded by (name,uid,gid). The kernel provides system calls
38 * which may be assigned to the mp_* fields in a userland-supplied
39 * kernel-managed port, and a registration interface which associates an
40 * upcall with a userland port. The kernel tracks authentication information
41 * and deals with connection failures by automatically replying to unreplied
42 * messages.
43 *
44 * From the userland perspective a client/server connection involves two
45 * message ports on the client and two message ports on the server.
46 */
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/kernel.h>
51#include <sys/sysproto.h>
52#include <sys/malloc.h>
53#include <sys/proc.h>
54#include <sys/ucred.h>
55#include <sys/caps.h>
56#include <sys/sysctl.h>
57#include <vm/vm.h>
58#include <vm/vm_extern.h>
59
60static int caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap);
61static void caps_free(caps_kinfo_t caps);
62static void caps_free_msg(caps_kmsg_t msg);
e30f9e2c 63static int caps_name_check(const char *name, int len);
f6bf3af1
MD
64static caps_kinfo_t caps_free_msg_mcaps(caps_kmsg_t msg);
65static caps_kinfo_t kern_caps_sys_service(const char *name, uid_t uid,
66 gid_t gid, struct ucred *cred,
67 int flags, int *error);
68static caps_kinfo_t kern_caps_sys_client(const char *name, uid_t uid,
69 gid_t gid, struct ucred *cred, int flags, int *error);
70
71#define CAPS_HSIZE 64
72#define CAPS_HMASK (CAPS_HSIZE - 1)
73
74static caps_kinfo_t caps_hash_ary[CAPS_HSIZE];
e30f9e2c 75static int caps_waitsvc;
f6bf3af1
MD
76
77MALLOC_DEFINE(M_CAPS, "caps", "caps IPC messaging");
78
79static int caps_enabled;
80SYSCTL_INT(_kern, OID_AUTO, caps_enabled,
81 CTLFLAG_RW, &caps_enabled, 0, "Enable CAPS");
82
83/************************************************************************
84 * INLINE SUPPORT FUNCTIONS *
85 ************************************************************************/
86
87static __inline
88struct caps_kinfo **
89caps_hash(const char *name, int len)
90{
91 int hv = 0x7123F4B3;
92
93 while (--len >= 0)
94 hv = (hv << 5) ^ name[len] ^ (hv >> 23);
95 return(&caps_hash_ary[(hv ^ (hv >> 16)) & CAPS_HMASK]);
96}
97
98static __inline
99void
100caps_hold(caps_kinfo_t caps)
101{
102 ++caps->ci_refs;
103}
104
105static __inline
106void
107caps_drop(caps_kinfo_t caps)
108{
109 if (--caps->ci_refs == 0)
110 caps_free(caps);
111}
112
113/************************************************************************
114 * STATIC SUPPORT FUNCTIONS *
115 ************************************************************************/
116
117static
118caps_kinfo_t
119caps_find(const char *name, int len, uid_t uid, gid_t gid)
120{
121 caps_kinfo_t caps;
122 struct caps_kinfo **chash;
123
124 chash = caps_hash(name, len);
125 for (caps = *chash; caps; caps = caps->ci_hnext) {
126 if ((uid == (uid_t)-1 || uid == caps->ci_uid) &&
127 (gid == (gid_t)-1 || gid == caps->ci_gid) &&
128 len == caps->ci_namelen &&
129 bcmp(name, caps->ci_name, len) == 0
130 ) {
131 caps_hold(caps);
132 break;
133 }
134 }
135 return(caps);
136}
137
138static
139caps_kinfo_t
e30f9e2c 140caps_find_id(thread_t td, int id)
f6bf3af1 141{
f6bf3af1
MD
142 caps_kinfo_t caps;
143
144 for (caps = td->td_caps; caps; caps = caps->ci_tdnext) {
145 if (caps->ci_id == id) {
146 caps_hold(caps);
147 break;
148 }
149 }
150 return(caps);
151}
152
153static
154caps_kinfo_t
e30f9e2c 155caps_alloc(thread_t td, const char *name, int len, uid_t uid, gid_t gid,
f6bf3af1
MD
156 int flags, caps_type_t type)
157{
158 struct caps_kinfo **chash;
f6bf3af1
MD
159 caps_kinfo_t caps;
160 caps_kinfo_t ctmp;
161
162 caps = malloc(offsetof(struct caps_kinfo, ci_name[len+1]),
163 M_CAPS, M_WAITOK|M_ZERO);
164 TAILQ_INIT(&caps->ci_msgpendq);
165 TAILQ_INIT(&caps->ci_msguserq);
166 caps->ci_uid = uid; /* -1 == not registered for uid search */
167 caps->ci_gid = gid; /* -1 == not registered for gid search */
168 caps->ci_type = type;
169 caps->ci_refs = 1; /* CAPKF_TDLIST reference */
170 caps->ci_namelen = len;
171 caps->ci_flags = flags;
172 bcopy(name, caps->ci_name, len + 1);
173 if (type == CAPT_SERVICE) {
174 chash = caps_hash(caps->ci_name, len);
175 caps->ci_hnext = *chash;
176 *chash = caps;
177 caps->ci_flags |= CAPKF_HLIST;
178 }
179 if (td->td_caps) {
180 caps->ci_id = td->td_caps->ci_id + 1;
181 if (caps->ci_id < 0) {
182 /*
183 * It is virtually impossible for this case to occur.
184 */
185 caps->ci_id = 1;
e30f9e2c 186 while ((ctmp = caps_find_id(td, caps->ci_id)) != NULL) {
f6bf3af1
MD
187 caps_drop(ctmp);
188 ++caps->ci_id;
189 }
190 }
191 } else {
192 caps->ci_id = 1;
193 }
194 caps->ci_flags |= CAPKF_TDLIST;
195 caps->ci_tdnext = td->td_caps;
196 caps->ci_td = td;
197 td->td_caps = caps;
198 return(caps);
199}
200
201static
202caps_kmsg_t
203caps_alloc_msg(caps_kinfo_t caps)
204{
205 caps_kmsg_t msg;
206
207 msg = malloc(sizeof(struct caps_kmsg), M_CAPS, M_WAITOK|M_ZERO);
208 msg->km_msgid.c_id = (off_t)(uintptr_t)msg;
209 return(msg);
210}
211
212static
213caps_kmsg_t
214caps_find_msg(caps_kinfo_t caps, off_t msgid)
215{
216 caps_kmsg_t msg;
217
218 TAILQ_FOREACH(msg, &caps->ci_msguserq, km_node) {
219 if (msg->km_msgid.c_id == msgid)
220 return(msg);
221 }
222 TAILQ_FOREACH(msg, &caps->ci_msgpendq, km_node) {
223 if (msg->km_msgid.c_id == msgid)
224 return(msg);
225 }
226 return(NULL);
227}
228
229static
230caps_kinfo_t
02fb4e24
MD
231caps_load_ccr(caps_kinfo_t caps, caps_kmsg_t msg, struct proc *p,
232 void *udata, int ubytes)
f6bf3af1
MD
233{
234 int i;
235 struct ucred *cr = p->p_ucred;
236 caps_kinfo_t rcaps;
237
238 /*
02fb4e24
MD
239 * replace km_mcaps with new VM state, return the old km_mcaps. The
240 * caller is expected to drop the rcaps ref count on return so we do
241 * not do it ourselves.
f6bf3af1
MD
242 */
243 rcaps = caps_free_msg_mcaps(msg); /* can be NULL */
f6bf3af1
MD
244 caps_hold(caps);
245 msg->km_mcaps = caps;
02fb4e24 246 xio_init_ubuf(&msg->km_xio, udata, ubytes, XIOF_READ);
f6bf3af1
MD
247
248 msg->km_ccr.pid = p ? p->p_pid : -1;
249 msg->km_ccr.uid = cr->cr_ruid;
250 msg->km_ccr.euid = cr->cr_uid;
251 msg->km_ccr.gid = cr->cr_rgid;
252 msg->km_ccr.ngroups = MIN(cr->cr_ngroups, CAPS_MAXGROUPS);
253 for (i = 0; i < msg->km_ccr.ngroups; ++i)
254 msg->km_ccr.groups[i] = cr->cr_groups[i];
255 return(rcaps);
256}
257
258static void
259caps_dequeue_msg(caps_kinfo_t caps, caps_kmsg_t msg)
260{
261 if (msg->km_flags & CAPKMF_ONUSERQ)
262 TAILQ_REMOVE(&caps->ci_msguserq, msg, km_node);
263 if (msg->km_flags & CAPKMF_ONPENDQ)
264 TAILQ_REMOVE(&caps->ci_msgpendq, msg, km_node);
265 msg->km_flags &= ~(CAPKMF_ONPENDQ|CAPKMF_ONUSERQ);
266}
267
268static void
269caps_put_msg(caps_kinfo_t caps, caps_kmsg_t msg, caps_msg_state_t state)
270{
271 KKASSERT((msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ)) == 0);
272
273 msg->km_flags |= CAPKMF_ONPENDQ;
274 msg->km_flags &= ~CAPKMF_PEEKED;
275 msg->km_state = state;
276 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
277
278 /*
279 * Instead of waking up the service for both new messages and disposals,
280 * just wakeup the service for new messages and it will process the
281 * previous disposal in the same loop, reducing the number of context
282 * switches required to run an IPC.
283 */
284 if (state != CAPMS_DISPOSE)
285 wakeup(caps);
286 caps_drop(caps);
287}
288
289/*
290 * caps_free_msg_mcaps()
f6bf3af1
MD
291 */
292static
293caps_kinfo_t
294caps_free_msg_mcaps(caps_kmsg_t msg)
295{
296 caps_kinfo_t mcaps;
297
02fb4e24
MD
298 mcaps = msg->km_mcaps; /* may be NULL */
299 msg->km_mcaps = NULL;
300 if (msg->km_xio.xio_npages)
301 xio_release(&msg->km_xio);
f6bf3af1
MD
302 return(mcaps);
303}
304
305/*
306 * caps_free_msg()
307 *
308 * Free a caps placeholder message. The message must not be on any queues.
309 */
310static void
311caps_free_msg(caps_kmsg_t msg)
312{
313 caps_kinfo_t rcaps;
314
315 if ((rcaps = caps_free_msg_mcaps(msg)) != NULL)
316 caps_drop(rcaps);
317 free(msg, M_CAPS);
318}
319
e30f9e2c
MD
320/*
321 * Validate the service name
322 */
323static int
324caps_name_check(const char *name, int len)
325{
326 int i;
327 char c;
328
329 for (i = len - 1; i >= 0; --i) {
330 c = name[i];
331 if (c >= '0' && c <= '9')
332 continue;
333 if (c >= 'a' && c <= 'z')
334 continue;
335 if (c >= 'A' && c <= 'Z')
336 continue;
337 if (c == '_' || c == '.')
338 continue;
339 return(EINVAL);
340 }
341 return(0);
342}
343
f6bf3af1
MD
344/*
345 * caps_term()
346 *
347 * Terminate portions of a caps info structure. This is used to close
348 * an end-point or to flush particular messages on an end-point.
349 *
350 * This function should not be called with CAPKF_TDLIST unless the caller
351 * has an additional hold on the caps structure.
352 */
353static void
354caps_term(caps_kinfo_t caps, int flags, caps_kinfo_t cflush)
355{
356 struct caps_kinfo **scan;
357 caps_kmsg_t msg;
358
359 if (flags & CAPKF_TDLIST)
360 caps->ci_flags |= CAPKF_CLOSED;
361
362 if (flags & CAPKF_FLUSH) {
363 int mflags;
364 struct caps_kmsg_queue tmpuserq;
365 struct caps_kmsg_queue tmppendq;
366 caps_kinfo_t rcaps;
367
368 TAILQ_INIT(&tmpuserq);
369 TAILQ_INIT(&tmppendq);
370
371 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) != NULL ||
372 (msg = TAILQ_FIRST(&caps->ci_msguserq)) != NULL
373 ) {
374 mflags = msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ);
375 caps_dequeue_msg(caps, msg);
376
377 if (cflush && msg->km_mcaps != cflush) {
378 if (mflags & CAPKMF_ONUSERQ)
379 TAILQ_INSERT_TAIL(&tmpuserq, msg, km_node);
380 else
381 TAILQ_INSERT_TAIL(&tmppendq, msg, km_node);
382 } else {
383 /*
384 * Dispose of the message. If the received message is a
385 * request we must reply it. If the received message is
386 * a reply we must return it for disposal. If the
387 * received message is a disposal request we simply free it.
388 */
389 switch(msg->km_state) {
390 case CAPMS_REQUEST:
391 case CAPMS_REQUEST_RETRY:
392 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
393 if (rcaps->ci_flags & CAPKF_CLOSED) {
394 /*
395 * can't reply, if we never read the message (its on
396 * the pending queue), or if we are closed ourselves,
397 * we can just free the message. Otherwise we have
398 * to send ourselves a disposal request (multi-threaded
399 * services have to deal with disposal requests for
400 * messages that might be in progress).
401 */
402 if ((caps->ci_flags & CAPKF_CLOSED) ||
403 (mflags & CAPKMF_ONPENDQ)
404 ) {
405 caps_free_msg(msg);
406 caps_drop(rcaps);
407 } else {
408 caps_drop(rcaps);
02fb4e24 409 caps_hold(caps); /* for message */
f6bf3af1
MD
410 caps_put_msg(caps, msg, CAPMS_DISPOSE);
411 }
412 } else {
413 /*
02fb4e24
MD
414 * auto-reply to the originator. rcaps already
415 * has a dangling hold so we do not have to hold it
416 * again.
f6bf3af1
MD
417 */
418 caps_put_msg(rcaps, msg, CAPMS_REPLY);
419 }
420 break;
421 case CAPMS_REPLY:
422 case CAPMS_REPLY_RETRY:
423 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
424 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
425 caps_free_msg(msg); /* degenerate disposal case */
426 caps_drop(rcaps);
427 } else {
428 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
429 }
430 break;
431 case CAPMS_DISPOSE:
432 caps_free_msg(msg);
433 break;
434 }
435 }
436 }
437 while ((msg = TAILQ_FIRST(&tmpuserq)) != NULL) {
438 TAILQ_REMOVE(&tmpuserq, msg, km_node);
439 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
440 msg->km_flags |= CAPKMF_ONUSERQ;
441 }
442 while ((msg = TAILQ_FIRST(&tmppendq)) != NULL) {
443 TAILQ_REMOVE(&tmppendq, msg, km_node);
444 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
445 msg->km_flags |= CAPKMF_ONPENDQ;
446 }
447 }
448 if ((flags & CAPKF_HLIST) && (caps->ci_flags & CAPKF_HLIST)) {
449 for (scan = caps_hash(caps->ci_name, caps->ci_namelen);
450 *scan != caps;
451 scan = &(*scan)->ci_hnext
452 ) {
453 KKASSERT(*scan != NULL);
454 }
455 *scan = caps->ci_hnext;
456 caps->ci_hnext = (void *)-1;
457 caps->ci_flags &= ~CAPKF_HLIST;
458 }
459 if ((flags & CAPKF_TDLIST) && (caps->ci_flags & CAPKF_TDLIST)) {
f6bf3af1
MD
460 for (scan = &caps->ci_td->td_caps;
461 *scan != caps;
462 scan = &(*scan)->ci_tdnext
463 ) {
464 KKASSERT(*scan != NULL);
465 }
466 *scan = caps->ci_tdnext;
467 caps->ci_flags &= ~CAPKF_TDLIST;
468 caps->ci_tdnext = (void *)-1;
469 caps->ci_td = NULL;
470 caps_drop(caps);
471 }
472 if ((flags & CAPKF_RCAPS) && (caps->ci_flags & CAPKF_RCAPS)) {
473 caps_kinfo_t ctmp;
474
475 caps->ci_flags &= ~CAPKF_RCAPS;
476 if ((ctmp = caps->ci_rcaps)) {
477 caps->ci_rcaps = NULL;
478 caps_term(ctmp, CAPKF_FLUSH, caps);
479 caps_drop(ctmp);
480 }
481 }
482}
483
484static void
485caps_free(caps_kinfo_t caps)
486{
487 KKASSERT(TAILQ_EMPTY(&caps->ci_msgpendq));
488 KKASSERT(TAILQ_EMPTY(&caps->ci_msguserq));
489 KKASSERT((caps->ci_flags & (CAPKF_HLIST|CAPKF_TDLIST)) == 0);
490 free(caps, M_CAPS);
491}
492
493/************************************************************************
494 * PROCESS SUPPORT FUNCTIONS *
495 ************************************************************************/
496
e30f9e2c
MD
497/*
498 * Create dummy entries in p2 so we can return the appropriate
499 * error code. Robust userland code will check the error for a
500 * forked condition and reforge the connection.
501 */
f6bf3af1 502void
e30f9e2c 503caps_fork(struct proc *p1, struct proc *p2, int flags)
f6bf3af1 504{
e30f9e2c
MD
505 caps_kinfo_t caps1;
506 caps_kinfo_t caps2;
507 thread_t td1;
508 thread_t td2;
509
510 td1 = p1->p_thread;
511 td2 = p2->p_thread;
512
513 /*
514 * Create dummy entries with the same id's as the originals. Note
515 * that service entries are not re-added to the hash table. The
516 * dummy entries return an ENOTCONN error allowing userland code to
517 * detect that a fork occured. Userland must reconnect to the service.
518 */
519 for (caps1 = td1->td_caps; caps1; caps1 = caps1->ci_tdnext) {
520 if (caps1->ci_flags & CAPF_NOFORK)
521 continue;
522 caps2 = caps_alloc(td2,
523 caps1->ci_name, caps1->ci_namelen,
524 caps1->ci_uid, caps1->ci_gid,
525 caps1->ci_flags & CAPF_UFLAGS, CAPT_FORKED);
526 caps2->ci_id = caps1->ci_id;
527 }
528
529 /*
530 * Reverse the list order to maintain highest-id-first
531 */
532 caps2 = td2->td_caps;
533 td2->td_caps = NULL;
534 while (caps2) {
535 caps1 = caps2->ci_tdnext;
536 caps2->ci_tdnext = td2->td_caps;
537 td2->td_caps = caps2;
538 caps2 = caps1;
539 }
f6bf3af1
MD
540}
541
542void
543caps_exit(struct thread *td)
544{
545 caps_kinfo_t caps;
546
547 while ((caps = td->td_caps) != NULL) {
548 caps_hold(caps);
549 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
550 caps_drop(caps);
551 }
552}
553
554/************************************************************************
555 * SYSTEM CALLS *
556 ************************************************************************/
557
558/*
559 * caps_sys_service(name, uid, gid, upcid, flags);
560 *
561 * Create an IPC service using the specified name, uid, gid, and flags.
562 * Either uid or gid can be -1, but not both. The port identifier is
563 * returned.
564 *
565 * upcid can either be an upcall or a kqueue identifier (XXX)
566 */
567int
568caps_sys_service(struct caps_sys_service_args *uap)
569{
570 struct ucred *cred = curproc->p_ucred;
571 char name[CAPS_MAXNAMELEN];
572 caps_kinfo_t caps;
573 int len;
574 int error;
575
576 if (caps_enabled == 0)
577 return(EOPNOTSUPP);
578 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
579 return(error);
580 if (--len <= 0)
581 return(EINVAL);
e30f9e2c
MD
582 if ((error = caps_name_check(name, len)) != 0)
583 return(error);
f6bf3af1
MD
584
585 caps = kern_caps_sys_service(name, uap->uid, uap->gid, cred,
586 uap->flags & CAPF_UFLAGS, &error);
587 if (caps)
588 uap->sysmsg_result = caps->ci_id;
589 return(error);
590}
591
592/*
593 * caps_sys_client(name, uid, gid, upcid, flags);
594 *
595 * Create an IPC client connected to the specified service. Either uid or gid
596 * may be -1, indicating a wildcard, but not both. The port identifier is
597 * returned.
598 *
599 * upcid can either be an upcall or a kqueue identifier (XXX)
600 */
601int
602caps_sys_client(struct caps_sys_client_args *uap)
603{
604 struct ucred *cred = curproc->p_ucred;
605 char name[CAPS_MAXNAMELEN];
606 caps_kinfo_t caps;
607 int len;
608 int error;
609
610 if (caps_enabled == 0)
611 return(EOPNOTSUPP);
612 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
613 return(error);
614 if (--len <= 0)
615 return(EINVAL);
e30f9e2c
MD
616 if ((error = caps_name_check(name, len)) != 0)
617 return(error);
f6bf3af1
MD
618
619 caps = kern_caps_sys_client(name, uap->uid, uap->gid, cred,
620 uap->flags & CAPF_UFLAGS, &error);
621 if (caps)
622 uap->sysmsg_result = caps->ci_id;
623 return(error);
624}
625
626int
627caps_sys_close(struct caps_sys_close_args *uap)
628{
629 caps_kinfo_t caps;
630
e30f9e2c 631 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1
MD
632 return(EINVAL);
633 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
634 caps_drop(caps);
635 return(0);
636}
637
e30f9e2c
MD
638int
639caps_sys_setgen(struct caps_sys_setgen_args *uap)
640{
641 caps_kinfo_t caps;
02fb4e24 642 int error;
e30f9e2c
MD
643
644 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
645 return(EINVAL);
02fb4e24
MD
646 if (caps->ci_type == CAPT_FORKED) {
647 error = ENOTCONN;
648 } else {
649 caps->ci_gen = uap->gen;
650 error = 0;
651 }
652 caps_drop(caps);
653 return(error);
e30f9e2c
MD
654}
655
656int
657caps_sys_getgen(struct caps_sys_getgen_args *uap)
658{
659 caps_kinfo_t caps;
02fb4e24 660 int error;
e30f9e2c
MD
661
662 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
663 return(EINVAL);
02fb4e24
MD
664 if (caps->ci_type == CAPT_FORKED) {
665 error = ENOTCONN;
666 } else if (caps->ci_rcaps == NULL) {
667 error = EINVAL;
668 } else {
669 uap->sysmsg_result64 = caps->ci_rcaps->ci_gen;
670 error = 0;
671 }
672 caps_drop(caps);
673 return(error);
e30f9e2c
MD
674}
675
f6bf3af1
MD
676/*
677 * caps_sys_put(portid, msg, msgsize)
678 *
679 * Send an opaque message of the specified size to the specified port. This
680 * function may only be used with a client port. The message id is returned.
681 */
682int
683caps_sys_put(struct caps_sys_put_args *uap)
684{
685 caps_kinfo_t caps;
686 caps_kmsg_t msg;
687 struct proc *p = curproc;
e30f9e2c 688 int error;
f6bf3af1
MD
689
690 if (uap->msgsize < 0)
691 return(EINVAL);
e30f9e2c 692 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 693 return(EINVAL);
02fb4e24 694 if (caps->ci_type == CAPT_FORKED) {
e30f9e2c 695 error = ENOTCONN;
02fb4e24
MD
696 } else if (caps->ci_rcaps == NULL) {
697 error = EINVAL;
698 } else if (caps->ci_cmsgcount > CAPS_MAXINPROG) {
699 /*
700 * If this client has queued a large number of messages return
701 * ENOBUFS. The client must process some replies before it can
702 * send new messages. The server can also throttle a client by
703 * holding its replies. XXX allow a server to refuse messages from
704 * a client.
705 */
706 error = ENOBUFS;
f6bf3af1 707 } else {
02fb4e24
MD
708 msg = caps_alloc_msg(caps);
709 uap->sysmsg_offset = msg->km_msgid.c_id;
710
711 /*
712 * If the remote end is closed return ENOTCONN immediately, otherwise
713 * send it to the remote end.
714 *
715 * Note: since this is a new message, caps_load_ccr() returns a remote
716 * caps of NULL.
717 */
718 if (caps->ci_rcaps->ci_flags & CAPKF_CLOSED) {
719 error = ENOTCONN;
720 caps_free_msg(msg);
721 } else {
722 /*
723 * new message, load_ccr returns NULL. hold rcaps for put_msg
724 */
725 error = 0;
726 caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
727 caps_hold(caps->ci_rcaps);
728 ++caps->ci_cmsgcount;
729 caps_put_msg(caps->ci_rcaps, msg, CAPMS_REQUEST); /* drops rcaps */
730 }
f6bf3af1
MD
731 }
732 caps_drop(caps);
e30f9e2c 733 return(error);
f6bf3af1
MD
734}
735
736/*
737 * caps_sys_reply(portid, msg, msgsize, msgid)
738 *
739 * Reply to the message referenced by the specified msgid, supplying opaque
740 * data back to the originator.
741 */
742int
743caps_sys_reply(struct caps_sys_reply_args *uap)
744{
745 caps_kinfo_t caps;
746 caps_kinfo_t rcaps;
747 caps_kmsg_t msg;
748 struct proc *p;
02fb4e24 749 int error;
f6bf3af1
MD
750
751 if (uap->msgsize < 0)
752 return(EINVAL);
e30f9e2c 753 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 754 return(EINVAL);
02fb4e24
MD
755 if (caps->ci_type == CAPT_FORKED) {
756 /*
757 * The caps structure is just a fork placeholder, tell the caller
758 * that he has to reconnect.
759 */
760 error = ENOTCONN;
761 } else if ((msg = caps_find_msg(caps, uap->msgcid)) == NULL) {
762 /*
763 * Could not find message being replied to (other side might have
764 * gone away).
765 */
766 error = EINVAL;
767 } else if ((msg->km_flags & CAPKMF_ONUSERQ) == 0) {
768 /*
769 * Trying to reply to a non-replyable message
770 */
771 error = EINVAL;
f6bf3af1 772 } else {
02fb4e24
MD
773 /*
774 * If the remote end is closed requeue to ourselves for disposal.
775 * Otherwise send the reply to the other end (the other end will
776 * return a passive DISPOSE to us when it has eaten the data)
777 */
778 error = 0;
779 caps_dequeue_msg(caps, msg);
780 p = curproc;
781 if (msg->km_mcaps->ci_flags & CAPKF_CLOSED) {
782 caps_drop(caps_load_ccr(caps, msg, p, NULL, 0));
783 caps_hold(caps); /* ref for message */
784 caps_put_msg(caps, msg, CAPMS_DISPOSE);
785 } else {
786 rcaps = caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
787 caps_put_msg(rcaps, msg, CAPMS_REPLY);
788 }
f6bf3af1
MD
789 }
790 caps_drop(caps);
02fb4e24 791 return(error);
f6bf3af1
MD
792}
793
794/*
795 * caps_sys_get(portid, msg, maxsize, msgid, ccr)
796 *
797 * Retrieve the next ready message on the port, store its message id in
798 * uap->msgid and return the length of the message. If the message is too
799 * large to fit the message id, length, and creds are still returned, but
800 * the message is not dequeued (the caller is expected to call again with
801 * a larger buffer or to reply the messageid if it does not want to handle
802 * the message).
803 *
804 * EWOULDBLOCK is returned if no messages are pending. Note that 0-length
805 * messages are perfectly acceptable so 0 can be legitimately returned.
806 */
807int
808caps_sys_get(struct caps_sys_get_args *uap)
809{
810 caps_kinfo_t caps;
811 caps_kmsg_t msg;
02fb4e24 812 int error;
f6bf3af1
MD
813
814 if (uap->maxsize < 0)
815 return(EINVAL);
e30f9e2c 816 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 817 return(EINVAL);
02fb4e24
MD
818 if (caps->ci_type == CAPT_FORKED) {
819 error = ENOTCONN;
820 } else if ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
821 error = EWOULDBLOCK;
822 } else {
823 error = caps_process_msg(caps, msg, uap);
f6bf3af1 824 }
02fb4e24
MD
825 caps_drop(caps);
826 return(error);
f6bf3af1
MD
827}
828
829/*
830 * caps_sys_wait(portid, msg, maxsize, msgid, ccr)
831 *
832 * Retrieve the next ready message on the port, store its message id in
833 * uap->msgid and return the length of the message. If the message is too
834 * large to fit the message id, length, and creds are still returned, but
835 * the message is not dequeued (the caller is expected to call again with
836 * a larger buffer or to reply the messageid if it does not want to handle
837 * the message).
838 *
839 * This function blocks until interrupted or a message is received.
840 * Note that 0-length messages are perfectly acceptable so 0 can be
841 * legitimately returned.
842 */
843int
844caps_sys_wait(struct caps_sys_wait_args *uap)
845{
846 caps_kinfo_t caps;
847 caps_kmsg_t msg;
848 int error;
849
850 if (uap->maxsize < 0)
851 return(EINVAL);
e30f9e2c 852 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 853 return(EINVAL);
02fb4e24
MD
854 if (caps->ci_type == CAPT_FORKED) {
855 error = ENOTCONN;
856 } else {
857 error = 0;
858 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
859 if ((error = tsleep(caps, PCATCH, "caps", 0)) != 0)
860 break;
861 }
862 if (error == 0) {
863 error = caps_process_msg(caps, msg,
864 (struct caps_sys_get_args *)uap);
f6bf3af1
MD
865 }
866 }
02fb4e24
MD
867 caps_drop(caps);
868 return(error);
f6bf3af1
MD
869}
870
871static int
872caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap)
873{
874 int error = 0;
875 int msgsize;
876 caps_kinfo_t rcaps;
877
878 msg->km_flags |= CAPKMF_PEEKED;
02fb4e24 879 msgsize = msg->km_xio.xio_bytes;
f6bf3af1
MD
880 if (msgsize <= uap->maxsize)
881 caps_dequeue_msg(caps, msg);
882
02fb4e24 883 if (msg->km_xio.xio_bytes != 0) {
02fb4e24
MD
884 error = xio_copy_xtou(&msg->km_xio, uap->msg,
885 min(msg->km_xio.xio_bytes, uap->maxsize));
f6bf3af1 886 if (error) {
7ed63b6c
MD
887 if (msg->km_mcaps->ci_td && msg->km_mcaps->ci_td->td_proc) {
888 printf("xio_copy_xtou: error %d from proc %d\n",
889 error, msg->km_mcaps->ci_td->td_proc->p_pid);
890 }
f6bf3af1
MD
891 if (msgsize > uap->maxsize)
892 caps_dequeue_msg(caps, msg);
893 msgsize = 0;
894 error = 0;
895 }
896 }
897
898 if (uap->msgid)
899 error = copyout(&msg->km_msgid, uap->msgid, sizeof(msg->km_msgid));
900 if (uap->ccr)
901 error = copyout(&msg->km_ccr, uap->ccr, sizeof(msg->km_ccr));
902 if (error == 0)
903 uap->sysmsg_result = msgsize;
904
905 /*
906 * If the message was dequeued we must deal with it.
907 */
908 if (msgsize <= uap->maxsize) {
909 switch(msg->km_state) {
910 case CAPMS_REQUEST:
911 case CAPMS_REQUEST_RETRY:
912 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
913 msg->km_flags |= CAPKMF_ONUSERQ;
914 break;
915 case CAPMS_REPLY:
916 case CAPMS_REPLY_RETRY:
917 --caps->ci_cmsgcount;
918 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
919 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
920 /* degenerate disposal case */
921 caps_free_msg(msg);
922 caps_drop(rcaps);
923 } else {
924 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
925 }
926 break;
927 case CAPMS_DISPOSE:
928 caps_free_msg(msg);
929 break;
930 }
931 }
f6bf3af1
MD
932 return(error);
933}
934
935/*
936 * caps_sys_abort(portid, msgcid, flags)
937 *
938 * Abort a previously sent message. You must still wait for the message
939 * to be returned after sending the abort request. This function will
940 * return the appropriate CAPS_ABORT_* code depending on what it had
941 * to do.
942 */
943int
944caps_sys_abort(struct caps_sys_abort_args *uap)
945{
946 uap->sysmsg_result = CAPS_ABORT_NOTIMPL;
947 return(0);
948}
949
950/*
951 * KERNEL SYSCALL SEPARATION SUPPORT FUNCTIONS
952 */
953
954static
955caps_kinfo_t
956kern_caps_sys_service(const char *name, uid_t uid, gid_t gid,
957 struct ucred *cred, int flags, int *error)
958{
959 caps_kinfo_t caps;
960 int len;
961
962 len = strlen(name);
963
964 /*
965 * Make sure we can use the uid and gid
966 */
967 if (cred) {
968 if (cred->cr_uid != 0 && uid != (uid_t)-1 && cred->cr_uid != uid) {
969 *error = EPERM;
970 return(NULL);
971 }
972 if (cred->cr_uid != 0 && gid != (gid_t)-1 && !groupmember(gid, cred)) {
973 *error = EPERM;
974 return(NULL);
975 }
976 }
977
978 /*
979 * Handle CAPF_EXCL
980 */
981 if (flags & CAPF_EXCL) {
982 if ((caps = caps_find(name, strlen(name), uid, gid)) != NULL) {
983 caps_drop(caps);
984 *error = EEXIST;
985 return(NULL);
986 }
987 }
988
989 /*
990 * Create the service
991 */
e30f9e2c
MD
992 caps = caps_alloc(curthread, name, len,
993 uid, gid, flags & CAPF_UFLAGS, CAPT_SERVICE);
994 wakeup(&caps_waitsvc);
f6bf3af1
MD
995 return(caps);
996}
997
998static
999caps_kinfo_t
1000kern_caps_sys_client(const char *name, uid_t uid, gid_t gid,
1001 struct ucred *cred, int flags, int *error)
1002{
1003 caps_kinfo_t caps, rcaps;
1004 int len;
1005
1006 len = strlen(name);
1007
1008 /*
1009 * Locate the CAPS service (rcaps ref is for caps->ci_rcaps)
1010 */
e30f9e2c 1011again:
f6bf3af1 1012 if ((rcaps = caps_find(name, len, uid, gid)) == NULL) {
e30f9e2c
MD
1013 if (flags & CAPF_WAITSVC) {
1014 char cbuf[32];
1015 snprintf(cbuf, sizeof(cbuf), "C%s", name);
1016 *error = tsleep(&caps_waitsvc, PCATCH, cbuf, 0);
1017 if (*error == 0)
1018 goto again;
1019 } else {
1020 *error = ENOENT;
1021 }
f6bf3af1
MD
1022 return(NULL);
1023 }
1024
1025 /*
1026 * Check permissions
1027 */
1028 if (cred) {
1029 *error = EACCES;
1030 if ((flags & CAPF_USER) && (rcaps->ci_flags & CAPF_USER)) {
1031 if (rcaps->ci_uid != (uid_t)-1 && rcaps->ci_uid == cred->cr_uid)
1032 *error = 0;
1033 }
1034 if ((flags & CAPF_GROUP) && (rcaps->ci_flags & CAPF_GROUP)) {
1035 if (rcaps->ci_gid != (gid_t)-1 && groupmember(rcaps->ci_gid, cred))
1036 *error = 0;
1037 }
1038 if ((flags & CAPF_WORLD) && (rcaps->ci_flags & CAPF_WORLD)) {
1039 *error = 0;
1040 }
1041 if (*error) {
1042 caps_drop(rcaps);
1043 return(NULL);
1044 }
1045 } else {
1046 *error = 0;
1047 }
1048
1049 /*
1050 * Allocate the client side and connect to the server
1051 */
e30f9e2c
MD
1052 caps = caps_alloc(curthread, name, len,
1053 uid, gid, flags & CAPF_UFLAGS, CAPT_CLIENT);
f6bf3af1
MD
1054 caps->ci_rcaps = rcaps;
1055 caps->ci_flags |= CAPKF_RCAPS;
1056 return(caps);
1057}
1058