Line data Source code
1 : /*
2 : * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "hi_locl.h"
37 :
38 : #if defined(__APPLE__) && defined(HAVE_GCD)
39 :
40 : #include "heim_ipc.h"
41 : #include "heim_ipc_asyncServer.h"
42 :
43 : #include <dispatch/dispatch.h>
44 : #include <mach/mach.h>
45 :
46 : static dispatch_once_t jobqinited = 0;
47 : static dispatch_queue_t jobq = NULL;
48 : static dispatch_queue_t syncq;
49 :
50 : struct mach_ctx {
51 : mach_port_t server;
52 : char *name;
53 : };
54 :
55 : static int
56 : mach_release(void *ctx);
57 :
58 : static int
59 : mach_init(const char *service, void **ctx)
60 : {
61 : struct mach_ctx *ipc;
62 : mach_port_t sport;
63 : int ret;
64 :
65 : dispatch_once(&jobqinited, ^{
66 : jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
67 : syncq = dispatch_queue_create("heim-ipc-syncq", NULL);
68 : });
69 :
70 : ret = bootstrap_look_up(bootstrap_port, service, &sport);
71 : if (ret)
72 : return ret;
73 :
74 : ipc = malloc(sizeof(*ipc));
75 : if (ipc == NULL) {
76 : mach_port_destroy(mach_task_self(), sport);
77 : return ENOMEM;
78 : }
79 :
80 : ipc->server = sport;
81 : ipc->name = strdup(service);
82 : if (ipc->name == NULL) {
83 : mach_release(ipc);
84 : return ENOMEM;
85 : }
86 :
87 : *ctx = ipc;
88 :
89 : return 0;
90 : }
91 :
92 : static int
93 : mach_ipc(void *ctx,
94 : const heim_idata *request, heim_idata *response,
95 : heim_icred *cred)
96 : {
97 : struct mach_ctx *ipc = ctx;
98 : heim_ipc_message_inband_t requestin;
99 : mach_msg_type_number_t requestin_length = 0;
100 : heim_ipc_message_outband_t requestout = NULL;
101 : mach_msg_type_number_t requestout_length = 0;
102 : heim_ipc_message_inband_t replyin;
103 : mach_msg_type_number_t replyin_length;
104 : heim_ipc_message_outband_t replyout;
105 : mach_msg_type_number_t replyout_length;
106 : int ret, errorcode, retries = 0;
107 :
108 : memcpy(requestin, request->data, request->length);
109 : requestin_length = request->length;
110 :
111 : while (retries < 2) {
112 : __block mach_port_t sport;
113 :
114 : dispatch_sync(syncq, ^{ sport = ipc->server; });
115 :
116 : ret = mheim_ipc_call(sport,
117 : requestin, requestin_length,
118 : requestout, requestout_length,
119 : &errorcode,
120 : replyin, &replyin_length,
121 : &replyout, &replyout_length);
122 : if (ret == MACH_SEND_INVALID_DEST) {
123 : mach_port_t nport;
124 : /* race other threads to get a new port */
125 : ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport);
126 : if (ret)
127 : return ret;
128 : dispatch_sync(syncq, ^{
129 : /* check if we lost the race to lookup the port */
130 : if (sport != ipc->server) {
131 : mach_port_deallocate(mach_task_self(), nport);
132 : } else {
133 : mach_port_deallocate(mach_task_self(), ipc->server);
134 : ipc->server = nport;
135 : }
136 : });
137 : retries++;
138 : } else if (ret) {
139 : return ret;
140 : } else
141 : break;
142 : }
143 : if (retries >= 2)
144 : return EINVAL;
145 :
146 : if (errorcode) {
147 : if (replyout_length)
148 : vm_deallocate (mach_task_self (), (vm_address_t) replyout,
149 : replyout_length);
150 : return errorcode;
151 : }
152 :
153 : if (replyout_length) {
154 : response->data = malloc(replyout_length);
155 : if (response->data == NULL) {
156 : vm_deallocate (mach_task_self (), (vm_address_t) replyout,
157 : replyout_length);
158 : return ENOMEM;
159 : }
160 : memcpy(response->data, replyout, replyout_length);
161 : response->length = replyout_length;
162 : vm_deallocate (mach_task_self (), (vm_address_t) replyout,
163 : replyout_length);
164 : } else {
165 : response->data = malloc(replyin_length);
166 : if (response->data == NULL)
167 : return ENOMEM;
168 : memcpy(response->data, replyin, replyin_length);
169 : response->length = replyin_length;
170 : }
171 :
172 : return 0;
173 : }
174 :
175 : struct async_client {
176 : mach_port_t mp;
177 : dispatch_source_t source;
178 : dispatch_queue_t queue;
179 : void (*func)(void *, int, heim_idata *, heim_icred);
180 : void *userctx;
181 : };
182 :
183 : kern_return_t
184 : mheim_ado_acall_reply(mach_port_t server_port,
185 : audit_token_t client_creds,
186 : int returnvalue,
187 : heim_ipc_message_inband_t replyin,
188 : mach_msg_type_number_t replyinCnt,
189 : heim_ipc_message_outband_t replyout,
190 : mach_msg_type_number_t replyoutCnt)
191 : {
192 : struct async_client *c = dispatch_get_context(dispatch_get_current_queue());
193 : heim_idata response;
194 :
195 : if (returnvalue) {
196 : response.data = NULL;
197 : response.length = 0;
198 : } else if (replyoutCnt) {
199 : response.data = replyout;
200 : response.length = replyoutCnt;
201 : } else {
202 : response.data = replyin;
203 : response.length = replyinCnt;
204 : }
205 :
206 : (*c->func)(c->userctx, returnvalue, &response, NULL);
207 :
208 : if (replyoutCnt)
209 : vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt);
210 :
211 : dispatch_source_cancel(c->source);
212 :
213 : return 0;
214 :
215 :
216 : }
217 :
218 :
219 : static int
220 : mach_async(void *ctx, const heim_idata *request, void *userctx,
221 : void (*func)(void *, int, heim_idata *, heim_icred))
222 : {
223 : struct mach_ctx *ipc = ctx;
224 : heim_ipc_message_inband_t requestin;
225 : mach_msg_type_number_t requestin_length = 0;
226 : heim_ipc_message_outband_t requestout = NULL;
227 : mach_msg_type_number_t requestout_length = 0;
228 : int ret, retries = 0;
229 : kern_return_t kr;
230 : struct async_client *c;
231 :
232 : /* first create the service that will catch the reply from the server */
233 : /* XXX these object should be cached and reused */
234 :
235 : c = malloc(sizeof(*c));
236 : if (c == NULL)
237 : return ENOMEM;
238 :
239 : kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp);
240 : if (kr != KERN_SUCCESS)
241 : return EINVAL;
242 :
243 : c->queue = dispatch_queue_create("heim-ipc-async-client", NULL);
244 : c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue);
245 : dispatch_set_context(c->queue, c);
246 :
247 : dispatch_source_set_event_handler(c->source, ^{
248 : dispatch_mig_server(c->source,
249 : sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem),
250 : mheim_aipc_server);
251 : });
252 :
253 : dispatch_source_set_cancel_handler(c->source, ^{
254 : mach_port_mod_refs(mach_task_self(), c->mp,
255 : MACH_PORT_RIGHT_RECEIVE, -1);
256 : dispatch_release(c->queue);
257 : dispatch_release(c->source);
258 : free(c);
259 : });
260 :
261 : c->func = func;
262 : c->userctx = userctx;
263 :
264 : dispatch_resume(c->source);
265 :
266 : /* ok, send the message */
267 :
268 : memcpy(requestin, request->data, request->length);
269 : requestin_length = request->length;
270 :
271 : while (retries < 2) {
272 : __block mach_port_t sport;
273 :
274 : dispatch_sync(syncq, ^{ sport = ipc->server; });
275 :
276 : ret = mheim_ipc_call_request(sport, c->mp,
277 : requestin, requestin_length,
278 : requestout, requestout_length);
279 : if (ret == MACH_SEND_INVALID_DEST) {
280 : ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport);
281 : if (ret) {
282 : dispatch_source_cancel(c->source);
283 : return ret;
284 : }
285 : mach_port_deallocate(mach_task_self(), ipc->server);
286 : ipc->server = sport;
287 : retries++;
288 : } else if (ret) {
289 : dispatch_source_cancel(c->source);
290 : return ret;
291 : } else
292 : break;
293 : }
294 : if (retries >= 2) {
295 : dispatch_source_cancel(c->source);
296 : return EINVAL;
297 : }
298 :
299 : return 0;
300 : }
301 :
302 : static int
303 : mach_release(void *ctx)
304 : {
305 : struct mach_ctx *ipc = ctx;
306 : if (ipc->server != MACH_PORT_NULL)
307 : mach_port_deallocate(mach_task_self(), ipc->server);
308 : free(ipc->name);
309 : free(ipc);
310 : return 0;
311 : }
312 :
313 : #endif
314 :
315 : struct path_ctx {
316 : char *path;
317 : int fd;
318 : };
319 :
320 : static int common_release(void *);
321 :
322 : static int
323 0 : connect_unix(struct path_ctx *s)
324 : {
325 0 : struct sockaddr_un addr;
326 :
327 0 : addr.sun_family = AF_UNIX;
328 0 : strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
329 :
330 0 : s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
331 0 : if (s->fd < 0)
332 0 : return errno;
333 0 : rk_cloexec(s->fd);
334 :
335 0 : if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
336 0 : return errno;
337 :
338 0 : return 0;
339 : }
340 :
341 : static int
342 0 : common_path_init(const char *base,
343 : const char *service,
344 : const char *file,
345 : void **ctx)
346 : {
347 0 : struct path_ctx *s;
348 :
349 0 : s = malloc(sizeof(*s));
350 0 : if (s == NULL)
351 0 : return ENOMEM;
352 0 : s->fd = -1;
353 :
354 0 : if (asprintf(&s->path, "%s/.heim_%s-%s", base, service, file) == -1) {
355 0 : free(s);
356 0 : return ENOMEM;
357 : }
358 :
359 0 : *ctx = s;
360 0 : return 0;
361 : }
362 :
363 : static int
364 0 : unix_socket_init(const char *service,
365 : void **ctx)
366 : {
367 0 : const char *base = secure_getenv("HEIM_IPC_DIR");
368 0 : int ret;
369 :
370 0 : ret = common_path_init(base ? base : _PATH_VARRUN, service, "socket", ctx);
371 0 : if (ret)
372 0 : return ret;
373 0 : ret = connect_unix(*ctx);
374 0 : if (ret)
375 0 : common_release(*ctx);
376 :
377 0 : return ret;
378 : }
379 :
380 : static int
381 0 : unix_socket_ipc(void *ctx,
382 : const heim_idata *req, heim_idata *rep,
383 : heim_icred *cred)
384 : {
385 0 : struct path_ctx *s = ctx;
386 0 : uint32_t len = htonl(req->length);
387 0 : uint32_t rv;
388 0 : int retval;
389 :
390 0 : if (cred)
391 0 : *cred = NULL;
392 :
393 0 : rep->data = NULL;
394 0 : rep->length = 0;
395 :
396 0 : if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
397 0 : return -1;
398 0 : if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
399 0 : return -1;
400 :
401 0 : if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
402 0 : return -1;
403 0 : if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
404 0 : return -1;
405 0 : retval = ntohl(rv);
406 :
407 0 : rep->length = ntohl(len);
408 0 : if (rep->length > 0) {
409 0 : rep->data = malloc(rep->length);
410 0 : if (rep->data == NULL)
411 0 : return -1;
412 0 : if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
413 0 : return -1;
414 : } else
415 0 : rep->data = NULL;
416 :
417 0 : return retval;
418 : }
419 :
420 : int
421 0 : common_release(void *ctx)
422 : {
423 0 : struct path_ctx *s = ctx;
424 0 : if (s->fd >= 0)
425 0 : close(s->fd);
426 0 : free(s->path);
427 0 : free(s);
428 0 : return 0;
429 : }
430 :
431 : #ifdef HAVE_DOOR_CREATE
432 :
433 : #include <door.h>
434 :
435 : #ifdef HAVE_SYS_MMAN_H
436 : #include <sys/mman.h>
437 : #endif
438 :
439 : static int
440 : door_init(const char *service,
441 : void **ctx)
442 : {
443 : const char *base = secure_getenv("HEIM_IPC_DIR");
444 : int ret;
445 : struct path_ctx *d;
446 :
447 : ret = common_path_init(base ? base : _PATH_VARRUN, service, "door", ctx);
448 : if (ret)
449 : return ret;
450 :
451 : d = (struct path_ctx *)*ctx;
452 : d->fd = open(d->path, O_RDWR);
453 : if (d->fd < 0) {
454 : ret = errno;
455 : common_release(*ctx);
456 : return ret;
457 : }
458 :
459 : return 0;
460 : }
461 :
462 : struct door_reply {
463 : int returnvalue;
464 : size_t length;
465 : unsigned char data[1];
466 : };
467 :
468 : static int
469 : door_ipc(void *ctx,
470 : const heim_idata *request, heim_idata *response,
471 : heim_icred *cred)
472 : {
473 : struct path_ctx *d = (struct path_ctx *)ctx;
474 : door_arg_t arg;
475 : int ret;
476 : struct door_reply *r;
477 :
478 : arg.data_ptr = request->data;
479 : arg.data_size = request->length;
480 : arg.desc_ptr = NULL;
481 : arg.desc_num = 0;
482 : arg.rbuf = NULL;
483 : arg.rsize = 0;
484 :
485 : ret = door_call(d->fd, &arg);
486 : if (ret != 0)
487 : return errno;
488 :
489 : if (arg.rsize < offsetof(struct door_reply, data))
490 : return EINVAL;
491 :
492 : r = (struct door_reply *)arg.rbuf;
493 : if (r->returnvalue != 0)
494 : return r->returnvalue;
495 :
496 : if (arg.rsize < offsetof(struct door_reply, data) + r->length)
497 : return ERANGE;
498 :
499 : response->data = malloc(r->length);
500 : if (response->data == NULL) {
501 : munmap(arg.rbuf, arg.rsize);
502 : return ENOMEM;
503 : }
504 :
505 : memcpy(response->data, r->data, r->length);
506 : response->length = r->length;
507 : munmap(arg.rbuf, arg.rsize);
508 :
509 : return 0;
510 : }
511 :
512 : #endif /* HAVE_DOOR_CREATE */
513 :
514 : struct hipc_ops {
515 : const char *prefix;
516 : int (*init)(const char *, void **);
517 : int (*release)(void *);
518 : int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *);
519 : int (*async)(void *, const heim_idata *, void *,
520 : void (*)(void *, int, heim_idata *, heim_icred));
521 : };
522 :
523 : static const struct hipc_ops ipcs[] = {
524 : #if defined(__APPLE__) && defined(HAVE_GCD)
525 : { "MACH", mach_init, mach_release, mach_ipc, mach_async },
526 : #endif
527 : #ifdef HAVE_DOOR_CREATE
528 : { "DOOR", door_init, common_release, door_ipc, NULL },
529 : #endif
530 : { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
531 : };
532 :
533 : struct heim_ipc {
534 : const struct hipc_ops *ops;
535 : void *ctx;
536 : };
537 :
538 :
539 : int
540 0 : heim_ipc_init_context(const char *name, heim_ipc *ctx)
541 : {
542 0 : unsigned int i;
543 0 : int ret, any = 0;
544 :
545 0 : for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
546 0 : size_t prefix_len = strlen(ipcs[i].prefix);
547 0 : heim_ipc c;
548 0 : if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
549 0 : && name[prefix_len] == ':') {
550 0 : } else if (strncmp("ANY:", name, 4) == 0) {
551 0 : prefix_len = 3;
552 0 : any = 1;
553 : } else
554 0 : continue;
555 :
556 0 : c = calloc(1, sizeof(*c));
557 0 : if (c == NULL)
558 0 : return ENOMEM;
559 :
560 0 : c->ops = &ipcs[i];
561 :
562 0 : ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
563 0 : if (ret) {
564 0 : free(c);
565 0 : if (any)
566 0 : continue;
567 0 : return ret;
568 : }
569 :
570 0 : *ctx = c;
571 0 : return 0;
572 : }
573 :
574 0 : return ENOENT;
575 : }
576 :
577 : void
578 0 : heim_ipc_free_context(heim_ipc ctx)
579 : {
580 0 : (ctx->ops->release)(ctx->ctx);
581 0 : free(ctx);
582 0 : }
583 :
584 : int
585 0 : heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
586 : heim_icred *cred)
587 : {
588 0 : if (cred)
589 0 : *cred = NULL;
590 0 : return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred);
591 : }
592 :
593 : int
594 0 : heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx,
595 : void (*func)(void *, int, heim_idata *, heim_icred))
596 : {
597 0 : if (ctx->ops->async == NULL) {
598 0 : heim_idata rcv;
599 0 : heim_icred cred = NULL;
600 0 : int ret;
601 :
602 0 : ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
603 0 : (*func)(userctx, ret, &rcv, cred);
604 0 : heim_ipc_free_cred(cred);
605 0 : free(rcv.data);
606 0 : return ret;
607 : } else {
608 0 : return (ctx->ops->async)(ctx->ctx, snd, userctx, func);
609 : }
610 : }
|