]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/heimdal/lib/ipc/client.c
heimdal: Fix multiple security vulnerabilities
[FreeBSD/FreeBSD.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         return errno;
337
338     return 0;
339 }
340
341 static int
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
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
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
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
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
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
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
540 heim_ipc_free_context(heim_ipc ctx)
541 {
542     (ctx->ops->release)(ctx->ctx);
543     free(ctx);
544 }
545
546 int
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
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 }