Hook XIO up to the kernel build.
[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 *
e30f9e2c 26 * $DragonFly: src/sys/kern/lwkt_caps.c,v 1.2 2004/03/06 22:14:09 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
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
e30f9e2c
MD
327/*
328 * Validate the service name
329 */
330static int
331caps_name_check(const char *name, int len)
332{
333 int i;
334 char c;
335
336 for (i = len - 1; i >= 0; --i) {
337 c = name[i];
338 if (c >= '0' && c <= '9')
339 continue;
340 if (c >= 'a' && c <= 'z')
341 continue;
342 if (c >= 'A' && c <= 'Z')
343 continue;
344 if (c == '_' || c == '.')
345 continue;
346 return(EINVAL);
347 }
348 return(0);
349}
350
f6bf3af1
MD
351/*
352 * caps_term()
353 *
354 * Terminate portions of a caps info structure. This is used to close
355 * an end-point or to flush particular messages on an end-point.
356 *
357 * This function should not be called with CAPKF_TDLIST unless the caller
358 * has an additional hold on the caps structure.
359 */
360static void
361caps_term(caps_kinfo_t caps, int flags, caps_kinfo_t cflush)
362{
363 struct caps_kinfo **scan;
364 caps_kmsg_t msg;
365
366 if (flags & CAPKF_TDLIST)
367 caps->ci_flags |= CAPKF_CLOSED;
368
369 if (flags & CAPKF_FLUSH) {
370 int mflags;
371 struct caps_kmsg_queue tmpuserq;
372 struct caps_kmsg_queue tmppendq;
373 caps_kinfo_t rcaps;
374
375 TAILQ_INIT(&tmpuserq);
376 TAILQ_INIT(&tmppendq);
377
378 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) != NULL ||
379 (msg = TAILQ_FIRST(&caps->ci_msguserq)) != NULL
380 ) {
381 mflags = msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ);
382 caps_dequeue_msg(caps, msg);
383
384 if (cflush && msg->km_mcaps != cflush) {
385 if (mflags & CAPKMF_ONUSERQ)
386 TAILQ_INSERT_TAIL(&tmpuserq, msg, km_node);
387 else
388 TAILQ_INSERT_TAIL(&tmppendq, msg, km_node);
389 } else {
390 /*
391 * Dispose of the message. If the received message is a
392 * request we must reply it. If the received message is
393 * a reply we must return it for disposal. If the
394 * received message is a disposal request we simply free it.
395 */
396 switch(msg->km_state) {
397 case CAPMS_REQUEST:
398 case CAPMS_REQUEST_RETRY:
399 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
400 if (rcaps->ci_flags & CAPKF_CLOSED) {
401 /*
402 * can't reply, if we never read the message (its on
403 * the pending queue), or if we are closed ourselves,
404 * we can just free the message. Otherwise we have
405 * to send ourselves a disposal request (multi-threaded
406 * services have to deal with disposal requests for
407 * messages that might be in progress).
408 */
409 if ((caps->ci_flags & CAPKF_CLOSED) ||
410 (mflags & CAPKMF_ONPENDQ)
411 ) {
412 caps_free_msg(msg);
413 caps_drop(rcaps);
414 } else {
415 caps_drop(rcaps);
416 caps_hold(caps);
417 caps_put_msg(caps, msg, CAPMS_DISPOSE);
418 }
419 } else {
420 /*
421 * auto-reply to the originator.
422 */
423 caps_put_msg(rcaps, msg, CAPMS_REPLY);
424 }
425 break;
426 case CAPMS_REPLY:
427 case CAPMS_REPLY_RETRY:
428 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
429 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
430 caps_free_msg(msg); /* degenerate disposal case */
431 caps_drop(rcaps);
432 } else {
433 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
434 }
435 break;
436 case CAPMS_DISPOSE:
437 caps_free_msg(msg);
438 break;
439 }
440 }
441 }
442 while ((msg = TAILQ_FIRST(&tmpuserq)) != NULL) {
443 TAILQ_REMOVE(&tmpuserq, msg, km_node);
444 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
445 msg->km_flags |= CAPKMF_ONUSERQ;
446 }
447 while ((msg = TAILQ_FIRST(&tmppendq)) != NULL) {
448 TAILQ_REMOVE(&tmppendq, msg, km_node);
449 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
450 msg->km_flags |= CAPKMF_ONPENDQ;
451 }
452 }
453 if ((flags & CAPKF_HLIST) && (caps->ci_flags & CAPKF_HLIST)) {
454 for (scan = caps_hash(caps->ci_name, caps->ci_namelen);
455 *scan != caps;
456 scan = &(*scan)->ci_hnext
457 ) {
458 KKASSERT(*scan != NULL);
459 }
460 *scan = caps->ci_hnext;
461 caps->ci_hnext = (void *)-1;
462 caps->ci_flags &= ~CAPKF_HLIST;
463 }
464 if ((flags & CAPKF_TDLIST) && (caps->ci_flags & CAPKF_TDLIST)) {
465 while (caps->ci_mrefs) {
466 caps->ci_flags |= CAPKF_MWAIT;
467 tsleep(caps, 0, "cexit", 0);
468 }
469 for (scan = &caps->ci_td->td_caps;
470 *scan != caps;
471 scan = &(*scan)->ci_tdnext
472 ) {
473 KKASSERT(*scan != NULL);
474 }
475 *scan = caps->ci_tdnext;
476 caps->ci_flags &= ~CAPKF_TDLIST;
477 caps->ci_tdnext = (void *)-1;
478 caps->ci_td = NULL;
479 caps_drop(caps);
480 }
481 if ((flags & CAPKF_RCAPS) && (caps->ci_flags & CAPKF_RCAPS)) {
482 caps_kinfo_t ctmp;
483
484 caps->ci_flags &= ~CAPKF_RCAPS;
485 if ((ctmp = caps->ci_rcaps)) {
486 caps->ci_rcaps = NULL;
487 caps_term(ctmp, CAPKF_FLUSH, caps);
488 caps_drop(ctmp);
489 }
490 }
491}
492
493static void
494caps_free(caps_kinfo_t caps)
495{
496 KKASSERT(TAILQ_EMPTY(&caps->ci_msgpendq));
497 KKASSERT(TAILQ_EMPTY(&caps->ci_msguserq));
498 KKASSERT((caps->ci_flags & (CAPKF_HLIST|CAPKF_TDLIST)) == 0);
499 free(caps, M_CAPS);
500}
501
502/************************************************************************
503 * PROCESS SUPPORT FUNCTIONS *
504 ************************************************************************/
505
e30f9e2c
MD
506/*
507 * Create dummy entries in p2 so we can return the appropriate
508 * error code. Robust userland code will check the error for a
509 * forked condition and reforge the connection.
510 */
f6bf3af1 511void
e30f9e2c 512caps_fork(struct proc *p1, struct proc *p2, int flags)
f6bf3af1 513{
e30f9e2c
MD
514 caps_kinfo_t caps1;
515 caps_kinfo_t caps2;
516 thread_t td1;
517 thread_t td2;
518
519 td1 = p1->p_thread;
520 td2 = p2->p_thread;
521
522 /*
523 * Create dummy entries with the same id's as the originals. Note
524 * that service entries are not re-added to the hash table. The
525 * dummy entries return an ENOTCONN error allowing userland code to
526 * detect that a fork occured. Userland must reconnect to the service.
527 */
528 for (caps1 = td1->td_caps; caps1; caps1 = caps1->ci_tdnext) {
529 if (caps1->ci_flags & CAPF_NOFORK)
530 continue;
531 caps2 = caps_alloc(td2,
532 caps1->ci_name, caps1->ci_namelen,
533 caps1->ci_uid, caps1->ci_gid,
534 caps1->ci_flags & CAPF_UFLAGS, CAPT_FORKED);
535 caps2->ci_id = caps1->ci_id;
536 }
537
538 /*
539 * Reverse the list order to maintain highest-id-first
540 */
541 caps2 = td2->td_caps;
542 td2->td_caps = NULL;
543 while (caps2) {
544 caps1 = caps2->ci_tdnext;
545 caps2->ci_tdnext = td2->td_caps;
546 td2->td_caps = caps2;
547 caps2 = caps1;
548 }
f6bf3af1
MD
549}
550
551void
552caps_exit(struct thread *td)
553{
554 caps_kinfo_t caps;
555
556 while ((caps = td->td_caps) != NULL) {
557 caps_hold(caps);
558 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
559 caps_drop(caps);
560 }
561}
562
563/************************************************************************
564 * SYSTEM CALLS *
565 ************************************************************************/
566
567/*
568 * caps_sys_service(name, uid, gid, upcid, flags);
569 *
570 * Create an IPC service using the specified name, uid, gid, and flags.
571 * Either uid or gid can be -1, but not both. The port identifier is
572 * returned.
573 *
574 * upcid can either be an upcall or a kqueue identifier (XXX)
575 */
576int
577caps_sys_service(struct caps_sys_service_args *uap)
578{
579 struct ucred *cred = curproc->p_ucred;
580 char name[CAPS_MAXNAMELEN];
581 caps_kinfo_t caps;
582 int len;
583 int error;
584
585 if (caps_enabled == 0)
586 return(EOPNOTSUPP);
587 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
588 return(error);
589 if (--len <= 0)
590 return(EINVAL);
e30f9e2c
MD
591 if ((error = caps_name_check(name, len)) != 0)
592 return(error);
f6bf3af1
MD
593
594 caps = kern_caps_sys_service(name, uap->uid, uap->gid, cred,
595 uap->flags & CAPF_UFLAGS, &error);
596 if (caps)
597 uap->sysmsg_result = caps->ci_id;
598 return(error);
599}
600
601/*
602 * caps_sys_client(name, uid, gid, upcid, flags);
603 *
604 * Create an IPC client connected to the specified service. Either uid or gid
605 * may be -1, indicating a wildcard, but not both. The port identifier is
606 * returned.
607 *
608 * upcid can either be an upcall or a kqueue identifier (XXX)
609 */
610int
611caps_sys_client(struct caps_sys_client_args *uap)
612{
613 struct ucred *cred = curproc->p_ucred;
614 char name[CAPS_MAXNAMELEN];
615 caps_kinfo_t caps;
616 int len;
617 int error;
618
619 if (caps_enabled == 0)
620 return(EOPNOTSUPP);
621 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
622 return(error);
623 if (--len <= 0)
624 return(EINVAL);
e30f9e2c
MD
625 if ((error = caps_name_check(name, len)) != 0)
626 return(error);
f6bf3af1
MD
627
628 caps = kern_caps_sys_client(name, uap->uid, uap->gid, cred,
629 uap->flags & CAPF_UFLAGS, &error);
630 if (caps)
631 uap->sysmsg_result = caps->ci_id;
632 return(error);
633}
634
635int
636caps_sys_close(struct caps_sys_close_args *uap)
637{
638 caps_kinfo_t caps;
639
e30f9e2c 640 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1
MD
641 return(EINVAL);
642 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
643 caps_drop(caps);
644 return(0);
645}
646
e30f9e2c
MD
647int
648caps_sys_setgen(struct caps_sys_setgen_args *uap)
649{
650 caps_kinfo_t caps;
651
652 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
653 return(EINVAL);
654 if (caps->ci_type == CAPT_FORKED)
655 return(ENOTCONN);
656 caps->ci_gen = uap->gen;
657 return(0);
658}
659
660int
661caps_sys_getgen(struct caps_sys_getgen_args *uap)
662{
663 caps_kinfo_t caps;
664
665 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
666 return(EINVAL);
667 if (caps->ci_type == CAPT_FORKED)
668 return(ENOTCONN);
669 if (caps->ci_rcaps == NULL)
670 return(EINVAL);
671 uap->sysmsg_result64 = caps->ci_rcaps->ci_gen;
672 return(0);
673}
674
f6bf3af1
MD
675/*
676 * caps_sys_put(portid, msg, msgsize)
677 *
678 * Send an opaque message of the specified size to the specified port. This
679 * function may only be used with a client port. The message id is returned.
680 */
681int
682caps_sys_put(struct caps_sys_put_args *uap)
683{
684 caps_kinfo_t caps;
685 caps_kmsg_t msg;
686 struct proc *p = curproc;
e30f9e2c 687 int error;
f6bf3af1
MD
688
689 if (uap->msgsize < 0)
690 return(EINVAL);
e30f9e2c 691 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 692 return(EINVAL);
e30f9e2c
MD
693 if (caps->ci_type == CAPT_FORKED)
694 return(ENOTCONN);
f6bf3af1
MD
695 if (caps->ci_rcaps == NULL) {
696 caps_drop(caps);
697 return(EINVAL);
698 }
699
700 /*
701 * If this client has queued a large number of messages return
702 * ENOBUFS. The client must process some replies before it can
703 * send new messages. The server can also throttle a client by
704 * holding its replies. XXX allow a server to refuse messages from
705 * a client.
706 */
707 if (caps->ci_cmsgcount > CAPS_MAXINPROG) {
708 caps_drop(caps);
709 return(ENOBUFS);
710 }
711 msg = caps_alloc_msg(caps);
712 uap->sysmsg_offset = msg->km_msgid.c_id;
713
714 /*
e30f9e2c
MD
715 * If the remote end is closed return ENOTCONN immediately, otherwise
716 * send it to the remote end.
f6bf3af1
MD
717 *
718 * Note: since this is a new message, caps_load_ccr() returns a remote
719 * caps of NULL.
720 */
e30f9e2c 721 error = 0;
f6bf3af1 722 if (caps->ci_rcaps->ci_flags & CAPKF_CLOSED) {
e30f9e2c
MD
723 error = ENOTCONN;
724 caps_free_msg(msg);
725#if 0
f6bf3af1
MD
726 caps_load_ccr(caps, msg, p, NULL, 0); /* returns NULL */
727 caps_hold(caps);
728 caps_put_msg(caps, msg, CAPMS_REPLY); /* drops caps */
e30f9e2c 729#endif
f6bf3af1
MD
730 } else {
731 caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize); /* returns NULL */
732 caps_hold(caps->ci_rcaps); /* need ref */
733 ++caps->ci_cmsgcount;
734 caps_put_msg(caps->ci_rcaps, msg, CAPMS_REQUEST); /* drops rcaps */
735 }
736 caps_drop(caps);
e30f9e2c 737 return(error);
f6bf3af1
MD
738}
739
740/*
741 * caps_sys_reply(portid, msg, msgsize, msgid)
742 *
743 * Reply to the message referenced by the specified msgid, supplying opaque
744 * data back to the originator.
745 */
746int
747caps_sys_reply(struct caps_sys_reply_args *uap)
748{
749 caps_kinfo_t caps;
750 caps_kinfo_t rcaps;
751 caps_kmsg_t msg;
752 struct proc *p;
753
754 if (uap->msgsize < 0)
755 return(EINVAL);
e30f9e2c 756 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 757 return(EINVAL);
e30f9e2c
MD
758 if (caps->ci_type == CAPT_FORKED)
759 return(ENOTCONN);
f6bf3af1
MD
760
761 /*
762 * Can't find message to reply to
763 */
764 if ((msg = caps_find_msg(caps, uap->msgcid)) == NULL) {
765 caps_drop(caps);
766 return(EINVAL);
767 }
768
769 /*
770 * Trying to reply to a non-replyable message
771 */
772 if ((msg->km_flags & CAPKMF_ONUSERQ) == 0) {
773 caps_drop(caps);
774 return(EINVAL);
775 }
776
777 /*
778 * If the remote end is closed requeue to ourselves for disposal.
779 * Otherwise send the reply to the other end (the other end will
780 * return a passive DISPOSE to us when it has eaten the data)
781 */
782 caps_dequeue_msg(caps, msg);
783 p = curproc;
784 if (msg->km_mcaps->ci_flags & CAPKF_CLOSED) {
785 caps_drop(caps_load_ccr(caps, msg, p, NULL, 0));
786 caps_hold(caps);
787 caps_put_msg(caps, msg, CAPMS_DISPOSE); /* drops caps */
788 } else {
789 rcaps = caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
790 caps_put_msg(rcaps, msg, CAPMS_REPLY);
791 }
792 caps_drop(caps);
793 return(0);
794}
795
796/*
797 * caps_sys_get(portid, msg, maxsize, msgid, ccr)
798 *
799 * Retrieve the next ready message on the port, store its message id in
800 * uap->msgid and return the length of the message. If the message is too
801 * large to fit the message id, length, and creds are still returned, but
802 * the message is not dequeued (the caller is expected to call again with
803 * a larger buffer or to reply the messageid if it does not want to handle
804 * the message).
805 *
806 * EWOULDBLOCK is returned if no messages are pending. Note that 0-length
807 * messages are perfectly acceptable so 0 can be legitimately returned.
808 */
809int
810caps_sys_get(struct caps_sys_get_args *uap)
811{
812 caps_kinfo_t caps;
813 caps_kmsg_t msg;
814
815 if (uap->maxsize < 0)
816 return(EINVAL);
e30f9e2c 817 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 818 return(EINVAL);
e30f9e2c
MD
819 if (caps->ci_type == CAPT_FORKED)
820 return(ENOTCONN);
f6bf3af1
MD
821 if ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
822 caps_drop(caps);
823 return(EWOULDBLOCK);
824 }
825 return(caps_process_msg(caps, msg, uap));
826}
827
828/*
829 * caps_sys_wait(portid, msg, maxsize, msgid, ccr)
830 *
831 * Retrieve the next ready message on the port, store its message id in
832 * uap->msgid and return the length of the message. If the message is too
833 * large to fit the message id, length, and creds are still returned, but
834 * the message is not dequeued (the caller is expected to call again with
835 * a larger buffer or to reply the messageid if it does not want to handle
836 * the message).
837 *
838 * This function blocks until interrupted or a message is received.
839 * Note that 0-length messages are perfectly acceptable so 0 can be
840 * legitimately returned.
841 */
842int
843caps_sys_wait(struct caps_sys_wait_args *uap)
844{
845 caps_kinfo_t caps;
846 caps_kmsg_t msg;
847 int error;
848
849 if (uap->maxsize < 0)
850 return(EINVAL);
e30f9e2c 851 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
f6bf3af1 852 return(EINVAL);
e30f9e2c
MD
853 if (caps->ci_type == CAPT_FORKED)
854 return(ENOTCONN);
f6bf3af1
MD
855 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
856 if ((error = tsleep(caps, PCATCH, "caps", 0)) != 0) {
857 caps_drop(caps);
858 return(error);
859 }
860 }
861 return(caps_process_msg(caps, msg, (struct caps_sys_get_args *)uap));
862}
863
864static int
865caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap)
866{
867 int error = 0;
868 int msgsize;
869 caps_kinfo_t rcaps;
870
871 msg->km_flags |= CAPKMF_PEEKED;
872 msgsize = msg->km_umsg_size;
873 if (msgsize <= uap->maxsize)
874 caps_dequeue_msg(caps, msg);
875
876 if (msg->km_umsg_size != 0) {
877 struct proc *rp = msg->km_mcaps->ci_td->td_proc;
878 KKASSERT(rp != NULL);
879 error = vmspace_copy(rp->p_vmspace, (vm_offset_t)msg->km_umsg,
880 curproc->p_vmspace, (vm_offset_t)uap->msg,
881 min(msgsize, uap->maxsize), uap->maxsize);
882 if (error) {
883 printf("vmspace_copy: error %d from proc %d\n", error, rp->p_pid);
884 if (msgsize > uap->maxsize)
885 caps_dequeue_msg(caps, msg);
886 msgsize = 0;
887 error = 0;
888 }
889 }
890
891 if (uap->msgid)
892 error = copyout(&msg->km_msgid, uap->msgid, sizeof(msg->km_msgid));
893 if (uap->ccr)
894 error = copyout(&msg->km_ccr, uap->ccr, sizeof(msg->km_ccr));
895 if (error == 0)
896 uap->sysmsg_result = msgsize;
897
898 /*
899 * If the message was dequeued we must deal with it.
900 */
901 if (msgsize <= uap->maxsize) {
902 switch(msg->km_state) {
903 case CAPMS_REQUEST:
904 case CAPMS_REQUEST_RETRY:
905 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
906 msg->km_flags |= CAPKMF_ONUSERQ;
907 break;
908 case CAPMS_REPLY:
909 case CAPMS_REPLY_RETRY:
910 --caps->ci_cmsgcount;
911 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
912 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
913 /* degenerate disposal case */
914 caps_free_msg(msg);
915 caps_drop(rcaps);
916 } else {
917 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
918 }
919 break;
920 case CAPMS_DISPOSE:
921 caps_free_msg(msg);
922 break;
923 }
924 }
925 caps_drop(caps);
926 return(error);
927}
928
929/*
930 * caps_sys_abort(portid, msgcid, flags)
931 *
932 * Abort a previously sent message. You must still wait for the message
933 * to be returned after sending the abort request. This function will
934 * return the appropriate CAPS_ABORT_* code depending on what it had
935 * to do.
936 */
937int
938caps_sys_abort(struct caps_sys_abort_args *uap)
939{
940 uap->sysmsg_result = CAPS_ABORT_NOTIMPL;
941 return(0);
942}
943
944/*
945 * KERNEL SYSCALL SEPARATION SUPPORT FUNCTIONS
946 */
947
948static
949caps_kinfo_t
950kern_caps_sys_service(const char *name, uid_t uid, gid_t gid,
951 struct ucred *cred, int flags, int *error)
952{
953 caps_kinfo_t caps;
954 int len;
955
956 len = strlen(name);
957
958 /*
959 * Make sure we can use the uid and gid
960 */
961 if (cred) {
962 if (cred->cr_uid != 0 && uid != (uid_t)-1 && cred->cr_uid != uid) {
963 *error = EPERM;
964 return(NULL);
965 }
966 if (cred->cr_uid != 0 && gid != (gid_t)-1 && !groupmember(gid, cred)) {
967 *error = EPERM;
968 return(NULL);
969 }
970 }
971
972 /*
973 * Handle CAPF_EXCL
974 */
975 if (flags & CAPF_EXCL) {
976 if ((caps = caps_find(name, strlen(name), uid, gid)) != NULL) {
977 caps_drop(caps);
978 *error = EEXIST;
979 return(NULL);
980 }
981 }
982
983 /*
984 * Create the service
985 */
e30f9e2c
MD
986 caps = caps_alloc(curthread, name, len,
987 uid, gid, flags & CAPF_UFLAGS, CAPT_SERVICE);
988 wakeup(&caps_waitsvc);
f6bf3af1
MD
989 return(caps);
990}
991
992static
993caps_kinfo_t
994kern_caps_sys_client(const char *name, uid_t uid, gid_t gid,
995 struct ucred *cred, int flags, int *error)
996{
997 caps_kinfo_t caps, rcaps;
998 int len;
999
1000 len = strlen(name);
1001
1002 /*
1003 * Locate the CAPS service (rcaps ref is for caps->ci_rcaps)
1004 */
e30f9e2c 1005again:
f6bf3af1 1006 if ((rcaps = caps_find(name, len, uid, gid)) == NULL) {
e30f9e2c
MD
1007 if (flags & CAPF_WAITSVC) {
1008 char cbuf[32];
1009 snprintf(cbuf, sizeof(cbuf), "C%s", name);
1010 *error = tsleep(&caps_waitsvc, PCATCH, cbuf, 0);
1011 if (*error == 0)
1012 goto again;
1013 } else {
1014 *error = ENOENT;
1015 }
f6bf3af1
MD
1016 return(NULL);
1017 }
1018
1019 /*
1020 * Check permissions
1021 */
1022 if (cred) {
1023 *error = EACCES;
1024 if ((flags & CAPF_USER) && (rcaps->ci_flags & CAPF_USER)) {
1025 if (rcaps->ci_uid != (uid_t)-1 && rcaps->ci_uid == cred->cr_uid)
1026 *error = 0;
1027 }
1028 if ((flags & CAPF_GROUP) && (rcaps->ci_flags & CAPF_GROUP)) {
1029 if (rcaps->ci_gid != (gid_t)-1 && groupmember(rcaps->ci_gid, cred))
1030 *error = 0;
1031 }
1032 if ((flags & CAPF_WORLD) && (rcaps->ci_flags & CAPF_WORLD)) {
1033 *error = 0;
1034 }
1035 if (*error) {
1036 caps_drop(rcaps);
1037 return(NULL);
1038 }
1039 } else {
1040 *error = 0;
1041 }
1042
1043 /*
1044 * Allocate the client side and connect to the server
1045 */
e30f9e2c
MD
1046 caps = caps_alloc(curthread, name, len,
1047 uid, gid, flags & CAPF_UFLAGS, CAPT_CLIENT);
f6bf3af1
MD
1048 caps->ci_rcaps = rcaps;
1049 caps->ci_flags |= CAPKF_RCAPS;
1050 return(caps);
1051}
1052