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