Home | History | Annotate | Line # | Download | only in ipc
      1 /*	$NetBSD: client.c,v 1.3 2023/06/19 21:41:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2009 Kungliga Tekniska Hgskolan
      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
     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
     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
    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
    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
    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
    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 	return errno;
    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     if (asprintf(&s->path, "/var/run/.heim_%s-%s", service, file) == -1) {
    356 	free(s);
    357 	return ENOMEM;
    358     }
    359 
    360     *ctx = s;
    361     return 0;
    362 }
    363 
    364 static int
    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
    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
    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
    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
    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
    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
    544 heim_ipc_free_context(heim_ipc ctx)
    545 {
    546     (ctx->ops->release)(ctx->ctx);
    547     free(ctx);
    548 }
    549 
    550 int
    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
    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