1 /*	$NetBSD: client.c,v 1.1.1.2 2014/04/24 12:45:48 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Kungliga Tekniska H�gskolan
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
mach_init(const char * service,void ** ctx)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
mach_ipc(void * ctx,const heim_idata * request,heim_idata * response,heim_icred * cred)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
mheim_ado_acall_reply(mach_port_t server_port,audit_token_t client_creds,int returnvalue,heim_ipc_message_inband_t replyin,mach_msg_type_number_t replyinCnt,heim_ipc_message_outband_t replyout,mach_msg_type_number_t replyoutCnt)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
mach_async(void * ctx,const heim_idata * request,void * userctx,void (* func)(void *,int,heim_idata *,heim_icred))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
mach_release(void * ctx)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
connect_unix(struct path_ctx * s)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 	close(s->fd);
339 	return errno;
340     }
341 
342     return 0;
343 }
344 
345 static int
common_path_init(const char * service,const char * file,void ** ctx)346 common_path_init(const char *service,
347 		 const char *file,
348 		 void **ctx)
349 {
350     struct path_ctx *s;
351 
352     s = malloc(sizeof(*s));
353     if (s == NULL)
354 	return ENOMEM;
355     s->fd = -1;
356 
357     asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
358 
359     *ctx = s;
360 
361     return 0;
362 }
363 
364 static int
unix_socket_init(const char * service,void ** ctx)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
unix_socket_ipc(void * ctx,const heim_idata * req,heim_idata * rep,heim_icred * cred)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
common_release(void * ctx)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
door_init(const char * service,void ** ctx)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
door_ipc(void * ctx,const heim_idata * request,heim_idata * response,heim_icred * cred)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
heim_ipc_init_context(const char * name,heim_ipc * ctx)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
heim_ipc_free_context(heim_ipc ctx)544 heim_ipc_free_context(heim_ipc ctx)
545 {
546     (ctx->ops->release)(ctx->ctx);
547     free(ctx);
548 }
549 
550 int
heim_ipc_call(heim_ipc ctx,const heim_idata * snd,heim_idata * rcv,heim_icred * cred)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
heim_ipc_async(heim_ipc ctx,const heim_idata * snd,void * userctx,void (* func)(void *,int,heim_idata *,heim_icred))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