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
mach_init(const char * service,void ** ctx)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
mach_ipc(void * ctx,const heim_idata * request,heim_idata * response,heim_icred * cred)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
mheim_ado_acall_reply(mach_port_t server_port,audit_token_t client_creds,int returnvalue,heim_ipc_message_inband_t replyin,mach_msg_type_number_t replyinCnt,heim_ipc_message_outband_t replyout,mach_msg_type_number_t replyoutCnt)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
mach_async(void * ctx,const heim_idata * request,void * userctx,void (* func)(void *,int,heim_idata *,heim_icred))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
mach_release(void * ctx)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
connect_unix(struct path_ctx * s)323 connect_unix(struct path_ctx *s)
324 {
325 struct sockaddr_un addr;
326
327 addr.sun_family = AF_UNIX;
328 strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
329
330 s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
331 if (s->fd < 0)
332 return errno;
333 rk_cloexec(s->fd);
334
335 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
336 return errno;
337
338 return 0;
339 }
340
341 static int
common_path_init(const char * service,const char * file,void ** ctx)342 common_path_init(const char *service,
343 const char *file,
344 void **ctx)
345 {
346 struct path_ctx *s;
347
348 s = malloc(sizeof(*s));
349 if (s == NULL)
350 return ENOMEM;
351 s->fd = -1;
352
353 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
354
355 *ctx = s;
356
357 return 0;
358 }
359
360 static int
unix_socket_init(const char * service,void ** ctx)361 unix_socket_init(const char *service,
362 void **ctx)
363 {
364 int ret;
365
366 ret = common_path_init(service, "socket", ctx);
367 if (ret)
368 return ret;
369 ret = connect_unix(*ctx);
370 if (ret)
371 common_release(*ctx);
372
373 return ret;
374 }
375
376 static int
unix_socket_ipc(void * ctx,const heim_idata * req,heim_idata * rep,heim_icred * cred)377 unix_socket_ipc(void *ctx,
378 const heim_idata *req, heim_idata *rep,
379 heim_icred *cred)
380 {
381 struct path_ctx *s = ctx;
382 uint32_t len = htonl(req->length);
383 uint32_t rv;
384 int retval;
385
386 if (cred)
387 *cred = NULL;
388
389 rep->data = NULL;
390 rep->length = 0;
391
392 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
393 return -1;
394 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
395 return -1;
396
397 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
398 return -1;
399 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
400 return -1;
401 retval = ntohl(rv);
402
403 rep->length = ntohl(len);
404 if (rep->length > 0) {
405 rep->data = malloc(rep->length);
406 if (rep->data == NULL)
407 return -1;
408 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
409 return -1;
410 } else
411 rep->data = NULL;
412
413 return retval;
414 }
415
416 int
common_release(void * ctx)417 common_release(void *ctx)
418 {
419 struct path_ctx *s = ctx;
420 if (s->fd >= 0)
421 close(s->fd);
422 free(s->path);
423 free(s);
424 return 0;
425 }
426
427 #ifdef HAVE_DOOR
428
429 static int
door_init(const char * service,void ** ctx)430 door_init(const char *service,
431 void **ctx)
432 {
433 ret = common_path_init(context, service, "door", ctx);
434 if (ret)
435 return ret;
436 ret = connect_door(*ctx);
437 if (ret)
438 common_release(*ctx);
439 return ret;
440 }
441
442 static int
door_ipc(void * ctx,const heim_idata * request,heim_idata * response,heim_icred * cred)443 door_ipc(void *ctx,
444 const heim_idata *request, heim_idata *response,
445 heim_icred *cred)
446 {
447 door_arg_t arg;
448 int ret;
449
450 arg.data_ptr = request->data;
451 arg.data_size = request->length;
452 arg.desc_ptr = NULL;
453 arg.desc_num = 0;
454 arg.rbuf = NULL;
455 arg.rsize = 0;
456
457 ret = door_call(fd, &arg);
458 close(fd);
459 if (ret != 0)
460 return errno;
461
462 response->data = malloc(arg.rsize);
463 if (response->data == NULL) {
464 munmap(arg.rbuf, arg.rsize);
465 return ENOMEM;
466 }
467 memcpy(response->data, arg.rbuf, arg.rsize);
468 response->length = arg.rsize;
469 munmap(arg.rbuf, arg.rsize);
470
471 return ret;
472 }
473
474 #endif
475
476 struct hipc_ops {
477 const char *prefix;
478 int (*init)(const char *, void **);
479 int (*release)(void *);
480 int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *);
481 int (*async)(void *, const heim_idata *, void *,
482 void (*)(void *, int, heim_idata *, heim_icred));
483 };
484
485 struct hipc_ops ipcs[] = {
486 #if defined(__APPLE__) && defined(HAVE_GCD)
487 { "MACH", mach_init, mach_release, mach_ipc, mach_async },
488 #endif
489 #ifdef HAVE_DOOR
490 { "DOOR", door_init, common_release, door_ipc, NULL }
491 #endif
492 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
493 };
494
495 struct heim_ipc {
496 struct hipc_ops *ops;
497 void *ctx;
498 };
499
500
501 int
heim_ipc_init_context(const char * name,heim_ipc * ctx)502 heim_ipc_init_context(const char *name, heim_ipc *ctx)
503 {
504 unsigned int i;
505 int ret, any = 0;
506
507 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
508 size_t prefix_len = strlen(ipcs[i].prefix);
509 heim_ipc c;
510 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
511 && name[prefix_len] == ':') {
512 } else if (strncmp("ANY:", name, 4) == 0) {
513 prefix_len = 3;
514 any = 1;
515 } else
516 continue;
517
518 c = calloc(1, sizeof(*c));
519 if (c == NULL)
520 return ENOMEM;
521
522 c->ops = &ipcs[i];
523
524 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
525 if (ret) {
526 free(c);
527 if (any)
528 continue;
529 return ret;
530 }
531
532 *ctx = c;
533 return 0;
534 }
535
536 return ENOENT;
537 }
538
539 void
heim_ipc_free_context(heim_ipc ctx)540 heim_ipc_free_context(heim_ipc ctx)
541 {
542 (ctx->ops->release)(ctx->ctx);
543 free(ctx);
544 }
545
546 int
heim_ipc_call(heim_ipc ctx,const heim_idata * snd,heim_idata * rcv,heim_icred * cred)547 heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
548 heim_icred *cred)
549 {
550 if (cred)
551 *cred = NULL;
552 return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred);
553 }
554
555 int
heim_ipc_async(heim_ipc ctx,const heim_idata * snd,void * userctx,void (* func)(void *,int,heim_idata *,heim_icred))556 heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx,
557 void (*func)(void *, int, heim_idata *, heim_icred))
558 {
559 if (ctx->ops->async == NULL) {
560 heim_idata rcv;
561 heim_icred cred = NULL;
562 int ret;
563
564 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
565 (*func)(userctx, ret, &rcv, cred);
566 heim_ipc_free_cred(cred);
567 free(rcv.data);
568 return ret;
569 } else {
570 return (ctx->ops->async)(ctx->ctx, snd, userctx, func);
571 }
572 }
573