CAPS IPC library stage 1/3: The core CAPS IPC code, providing system calls
[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 *
26 * $DragonFly: src/sys/kern/lwkt_caps.c,v 1.1 2004/01/18 12:29:49 dillon Exp $
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);
63static caps_kinfo_t caps_free_msg_mcaps(caps_kmsg_t msg);
64static caps_kinfo_t kern_caps_sys_service(const char *name, uid_t uid,
65 gid_t gid, struct ucred *cred,
66 int flags, int *error);
67static caps_kinfo_t kern_caps_sys_client(const char *name, uid_t uid,
68 gid_t gid, struct ucred *cred, int flags, int *error);
69
70#define CAPS_HSIZE 64
71#define CAPS_HMASK (CAPS_HSIZE - 1)
72
73static caps_kinfo_t caps_hash_ary[CAPS_HSIZE];
74
75MALLOC_DEFINE(M_CAPS, "caps", "caps IPC messaging");
76
77static int caps_enabled;
78SYSCTL_INT(_kern, OID_AUTO, caps_enabled,
79 CTLFLAG_RW, &caps_enabled, 0, "Enable CAPS");
80
81/************************************************************************
82 * INLINE SUPPORT FUNCTIONS *
83 ************************************************************************/
84
85static __inline
86struct caps_kinfo **
87caps_hash(const char *name, int len)
88{
89 int hv = 0x7123F4B3;
90
91 while (--len >= 0)
92 hv = (hv << 5) ^ name[len] ^ (hv >> 23);
93 return(&caps_hash_ary[(hv ^ (hv >> 16)) & CAPS_HMASK]);
94}
95
96static __inline
97void
98caps_hold(caps_kinfo_t caps)
99{
100 ++caps->ci_refs;
101}
102
103static __inline
104void
105caps_drop(caps_kinfo_t caps)
106{
107 if (--caps->ci_refs == 0)
108 caps_free(caps);
109}
110
111/************************************************************************
112 * STATIC SUPPORT FUNCTIONS *
113 ************************************************************************/
114
115static
116caps_kinfo_t
117caps_find(const char *name, int len, uid_t uid, gid_t gid)
118{
119 caps_kinfo_t caps;
120 struct caps_kinfo **chash;
121
122 chash = caps_hash(name, len);
123 for (caps = *chash; caps; caps = caps->ci_hnext) {
124 if ((uid == (uid_t)-1 || uid == caps->ci_uid) &&
125 (gid == (gid_t)-1 || gid == caps->ci_gid) &&
126 len == caps->ci_namelen &&
127 bcmp(name, caps->ci_name, len) == 0
128 ) {
129 caps_hold(caps);
130 break;
131 }
132 }
133 return(caps);
134}
135
136static
137caps_kinfo_t
138caps_find_id(int id)
139{
140 thread_t td = curthread;
141 caps_kinfo_t caps;
142
143 for (caps = td->td_caps; caps; caps = caps->ci_tdnext) {
144 if (caps->ci_id == id) {
145 caps_hold(caps);
146 break;
147 }
148 }
149 return(caps);
150}
151
152static
153caps_kinfo_t
154caps_alloc(const char *name, int len, uid_t uid, gid_t gid,
155 int flags, caps_type_t type)
156{
157 struct caps_kinfo **chash;
158 thread_t td = curthread;
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;
186 while ((ctmp = caps_find_id(caps->ci_id)) != NULL) {
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
231caps_load_ccr(caps_kinfo_t caps, caps_kmsg_t msg, struct proc *p, void *udata, int ubytes)
232{
233 int i;
234 struct ucred *cr = p->p_ucred;
235 caps_kinfo_t rcaps;
236
237 /*
238 * replace km_mcaps with new VM state, return the old km_mcaps. We
239 * dereference the old mcap's mrefs but do not drop its main ref count.
240 * The caller is expected to do that.
241 */
242 rcaps = caps_free_msg_mcaps(msg); /* can be NULL */
243 ++caps->ci_mrefs;
244 caps_hold(caps);
245 msg->km_mcaps = caps;
246 msg->km_umsg = udata;
247 msg->km_umsg_size = ubytes;
248
249 msg->km_ccr.pid = p ? p->p_pid : -1;
250 msg->km_ccr.uid = cr->cr_ruid;
251 msg->km_ccr.euid = cr->cr_uid;
252 msg->km_ccr.gid = cr->cr_rgid;
253 msg->km_ccr.ngroups = MIN(cr->cr_ngroups, CAPS_MAXGROUPS);
254 for (i = 0; i < msg->km_ccr.ngroups; ++i)
255 msg->km_ccr.groups[i] = cr->cr_groups[i];
256 return(rcaps);
257}
258
259static void
260caps_dequeue_msg(caps_kinfo_t caps, caps_kmsg_t msg)
261{
262 if (msg->km_flags & CAPKMF_ONUSERQ)
263 TAILQ_REMOVE(&caps->ci_msguserq, msg, km_node);
264 if (msg->km_flags & CAPKMF_ONPENDQ)
265 TAILQ_REMOVE(&caps->ci_msgpendq, msg, km_node);
266 msg->km_flags &= ~(CAPKMF_ONPENDQ|CAPKMF_ONUSERQ);
267}
268
269static void
270caps_put_msg(caps_kinfo_t caps, caps_kmsg_t msg, caps_msg_state_t state)
271{
272 KKASSERT((msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ)) == 0);
273
274 msg->km_flags |= CAPKMF_ONPENDQ;
275 msg->km_flags &= ~CAPKMF_PEEKED;
276 msg->km_state = state;
277 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
278
279 /*
280 * Instead of waking up the service for both new messages and disposals,
281 * just wakeup the service for new messages and it will process the
282 * previous disposal in the same loop, reducing the number of context
283 * switches required to run an IPC.
284 */
285 if (state != CAPMS_DISPOSE)
286 wakeup(caps);
287 caps_drop(caps);
288}
289
290/*
291 * caps_free_msg_mcaps()
292 *
293 * Free the vmspace reference relating to the data associated with the
294 * message (this prevents the target process from exiting too early).
295 * Return and clear km_mcaps. The caller is responsible for dropping the
296 * reference to the returned caps.
297 */
298static
299caps_kinfo_t
300caps_free_msg_mcaps(caps_kmsg_t msg)
301{
302 caps_kinfo_t mcaps;
303
304 if ((mcaps = msg->km_mcaps) != NULL) {
305 msg->km_mcaps = NULL;
306 if (--mcaps->ci_mrefs == 0 && (mcaps->ci_flags & CAPKF_MWAIT))
307 wakeup(mcaps);
308 }
309 return(mcaps);
310}
311
312/*
313 * caps_free_msg()
314 *
315 * Free a caps placeholder message. The message must not be on any queues.
316 */
317static void
318caps_free_msg(caps_kmsg_t msg)
319{
320 caps_kinfo_t rcaps;
321
322 if ((rcaps = caps_free_msg_mcaps(msg)) != NULL)
323 caps_drop(rcaps);
324 free(msg, M_CAPS);
325}
326
327/*
328 * caps_term()
329 *
330 * Terminate portions of a caps info structure. This is used to close
331 * an end-point or to flush particular messages on an end-point.
332 *
333 * This function should not be called with CAPKF_TDLIST unless the caller
334 * has an additional hold on the caps structure.
335 */
336static void
337caps_term(caps_kinfo_t caps, int flags, caps_kinfo_t cflush)
338{
339 struct caps_kinfo **scan;
340 caps_kmsg_t msg;
341
342 if (flags & CAPKF_TDLIST)
343 caps->ci_flags |= CAPKF_CLOSED;
344
345 if (flags & CAPKF_FLUSH) {
346 int mflags;
347 struct caps_kmsg_queue tmpuserq;
348 struct caps_kmsg_queue tmppendq;
349 caps_kinfo_t rcaps;
350
351 TAILQ_INIT(&tmpuserq);
352 TAILQ_INIT(&tmppendq);
353
354 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) != NULL ||
355 (msg = TAILQ_FIRST(&caps->ci_msguserq)) != NULL
356 ) {
357 mflags = msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ);
358 caps_dequeue_msg(caps, msg);
359
360 if (cflush && msg->km_mcaps != cflush) {
361 if (mflags & CAPKMF_ONUSERQ)
362 TAILQ_INSERT_TAIL(&tmpuserq, msg, km_node);
363 else
364 TAILQ_INSERT_TAIL(&tmppendq, msg, km_node);
365 } else {
366 /*
367 * Dispose of the message. If the received message is a
368 * request we must reply it. If the received message is
369 * a reply we must return it for disposal. If the
370 * received message is a disposal request we simply free it.
371 */
372 switch(msg->km_state) {
373 case CAPMS_REQUEST:
374 case CAPMS_REQUEST_RETRY:
375 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
376 if (rcaps->ci_flags & CAPKF_CLOSED) {
377 /*
378 * can't reply, if we never read the message (its on
379 * the pending queue), or if we are closed ourselves,
380 * we can just free the message. Otherwise we have
381 * to send ourselves a disposal request (multi-threaded
382 * services have to deal with disposal requests for
383 * messages that might be in progress).
384 */
385 if ((caps->ci_flags & CAPKF_CLOSED) ||
386 (mflags & CAPKMF_ONPENDQ)
387 ) {
388 caps_free_msg(msg);
389 caps_drop(rcaps);
390 } else {
391 caps_drop(rcaps);
392 caps_hold(caps);
393 caps_put_msg(caps, msg, CAPMS_DISPOSE);
394 }
395 } else {
396 /*
397 * auto-reply to the originator.
398 */
399 caps_put_msg(rcaps, msg, CAPMS_REPLY);
400 }
401 break;
402 case CAPMS_REPLY:
403 case CAPMS_REPLY_RETRY:
404 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
405 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
406 caps_free_msg(msg); /* degenerate disposal case */
407 caps_drop(rcaps);
408 } else {
409 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
410 }
411 break;
412 case CAPMS_DISPOSE:
413 caps_free_msg(msg);
414 break;
415 }
416 }
417 }
418 while ((msg = TAILQ_FIRST(&tmpuserq)) != NULL) {
419 TAILQ_REMOVE(&tmpuserq, msg, km_node);
420 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
421 msg->km_flags |= CAPKMF_ONUSERQ;
422 }
423 while ((msg = TAILQ_FIRST(&tmppendq)) != NULL) {
424 TAILQ_REMOVE(&tmppendq, msg, km_node);
425 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
426 msg->km_flags |= CAPKMF_ONPENDQ;
427 }
428 }
429 if ((flags & CAPKF_HLIST) && (caps->ci_flags & CAPKF_HLIST)) {
430 for (scan = caps_hash(caps->ci_name, caps->ci_namelen);
431 *scan != caps;
432 scan = &(*scan)->ci_hnext
433 ) {
434 KKASSERT(*scan != NULL);
435 }
436 *scan = caps->ci_hnext;
437 caps->ci_hnext = (void *)-1;
438 caps->ci_flags &= ~CAPKF_HLIST;
439 }
440 if ((flags & CAPKF_TDLIST) && (caps->ci_flags & CAPKF_TDLIST)) {
441 while (caps->ci_mrefs) {
442 caps->ci_flags |= CAPKF_MWAIT;
443 tsleep(caps, 0, "cexit", 0);
444 }
445 for (scan = &caps->ci_td->td_caps;
446 *scan != caps;
447 scan = &(*scan)->ci_tdnext
448 ) {
449 KKASSERT(*scan != NULL);
450 }
451 *scan = caps->ci_tdnext;
452 caps->ci_flags &= ~CAPKF_TDLIST;
453 caps->ci_tdnext = (void *)-1;
454 caps->ci_td = NULL;
455 caps_drop(caps);
456 }
457 if ((flags & CAPKF_RCAPS) && (caps->ci_flags & CAPKF_RCAPS)) {
458 caps_kinfo_t ctmp;
459
460 caps->ci_flags &= ~CAPKF_RCAPS;
461 if ((ctmp = caps->ci_rcaps)) {
462 caps->ci_rcaps = NULL;
463 caps_term(ctmp, CAPKF_FLUSH, caps);
464 caps_drop(ctmp);
465 }
466 }
467}
468
469static void
470caps_free(caps_kinfo_t caps)
471{
472 KKASSERT(TAILQ_EMPTY(&caps->ci_msgpendq));
473 KKASSERT(TAILQ_EMPTY(&caps->ci_msguserq));
474 KKASSERT((caps->ci_flags & (CAPKF_HLIST|CAPKF_TDLIST)) == 0);
475 free(caps, M_CAPS);
476}
477
478/************************************************************************
479 * PROCESS SUPPORT FUNCTIONS *
480 ************************************************************************/
481
482void
483caps_fork(struct proc *p1, struct proc *p2)
484{
485 /* create dummy caps entries that fail? Or dup client entries? XXX */
486}
487
488void
489caps_exit(struct thread *td)
490{
491 caps_kinfo_t caps;
492
493 while ((caps = td->td_caps) != NULL) {
494 caps_hold(caps);
495 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
496 caps_drop(caps);
497 }
498}
499
500/************************************************************************
501 * SYSTEM CALLS *
502 ************************************************************************/
503
504/*
505 * caps_sys_service(name, uid, gid, upcid, flags);
506 *
507 * Create an IPC service using the specified name, uid, gid, and flags.
508 * Either uid or gid can be -1, but not both. The port identifier is
509 * returned.
510 *
511 * upcid can either be an upcall or a kqueue identifier (XXX)
512 */
513int
514caps_sys_service(struct caps_sys_service_args *uap)
515{
516 struct ucred *cred = curproc->p_ucred;
517 char name[CAPS_MAXNAMELEN];
518 caps_kinfo_t caps;
519 int len;
520 int error;
521
522 if (caps_enabled == 0)
523 return(EOPNOTSUPP);
524 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
525 return(error);
526 if (--len <= 0)
527 return(EINVAL);
528
529 caps = kern_caps_sys_service(name, uap->uid, uap->gid, cred,
530 uap->flags & CAPF_UFLAGS, &error);
531 if (caps)
532 uap->sysmsg_result = caps->ci_id;
533 return(error);
534}
535
536/*
537 * caps_sys_client(name, uid, gid, upcid, flags);
538 *
539 * Create an IPC client connected to the specified service. Either uid or gid
540 * may be -1, indicating a wildcard, but not both. The port identifier is
541 * returned.
542 *
543 * upcid can either be an upcall or a kqueue identifier (XXX)
544 */
545int
546caps_sys_client(struct caps_sys_client_args *uap)
547{
548 struct ucred *cred = curproc->p_ucred;
549 char name[CAPS_MAXNAMELEN];
550 caps_kinfo_t caps;
551 int len;
552 int error;
553
554 if (caps_enabled == 0)
555 return(EOPNOTSUPP);
556 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
557 return(error);
558 if (--len <= 0)
559 return(EINVAL);
560
561 caps = kern_caps_sys_client(name, uap->uid, uap->gid, cred,
562 uap->flags & CAPF_UFLAGS, &error);
563 if (caps)
564 uap->sysmsg_result = caps->ci_id;
565 return(error);
566}
567
568int
569caps_sys_close(struct caps_sys_close_args *uap)
570{
571 caps_kinfo_t caps;
572
573 if ((caps = caps_find_id(uap->portid)) == NULL)
574 return(EINVAL);
575 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
576 caps_drop(caps);
577 return(0);
578}
579
580/*
581 * caps_sys_put(portid, msg, msgsize)
582 *
583 * Send an opaque message of the specified size to the specified port. This
584 * function may only be used with a client port. The message id is returned.
585 */
586int
587caps_sys_put(struct caps_sys_put_args *uap)
588{
589 caps_kinfo_t caps;
590 caps_kmsg_t msg;
591 struct proc *p = curproc;
592
593 if (uap->msgsize < 0)
594 return(EINVAL);
595 if ((caps = caps_find_id(uap->portid)) == NULL)
596 return(EINVAL);
597 if (caps->ci_rcaps == NULL) {
598 caps_drop(caps);
599 return(EINVAL);
600 }
601
602 /*
603 * If this client has queued a large number of messages return
604 * ENOBUFS. The client must process some replies before it can
605 * send new messages. The server can also throttle a client by
606 * holding its replies. XXX allow a server to refuse messages from
607 * a client.
608 */
609 if (caps->ci_cmsgcount > CAPS_MAXINPROG) {
610 caps_drop(caps);
611 return(ENOBUFS);
612 }
613 msg = caps_alloc_msg(caps);
614 uap->sysmsg_offset = msg->km_msgid.c_id;
615
616 /*
617 * If the remote end is closed reply the message immediately, otherwise
618 * send it to the remote end. Disposal XXX
619 *
620 * Note: since this is a new message, caps_load_ccr() returns a remote
621 * caps of NULL.
622 */
623 if (caps->ci_rcaps->ci_flags & CAPKF_CLOSED) {
624 caps_load_ccr(caps, msg, p, NULL, 0); /* returns NULL */
625 caps_hold(caps);
626 caps_put_msg(caps, msg, CAPMS_REPLY); /* drops caps */
627 } else {
628 caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize); /* returns NULL */
629 caps_hold(caps->ci_rcaps); /* need ref */
630 ++caps->ci_cmsgcount;
631 caps_put_msg(caps->ci_rcaps, msg, CAPMS_REQUEST); /* drops rcaps */
632 }
633 caps_drop(caps);
634 return(0);
635}
636
637/*
638 * caps_sys_reply(portid, msg, msgsize, msgid)
639 *
640 * Reply to the message referenced by the specified msgid, supplying opaque
641 * data back to the originator.
642 */
643int
644caps_sys_reply(struct caps_sys_reply_args *uap)
645{
646 caps_kinfo_t caps;
647 caps_kinfo_t rcaps;
648 caps_kmsg_t msg;
649 struct proc *p;
650
651 if (uap->msgsize < 0)
652 return(EINVAL);
653 if ((caps = caps_find_id(uap->portid)) == NULL)
654 return(EINVAL);
655
656 /*
657 * Can't find message to reply to
658 */
659 if ((msg = caps_find_msg(caps, uap->msgcid)) == NULL) {
660 caps_drop(caps);
661 return(EINVAL);
662 }
663
664 /*
665 * Trying to reply to a non-replyable message
666 */
667 if ((msg->km_flags & CAPKMF_ONUSERQ) == 0) {
668 caps_drop(caps);
669 return(EINVAL);
670 }
671
672 /*
673 * If the remote end is closed requeue to ourselves for disposal.
674 * Otherwise send the reply to the other end (the other end will
675 * return a passive DISPOSE to us when it has eaten the data)
676 */
677 caps_dequeue_msg(caps, msg);
678 p = curproc;
679 if (msg->km_mcaps->ci_flags & CAPKF_CLOSED) {
680 caps_drop(caps_load_ccr(caps, msg, p, NULL, 0));
681 caps_hold(caps);
682 caps_put_msg(caps, msg, CAPMS_DISPOSE); /* drops caps */
683 } else {
684 rcaps = caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
685 caps_put_msg(rcaps, msg, CAPMS_REPLY);
686 }
687 caps_drop(caps);
688 return(0);
689}
690
691/*
692 * caps_sys_get(portid, msg, maxsize, msgid, ccr)
693 *
694 * Retrieve the next ready message on the port, store its message id in
695 * uap->msgid and return the length of the message. If the message is too
696 * large to fit the message id, length, and creds are still returned, but
697 * the message is not dequeued (the caller is expected to call again with
698 * a larger buffer or to reply the messageid if it does not want to handle
699 * the message).
700 *
701 * EWOULDBLOCK is returned if no messages are pending. Note that 0-length
702 * messages are perfectly acceptable so 0 can be legitimately returned.
703 */
704int
705caps_sys_get(struct caps_sys_get_args *uap)
706{
707 caps_kinfo_t caps;
708 caps_kmsg_t msg;
709
710 if (uap->maxsize < 0)
711 return(EINVAL);
712 if ((caps = caps_find_id(uap->portid)) == NULL)
713 return(EINVAL);
714 if ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
715 caps_drop(caps);
716 return(EWOULDBLOCK);
717 }
718 return(caps_process_msg(caps, msg, uap));
719}
720
721/*
722 * caps_sys_wait(portid, msg, maxsize, msgid, ccr)
723 *
724 * Retrieve the next ready message on the port, store its message id in
725 * uap->msgid and return the length of the message. If the message is too
726 * large to fit the message id, length, and creds are still returned, but
727 * the message is not dequeued (the caller is expected to call again with
728 * a larger buffer or to reply the messageid if it does not want to handle
729 * the message).
730 *
731 * This function blocks until interrupted or a message is received.
732 * Note that 0-length messages are perfectly acceptable so 0 can be
733 * legitimately returned.
734 */
735int
736caps_sys_wait(struct caps_sys_wait_args *uap)
737{
738 caps_kinfo_t caps;
739 caps_kmsg_t msg;
740 int error;
741
742 if (uap->maxsize < 0)
743 return(EINVAL);
744 if ((caps = caps_find_id(uap->portid)) == NULL)
745 return(EINVAL);
746 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
747 if ((error = tsleep(caps, PCATCH, "caps", 0)) != 0) {
748 caps_drop(caps);
749 return(error);
750 }
751 }
752 return(caps_process_msg(caps, msg, (struct caps_sys_get_args *)uap));
753}
754
755static int
756caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap)
757{
758 int error = 0;
759 int msgsize;
760 caps_kinfo_t rcaps;
761
762 msg->km_flags |= CAPKMF_PEEKED;
763 msgsize = msg->km_umsg_size;
764 if (msgsize <= uap->maxsize)
765 caps_dequeue_msg(caps, msg);
766
767 if (msg->km_umsg_size != 0) {
768 struct proc *rp = msg->km_mcaps->ci_td->td_proc;
769 KKASSERT(rp != NULL);
770 error = vmspace_copy(rp->p_vmspace, (vm_offset_t)msg->km_umsg,
771 curproc->p_vmspace, (vm_offset_t)uap->msg,
772 min(msgsize, uap->maxsize), uap->maxsize);
773 if (error) {
774 printf("vmspace_copy: error %d from proc %d\n", error, rp->p_pid);
775 if (msgsize > uap->maxsize)
776 caps_dequeue_msg(caps, msg);
777 msgsize = 0;
778 error = 0;
779 }
780 }
781
782 if (uap->msgid)
783 error = copyout(&msg->km_msgid, uap->msgid, sizeof(msg->km_msgid));
784 if (uap->ccr)
785 error = copyout(&msg->km_ccr, uap->ccr, sizeof(msg->km_ccr));
786 if (error == 0)
787 uap->sysmsg_result = msgsize;
788
789 /*
790 * If the message was dequeued we must deal with it.
791 */
792 if (msgsize <= uap->maxsize) {
793 switch(msg->km_state) {
794 case CAPMS_REQUEST:
795 case CAPMS_REQUEST_RETRY:
796 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
797 msg->km_flags |= CAPKMF_ONUSERQ;
798 break;
799 case CAPMS_REPLY:
800 case CAPMS_REPLY_RETRY:
801 --caps->ci_cmsgcount;
802 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
803 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
804 /* degenerate disposal case */
805 caps_free_msg(msg);
806 caps_drop(rcaps);
807 } else {
808 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
809 }
810 break;
811 case CAPMS_DISPOSE:
812 caps_free_msg(msg);
813 break;
814 }
815 }
816 caps_drop(caps);
817 return(error);
818}
819
820/*
821 * caps_sys_abort(portid, msgcid, flags)
822 *
823 * Abort a previously sent message. You must still wait for the message
824 * to be returned after sending the abort request. This function will
825 * return the appropriate CAPS_ABORT_* code depending on what it had
826 * to do.
827 */
828int
829caps_sys_abort(struct caps_sys_abort_args *uap)
830{
831 uap->sysmsg_result = CAPS_ABORT_NOTIMPL;
832 return(0);
833}
834
835/*
836 * KERNEL SYSCALL SEPARATION SUPPORT FUNCTIONS
837 */
838
839static
840caps_kinfo_t
841kern_caps_sys_service(const char *name, uid_t uid, gid_t gid,
842 struct ucred *cred, int flags, int *error)
843{
844 caps_kinfo_t caps;
845 int len;
846
847 len = strlen(name);
848
849 /*
850 * Make sure we can use the uid and gid
851 */
852 if (cred) {
853 if (cred->cr_uid != 0 && uid != (uid_t)-1 && cred->cr_uid != uid) {
854 *error = EPERM;
855 return(NULL);
856 }
857 if (cred->cr_uid != 0 && gid != (gid_t)-1 && !groupmember(gid, cred)) {
858 *error = EPERM;
859 return(NULL);
860 }
861 }
862
863 /*
864 * Handle CAPF_EXCL
865 */
866 if (flags & CAPF_EXCL) {
867 if ((caps = caps_find(name, strlen(name), uid, gid)) != NULL) {
868 caps_drop(caps);
869 *error = EEXIST;
870 return(NULL);
871 }
872 }
873
874 /*
875 * Create the service
876 */
877 caps = caps_alloc(name, len, uid, gid, flags & CAPF_UFLAGS, CAPT_SERVICE);
878 return(caps);
879}
880
881static
882caps_kinfo_t
883kern_caps_sys_client(const char *name, uid_t uid, gid_t gid,
884 struct ucred *cred, int flags, int *error)
885{
886 caps_kinfo_t caps, rcaps;
887 int len;
888
889 len = strlen(name);
890
891 /*
892 * Locate the CAPS service (rcaps ref is for caps->ci_rcaps)
893 */
894 if ((rcaps = caps_find(name, len, uid, gid)) == NULL) {
895 *error = ENOENT;
896 return(NULL);
897 }
898
899 /*
900 * Check permissions
901 */
902 if (cred) {
903 *error = EACCES;
904 if ((flags & CAPF_USER) && (rcaps->ci_flags & CAPF_USER)) {
905 if (rcaps->ci_uid != (uid_t)-1 && rcaps->ci_uid == cred->cr_uid)
906 *error = 0;
907 }
908 if ((flags & CAPF_GROUP) && (rcaps->ci_flags & CAPF_GROUP)) {
909 if (rcaps->ci_gid != (gid_t)-1 && groupmember(rcaps->ci_gid, cred))
910 *error = 0;
911 }
912 if ((flags & CAPF_WORLD) && (rcaps->ci_flags & CAPF_WORLD)) {
913 *error = 0;
914 }
915 if (*error) {
916 caps_drop(rcaps);
917 return(NULL);
918 }
919 } else {
920 *error = 0;
921 }
922
923 /*
924 * Allocate the client side and connect to the server
925 */
926 caps = caps_alloc(name, len, uid, gid, flags & CAPF_UFLAGS, CAPT_CLIENT);
927 caps->ci_rcaps = rcaps;
928 caps->ci_flags |= CAPKF_RCAPS;
929 return(caps);
930}
931