client.c revision 226031
1137015Sdes/*
298937Sdes * Copyright (c) 2009 Kungliga Tekniska H�gskolan
398937Sdes * (Royal Institute of Technology, Stockholm, Sweden).
498937Sdes * All rights reserved.
598937Sdes *
698937Sdes * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
798937Sdes *
898937Sdes * Redistribution and use in source and binary forms, with or without
998937Sdes * modification, are permitted provided that the following conditions
1098937Sdes * are met:
1198937Sdes *
1298937Sdes * 1. Redistributions of source code must retain the above copyright
1398937Sdes *    notice, this list of conditions and the following disclaimer.
1498937Sdes *
1598937Sdes * 2. Redistributions in binary form must reproduce the above copyright
1698937Sdes *    notice, this list of conditions and the following disclaimer in the
1798937Sdes *    documentation and/or other materials provided with the distribution.
1898937Sdes *
1998937Sdes * 3. Neither the name of the Institute nor the names of its contributors
2098937Sdes *    may be used to endorse or promote products derived from this software
2198937Sdes *    without specific prior written permission.
2298937Sdes *
2398937Sdes * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2498937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2598937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2698937Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2798937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2898937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2999060Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30113908Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3198937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3298937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3398937Sdes * SUCH DAMAGE.
3498937Sdes */
3598937Sdes
3698937Sdes#include "hi_locl.h"
3798937Sdes
3898937Sdes#if defined(__APPLE__) && defined(HAVE_GCD)
3998937Sdes
4098937Sdes#include "heim_ipc.h"
4198937Sdes#include "heim_ipc_asyncServer.h"
4298937Sdes
4398937Sdes#include <dispatch/dispatch.h>
4498937Sdes#include <mach/mach.h>
4598937Sdes
4698937Sdesstatic dispatch_once_t jobqinited = 0;
4798937Sdesstatic dispatch_queue_t jobq = NULL;
4898937Sdesstatic dispatch_queue_t syncq;
49124208Sdes
5098937Sdesstruct mach_ctx {
5198937Sdes    mach_port_t server;
5298937Sdes    char *name;
53113908Sdes};
5498937Sdes
5598937Sdesstatic int
5698937Sdesmach_release(void *ctx);
5798937Sdes
5898937Sdesstatic int
5998937Sdesmach_init(const char *service, void **ctx)
6098937Sdes{
6198937Sdes    struct mach_ctx *ipc;
62113908Sdes    mach_port_t sport;
6398937Sdes    int ret;
64126274Sdes
65126274Sdes    dispatch_once(&jobqinited, ^{
66126274Sdes	    jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
67126274Sdes	    syncq = dispatch_queue_create("heim-ipc-syncq", NULL);
68126274Sdes	});
69126274Sdes
70137015Sdes    ret = bootstrap_look_up(bootstrap_port, service, &sport);
71137015Sdes    if (ret)
72137015Sdes	return ret;
73124208Sdes
7498937Sdes    ipc = malloc(sizeof(*ipc));
75113908Sdes    if (ipc == NULL) {
76113908Sdes	mach_port_destroy(mach_task_self(), sport);
7798937Sdes	return ENOMEM;
78113908Sdes    }
79137015Sdes
80113908Sdes    ipc->server = sport;
81113908Sdes    ipc->name = strdup(service);
82113908Sdes    if (ipc->name == NULL) {
83113908Sdes	mach_release(ipc);
84137015Sdes	return ENOMEM;
85124208Sdes    }
86124208Sdes
87126274Sdes    *ctx = ipc;
8898937Sdes
8998937Sdes    return 0;
9098937Sdes}
9198937Sdes
9298937Sdesstatic int
9398937Sdesmach_ipc(void *ctx,
9498937Sdes	 const heim_idata *request, heim_idata *response,
9598937Sdes	 heim_icred *cred)
9698937Sdes{
97113908Sdes    struct mach_ctx *ipc = ctx;
98113908Sdes    heim_ipc_message_inband_t requestin;
99113908Sdes    mach_msg_type_number_t requestin_length = 0;
100113908Sdes    heim_ipc_message_outband_t requestout = NULL;
101113908Sdes    mach_msg_type_number_t requestout_length = 0;
102113908Sdes    heim_ipc_message_inband_t replyin;
103113908Sdes    mach_msg_type_number_t replyin_length;
104113908Sdes    heim_ipc_message_outband_t replyout;
105113908Sdes    mach_msg_type_number_t replyout_length;
106113908Sdes    int ret, errorcode, retries = 0;
107113908Sdes
108113908Sdes    memcpy(requestin, request->data, request->length);
109113908Sdes    requestin_length = request->length;
110113908Sdes
111113908Sdes    while (retries < 2) {
11298937Sdes	__block mach_port_t sport;
113113908Sdes
11498937Sdes	dispatch_sync(syncq, ^{ sport = ipc->server; });
115124208Sdes
11698937Sdes	ret = mheim_ipc_call(sport,
117124208Sdes			     requestin, requestin_length,
118124208Sdes			     requestout, requestout_length,
119124208Sdes			     &errorcode,
12098937Sdes			     replyin, &replyin_length,
12198937Sdes			     &replyout, &replyout_length);
12298937Sdes	if (ret == MACH_SEND_INVALID_DEST) {
12398937Sdes	    mach_port_t nport;
12498937Sdes	    /* race other threads to get a new port */
12598937Sdes	    ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport);
12698937Sdes	    if (ret)
12798937Sdes		return ret;
12898937Sdes	    dispatch_sync(syncq, ^{
12998937Sdes		    /* check if we lost the race to lookup the port */
13098937Sdes		    if (sport != ipc->server) {
13198937Sdes			mach_port_deallocate(mach_task_self(), nport);
13298937Sdes		    } else {
13398937Sdes			mach_port_deallocate(mach_task_self(), ipc->server);
13498937Sdes			ipc->server = nport;
13598937Sdes		    }
13698937Sdes		});
13798937Sdes	    retries++;
13898937Sdes	} else if (ret) {
139113908Sdes	    return ret;
140113908Sdes	} else
14198937Sdes	    break;
14298937Sdes    }
143126274Sdes    if (retries >= 2)
14498937Sdes	return EINVAL;
14598937Sdes
146126274Sdes    if (errorcode) {
14798937Sdes	if (replyout_length)
14898937Sdes	    vm_deallocate (mach_task_self (), (vm_address_t) replyout,
149126274Sdes			   replyout_length);
15098937Sdes	return errorcode;
15198937Sdes    }
152126274Sdes
15398937Sdes    if (replyout_length) {
15498937Sdes	response->data = malloc(replyout_length);
155126274Sdes	if (response->data == NULL) {
15698937Sdes	    vm_deallocate (mach_task_self (), (vm_address_t) replyout,
15798937Sdes			   replyout_length);
158126274Sdes	    return ENOMEM;
15998937Sdes	}
160126274Sdes	memcpy(response->data, replyout, replyout_length);
161126274Sdes	response->length = replyout_length;
16298937Sdes	vm_deallocate (mach_task_self (), (vm_address_t) replyout,
16398937Sdes		       replyout_length);
16498937Sdes    } else {
16598937Sdes	response->data = malloc(replyin_length);
16698937Sdes	if (response->data == NULL)
16798937Sdes	    return ENOMEM;
16898937Sdes	memcpy(response->data, replyin, replyin_length);
16998937Sdes	response->length = replyin_length;
17098937Sdes    }
17198937Sdes
17298937Sdes    return 0;
17398937Sdes}
17498937Sdes
17598937Sdesstruct async_client {
17698937Sdes    mach_port_t mp;
177124208Sdes    dispatch_source_t source;
17898937Sdes    dispatch_queue_t queue;
17998937Sdes    void (*func)(void *, int, heim_idata *, heim_icred);
18098937Sdes    void *userctx;
18198937Sdes};
18298937Sdes
18398937Sdeskern_return_t
18498937Sdesmheim_ado_acall_reply(mach_port_t server_port,
18598937Sdes		      audit_token_t client_creds,
186124208Sdes		      int returnvalue,
187124208Sdes		      heim_ipc_message_inband_t replyin,
188124208Sdes		      mach_msg_type_number_t replyinCnt,
189124208Sdes		      heim_ipc_message_outband_t replyout,
190124208Sdes		      mach_msg_type_number_t replyoutCnt)
191124208Sdes{
192124208Sdes    struct async_client *c = dispatch_get_context(dispatch_get_current_queue());
193124208Sdes    heim_idata response;
194124208Sdes
195124208Sdes    if (returnvalue) {
196126274Sdes	response.data = NULL;
197126274Sdes	response.length = 0;
19898937Sdes    } else if (replyoutCnt) {
19998937Sdes	response.data = replyout;
200124208Sdes	response.length = replyoutCnt;
201126274Sdes    } else {
202137015Sdes	response.data = replyin;
203137015Sdes	response.length = replyinCnt;
20498937Sdes    }
20598937Sdes
20698937Sdes    (*c->func)(c->userctx, returnvalue, &response, NULL);
207137015Sdes
208137015Sdes    if (replyoutCnt)
209137015Sdes	vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt);
21098937Sdes
211124208Sdes    dispatch_source_cancel(c->source);
21298937Sdes
21398937Sdes    return 0;
214124208Sdes
21598937Sdes
216124208Sdes}
217124208Sdes
21898937Sdes
21998937Sdesstatic int
22098937Sdesmach_async(void *ctx, const heim_idata *request, void *userctx,
22198937Sdes	   void (*func)(void *, int, heim_idata *, heim_icred))
22298937Sdes{
22398937Sdes    struct mach_ctx *ipc = ctx;
22498937Sdes    heim_ipc_message_inband_t requestin;
22598937Sdes    mach_msg_type_number_t requestin_length = 0;
22698937Sdes    heim_ipc_message_outband_t requestout = NULL;
22798937Sdes    mach_msg_type_number_t requestout_length = 0;
228126274Sdes    int ret, retries = 0;
22998937Sdes    kern_return_t kr;
23098937Sdes    struct async_client *c;
231124208Sdes
232124208Sdes    /* first create the service that will catch the reply from the server */
23398937Sdes    /* XXX these object should be cached and reused */
234106121Sdes
235106121Sdes    c = malloc(sizeof(*c));
23699060Sdes    if (c == NULL)
23798937Sdes	return ENOMEM;
23898937Sdes
23998937Sdes    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp);
24098937Sdes    if (kr != KERN_SUCCESS)
24198937Sdes	return EINVAL;
24298937Sdes
24398937Sdes    c->queue = dispatch_queue_create("heim-ipc-async-client", NULL);
24498937Sdes    c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue);
24598937Sdes    dispatch_set_context(c->queue, c);
24698937Sdes
24798937Sdes    dispatch_source_set_event_handler(c->source, ^{
24898937Sdes	    dispatch_mig_server(c->source,
249106121Sdes				sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem),
250113908Sdes				mheim_aipc_server);
251113908Sdes	});
252113908Sdes
253113908Sdes    dispatch_source_set_cancel_handler(c->source, ^{
254113908Sdes	    mach_port_mod_refs(mach_task_self(), c->mp,
255113908Sdes			       MACH_PORT_RIGHT_RECEIVE, -1);
256113908Sdes	    dispatch_release(c->queue);
25798937Sdes	    dispatch_release(c->source);
258113908Sdes	    free(c);
25998937Sdes	});
260113908Sdes
261113908Sdes    c->func = func;
262113908Sdes    c->userctx = userctx;
26398937Sdes
26498937Sdes    dispatch_resume(c->source);
26598937Sdes
26698937Sdes    /* ok, send the message */
26798937Sdes
26898937Sdes    memcpy(requestin, request->data, request->length);
26998937Sdes    requestin_length = request->length;
27098937Sdes
27198937Sdes    while (retries < 2) {
27299060Sdes	__block mach_port_t sport;
27398937Sdes
27498937Sdes	dispatch_sync(syncq, ^{ sport = ipc->server; });
275113908Sdes
276113908Sdes	ret = mheim_ipc_call_request(sport, c->mp,
27798937Sdes				     requestin, requestin_length,
27898937Sdes				     requestout, requestout_length);
279106121Sdes	if (ret == MACH_SEND_INVALID_DEST) {
28098937Sdes	    ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport);
281106121Sdes	    if (ret) {
28298937Sdes		dispatch_source_cancel(c->source);
28398937Sdes		return ret;
28498937Sdes	    }
285106121Sdes	    mach_port_deallocate(mach_task_self(), ipc->server);
28698937Sdes	    ipc->server = sport;
28798937Sdes	    retries++;
28898937Sdes	} else if (ret) {
28998937Sdes	    dispatch_source_cancel(c->source);
290106121Sdes	    return ret;
29198937Sdes	} else
29298937Sdes	    break;
29398937Sdes    }
29498937Sdes    if (retries >= 2) {
295106121Sdes	dispatch_source_cancel(c->source);
29698937Sdes	return EINVAL;
29798937Sdes    }
29898937Sdes
29998937Sdes    return 0;
30098937Sdes}
30198937Sdes
302106121Sdesstatic int
30398937Sdesmach_release(void *ctx)
30498937Sdes{
30598937Sdes    struct mach_ctx *ipc = ctx;
30698937Sdes    if (ipc->server != MACH_PORT_NULL)
30798937Sdes	mach_port_deallocate(mach_task_self(), ipc->server);
30898937Sdes    free(ipc->name);
30998937Sdes    free(ipc);
31098937Sdes    return 0;
31198937Sdes}
31298937Sdes
31398937Sdes#endif
314106121Sdes
31598937Sdesstruct path_ctx {
31698937Sdes    char *path;
31798937Sdes    int fd;
31898937Sdes};
31998937Sdes
32098937Sdesstatic int common_release(void *);
32198937Sdes
32298937Sdesstatic int
32398937Sdesconnect_unix(struct path_ctx *s)
32498937Sdes{
32598937Sdes    struct sockaddr_un addr;
32698937Sdes
32798937Sdes    addr.sun_family = AF_UNIX;
32898937Sdes    strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
32998937Sdes
33098937Sdes    s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
33198937Sdes    if (s->fd < 0)
33298937Sdes	return errno;
33398937Sdes    rk_cloexec(s->fd);
33498937Sdes
33598937Sdes    if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
33698937Sdes	close(s->fd);
33798937Sdes	return errno;
33898937Sdes    }
33998937Sdes
34098937Sdes    return 0;
34198937Sdes}
34298937Sdes
34398937Sdesstatic int
34498937Sdescommon_path_init(const char *service,
34598937Sdes		 const char *file,
34698937Sdes		 void **ctx)
34798937Sdes{
34898937Sdes    struct path_ctx *s;
349126274Sdes
35098937Sdes    s = malloc(sizeof(*s));
35198937Sdes    if (s == NULL)
35298937Sdes	return ENOMEM;
35398937Sdes    s->fd = -1;
35498937Sdes
35598937Sdes    asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
35698937Sdes
35798937Sdes    *ctx = s;
35898937Sdes
35998937Sdes    return 0;
36098937Sdes}
36198937Sdes
36298937Sdesstatic int
36398937Sdesunix_socket_init(const char *service,
36498937Sdes		 void **ctx)
36598937Sdes{
36698937Sdes    int ret;
36798937Sdes
36898937Sdes    ret = common_path_init(service, "socket", ctx);
36998937Sdes    if (ret)
37098937Sdes	return ret;
37198937Sdes    ret = connect_unix(*ctx);
37298937Sdes    if (ret)
37398937Sdes	common_release(*ctx);
374124208Sdes
375124208Sdes    return ret;
376124208Sdes}
377124208Sdes
378124208Sdesstatic int
379124208Sdesunix_socket_ipc(void *ctx,
380137015Sdes		const heim_idata *req, heim_idata *rep,
381124208Sdes		heim_icred *cred)
382124208Sdes{
383124208Sdes    struct path_ctx *s = ctx;
384124208Sdes    uint32_t len = htonl(req->length);
385124208Sdes    uint32_t rv;
386124208Sdes    int retval;
387124208Sdes
388124208Sdes    if (cred)
389124208Sdes	*cred = NULL;
390124208Sdes
391124208Sdes    rep->data = NULL;
392124208Sdes    rep->length = 0;
393124208Sdes
394124208Sdes    if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
395124208Sdes	return -1;
396126274Sdes    if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
397124208Sdes	return -1;
398124208Sdes
399124208Sdes    if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
400124208Sdes	return -1;
401124208Sdes    if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
402124208Sdes	return -1;
403124208Sdes    retval = ntohl(rv);
404124208Sdes
405124208Sdes    rep->length = ntohl(len);
406124208Sdes    if (rep->length > 0) {
407124208Sdes	rep->data = malloc(rep->length);
408124208Sdes	if (rep->data == NULL)
409124208Sdes	    return -1;
410124208Sdes	if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
411124208Sdes	    return -1;
412137015Sdes    } else
413137015Sdes	rep->data = NULL;
414137015Sdes
415137015Sdes    return retval;
416137015Sdes}
417137015Sdes
418int
419common_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
431static int
432door_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
444static int
445door_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
478struct 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
487struct 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
497struct heim_ipc {
498    struct hipc_ops *ops;
499    void *ctx;
500};
501
502
503int
504heim_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
541void
542heim_ipc_free_context(heim_ipc ctx)
543{
544    (ctx->ops->release)(ctx->ctx);
545    free(ctx);
546}
547
548int
549heim_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
557int
558heim_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}
575