/src/samba/third_party/heimdal/lib/ipc/client.c
Line | Count | Source |
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 | | connect_unix(struct path_ctx *s) |
324 | 0 | { |
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 | 0 | } |
340 | | |
341 | | static int |
342 | | common_path_init(const char *base, |
343 | | const char *service, |
344 | | const char *file, |
345 | | void **ctx) |
346 | 0 | { |
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 | 0 | } |
358 | | |
359 | 0 | *ctx = s; |
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | | static int |
364 | | unix_socket_init(const char *service, |
365 | | void **ctx) |
366 | 0 | { |
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 | 0 | } |
379 | | |
380 | | static int |
381 | | unix_socket_ipc(void *ctx, |
382 | | const heim_idata *req, heim_idata *rep, |
383 | | heim_icred *cred) |
384 | 0 | { |
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 | 0 | } else |
415 | 0 | rep->data = NULL; |
416 | | |
417 | 0 | return retval; |
418 | 0 | } |
419 | | |
420 | | int |
421 | | common_release(void *ctx) |
422 | 0 | { |
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 | 0 | } |
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 | | heim_ipc_init_context(const char *name, heim_ipc *ctx) |
541 | 0 | { |
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 | 0 | } 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 | 0 | } |
569 | | |
570 | 0 | *ctx = c; |
571 | 0 | return 0; |
572 | 0 | } |
573 | | |
574 | 0 | return ENOENT; |
575 | 0 | } |
576 | | |
577 | | void |
578 | | heim_ipc_free_context(heim_ipc ctx) |
579 | 0 | { |
580 | 0 | (ctx->ops->release)(ctx->ctx); |
581 | 0 | free(ctx); |
582 | 0 | } |
583 | | |
584 | | int |
585 | | heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, |
586 | | heim_icred *cred) |
587 | 0 | { |
588 | 0 | if (cred) |
589 | 0 | *cred = NULL; |
590 | 0 | return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); |
591 | 0 | } |
592 | | |
593 | | int |
594 | | heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, |
595 | | void (*func)(void *, int, heim_idata *, heim_icred)) |
596 | 0 | { |
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 | 0 | } else { |
608 | 0 | return (ctx->ops->async)(ctx->ctx, snd, userctx, func); |
609 | 0 | } |
610 | 0 | } |