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