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