xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/ipc/server.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1*d3273b5bSchristos /*	$NetBSD: server.c,v 1.2 2017/01/28 21:31:48 christos Exp $	*/
2ca1c9b0cSelric 
3ca1c9b0cSelric /*
4ca1c9b0cSelric  * Copyright (c) 2009 Kungliga Tekniska H�gskolan
5ca1c9b0cSelric  * (Royal Institute of Technology, Stockholm, Sweden).
6ca1c9b0cSelric  * All rights reserved.
7ca1c9b0cSelric  *
8ca1c9b0cSelric  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9ca1c9b0cSelric  *
10ca1c9b0cSelric  * Redistribution and use in source and binary forms, with or without
11ca1c9b0cSelric  * modification, are permitted provided that the following conditions
12ca1c9b0cSelric  * are met:
13ca1c9b0cSelric  *
14ca1c9b0cSelric  * 1. Redistributions of source code must retain the above copyright
15ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer.
16ca1c9b0cSelric  *
17ca1c9b0cSelric  * 2. Redistributions in binary form must reproduce the above copyright
18ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer in the
19ca1c9b0cSelric  *    documentation and/or other materials provided with the distribution.
20ca1c9b0cSelric  *
21ca1c9b0cSelric  * 3. Neither the name of the Institute nor the names of its contributors
22ca1c9b0cSelric  *    may be used to endorse or promote products derived from this software
23ca1c9b0cSelric  *    without specific prior written permission.
24ca1c9b0cSelric  *
25ca1c9b0cSelric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26ca1c9b0cSelric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27ca1c9b0cSelric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28ca1c9b0cSelric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29ca1c9b0cSelric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30ca1c9b0cSelric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31ca1c9b0cSelric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32ca1c9b0cSelric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33ca1c9b0cSelric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34ca1c9b0cSelric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35ca1c9b0cSelric  * SUCH DAMAGE.
36ca1c9b0cSelric  */
37ca1c9b0cSelric 
38ca1c9b0cSelric #include "hi_locl.h"
39ca1c9b0cSelric #include <assert.h>
40b9d004c6Schristos #include <err.h>
41ca1c9b0cSelric 
42ca1c9b0cSelric #define MAX_PACKET_SIZE (128 * 1024)
43ca1c9b0cSelric 
44ca1c9b0cSelric struct heim_sipc {
45ca1c9b0cSelric     int (*release)(heim_sipc ctx);
46ca1c9b0cSelric     heim_ipc_callback callback;
47ca1c9b0cSelric     void *userctx;
48ca1c9b0cSelric     void *mech;
49ca1c9b0cSelric };
50ca1c9b0cSelric 
51ca1c9b0cSelric #if defined(__APPLE__) && defined(HAVE_GCD)
52ca1c9b0cSelric 
53ca1c9b0cSelric #include "heim_ipcServer.h"
54ca1c9b0cSelric #include "heim_ipc_reply.h"
55ca1c9b0cSelric #include "heim_ipc_async.h"
56ca1c9b0cSelric 
57ca1c9b0cSelric static dispatch_source_t timer;
58ca1c9b0cSelric static dispatch_queue_t timerq;
59ca1c9b0cSelric static uint64_t timeoutvalue;
60ca1c9b0cSelric 
61ca1c9b0cSelric static dispatch_queue_t eventq;
62ca1c9b0cSelric 
63ca1c9b0cSelric static dispatch_queue_t workq;
64ca1c9b0cSelric 
65ca1c9b0cSelric static void
default_timer_ev(void)66ca1c9b0cSelric default_timer_ev(void)
67ca1c9b0cSelric {
68ca1c9b0cSelric     exit(0);
69ca1c9b0cSelric }
70ca1c9b0cSelric 
71ca1c9b0cSelric static void (*timer_ev)(void) = default_timer_ev;
72ca1c9b0cSelric 
73ca1c9b0cSelric static void
set_timer(void)74ca1c9b0cSelric set_timer(void)
75ca1c9b0cSelric {
76ca1c9b0cSelric     dispatch_source_set_timer(timer,
77ca1c9b0cSelric 			      dispatch_time(DISPATCH_TIME_NOW,
78ca1c9b0cSelric 					    timeoutvalue * NSEC_PER_SEC),
79ca1c9b0cSelric 			      timeoutvalue * NSEC_PER_SEC, 1000000);
80ca1c9b0cSelric }
81ca1c9b0cSelric 
82ca1c9b0cSelric static void
init_globals(void)83ca1c9b0cSelric init_globals(void)
84ca1c9b0cSelric {
85ca1c9b0cSelric     static dispatch_once_t once;
86ca1c9b0cSelric     dispatch_once(&once, ^{
87ca1c9b0cSelric 	timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
88ca1c9b0cSelric         timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
89ca1c9b0cSelric 	dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
90ca1c9b0cSelric 
91ca1c9b0cSelric 	workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
92ca1c9b0cSelric 	eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
93ca1c9b0cSelric     });
94ca1c9b0cSelric }
95ca1c9b0cSelric 
96ca1c9b0cSelric static void
suspend_timer(void)97ca1c9b0cSelric suspend_timer(void)
98ca1c9b0cSelric {
99ca1c9b0cSelric     dispatch_suspend(timer);
100ca1c9b0cSelric }
101ca1c9b0cSelric 
102ca1c9b0cSelric static void
restart_timer(void)103ca1c9b0cSelric restart_timer(void)
104ca1c9b0cSelric {
105ca1c9b0cSelric     dispatch_sync(timerq, ^{ set_timer(); });
106ca1c9b0cSelric     dispatch_resume(timer);
107ca1c9b0cSelric }
108ca1c9b0cSelric 
109ca1c9b0cSelric struct mach_service {
110ca1c9b0cSelric     mach_port_t sport;
111ca1c9b0cSelric     dispatch_source_t source;
112ca1c9b0cSelric     dispatch_queue_t queue;
113ca1c9b0cSelric };
114ca1c9b0cSelric 
115ca1c9b0cSelric struct mach_call_ctx {
116ca1c9b0cSelric     mach_port_t reply_port;
117ca1c9b0cSelric     heim_icred cred;
118ca1c9b0cSelric     heim_idata req;
119ca1c9b0cSelric };
120ca1c9b0cSelric 
121ca1c9b0cSelric 
122ca1c9b0cSelric static void
mach_complete_sync(heim_sipc_call ctx,int returnvalue,heim_idata * reply)123ca1c9b0cSelric mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
124ca1c9b0cSelric {
125ca1c9b0cSelric     struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
126ca1c9b0cSelric     heim_ipc_message_inband_t replyin;
127ca1c9b0cSelric     mach_msg_type_number_t replyinCnt;
128ca1c9b0cSelric     heim_ipc_message_outband_t replyout;
129ca1c9b0cSelric     mach_msg_type_number_t replyoutCnt;
130ca1c9b0cSelric     kern_return_t kr;
131ca1c9b0cSelric 
132ca1c9b0cSelric     if (returnvalue) {
133ca1c9b0cSelric 	/* on error, no reply */
134ca1c9b0cSelric 	replyinCnt = 0;
135ca1c9b0cSelric 	replyout = 0; replyoutCnt = 0;
136ca1c9b0cSelric 	kr = KERN_SUCCESS;
137ca1c9b0cSelric     } else if (reply->length < 2048) {
138ca1c9b0cSelric 	replyinCnt = reply->length;
139ca1c9b0cSelric 	memcpy(replyin, reply->data, replyinCnt);
140ca1c9b0cSelric 	replyout = 0; replyoutCnt = 0;
141ca1c9b0cSelric 	kr = KERN_SUCCESS;
142ca1c9b0cSelric     } else {
143ca1c9b0cSelric 	replyinCnt = 0;
144ca1c9b0cSelric 	kr = vm_read(mach_task_self(),
145ca1c9b0cSelric 		     (vm_address_t)reply->data, reply->length,
146ca1c9b0cSelric 		     (vm_address_t *)&replyout, &replyoutCnt);
147ca1c9b0cSelric     }
148ca1c9b0cSelric 
149ca1c9b0cSelric     mheim_ripc_call_reply(s->reply_port, returnvalue,
150ca1c9b0cSelric 			  replyin, replyinCnt,
151ca1c9b0cSelric 			  replyout, replyoutCnt);
152ca1c9b0cSelric 
153ca1c9b0cSelric     heim_ipc_free_cred(s->cred);
154ca1c9b0cSelric     free(s->req.data);
155ca1c9b0cSelric     free(s);
156ca1c9b0cSelric     restart_timer();
157ca1c9b0cSelric }
158ca1c9b0cSelric 
159ca1c9b0cSelric static void
mach_complete_async(heim_sipc_call ctx,int returnvalue,heim_idata * reply)160ca1c9b0cSelric mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
161ca1c9b0cSelric {
162ca1c9b0cSelric     struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
163ca1c9b0cSelric     heim_ipc_message_inband_t replyin;
164ca1c9b0cSelric     mach_msg_type_number_t replyinCnt;
165ca1c9b0cSelric     heim_ipc_message_outband_t replyout;
166ca1c9b0cSelric     mach_msg_type_number_t replyoutCnt;
167ca1c9b0cSelric     kern_return_t kr;
168ca1c9b0cSelric 
169ca1c9b0cSelric     if (returnvalue) {
170ca1c9b0cSelric 	/* on error, no reply */
171ca1c9b0cSelric 	replyinCnt = 0;
172ca1c9b0cSelric 	replyout = 0; replyoutCnt = 0;
173ca1c9b0cSelric 	kr = KERN_SUCCESS;
174ca1c9b0cSelric     } else if (reply->length < 2048) {
175ca1c9b0cSelric 	replyinCnt = reply->length;
176ca1c9b0cSelric 	memcpy(replyin, reply->data, replyinCnt);
177ca1c9b0cSelric 	replyout = 0; replyoutCnt = 0;
178ca1c9b0cSelric 	kr = KERN_SUCCESS;
179ca1c9b0cSelric     } else {
180ca1c9b0cSelric 	replyinCnt = 0;
181ca1c9b0cSelric 	kr = vm_read(mach_task_self(),
182ca1c9b0cSelric 		     (vm_address_t)reply->data, reply->length,
183ca1c9b0cSelric 		     (vm_address_t *)&replyout, &replyoutCnt);
184ca1c9b0cSelric     }
185ca1c9b0cSelric 
186ca1c9b0cSelric     kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
187ca1c9b0cSelric 				replyin, replyinCnt,
188ca1c9b0cSelric 				replyout, replyoutCnt);
189ca1c9b0cSelric     heim_ipc_free_cred(s->cred);
190ca1c9b0cSelric     free(s->req.data);
191ca1c9b0cSelric     free(s);
192ca1c9b0cSelric     restart_timer();
193ca1c9b0cSelric }
194ca1c9b0cSelric 
195ca1c9b0cSelric 
196ca1c9b0cSelric kern_return_t
mheim_do_call(mach_port_t server_port,audit_token_t client_creds,mach_port_t reply_port,heim_ipc_message_inband_t requestin,mach_msg_type_number_t requestinCnt,heim_ipc_message_outband_t requestout,mach_msg_type_number_t requestoutCnt,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)197ca1c9b0cSelric mheim_do_call(mach_port_t server_port,
198ca1c9b0cSelric 	      audit_token_t client_creds,
199ca1c9b0cSelric 	      mach_port_t reply_port,
200ca1c9b0cSelric 	      heim_ipc_message_inband_t requestin,
201ca1c9b0cSelric 	      mach_msg_type_number_t requestinCnt,
202ca1c9b0cSelric 	      heim_ipc_message_outband_t requestout,
203ca1c9b0cSelric 	      mach_msg_type_number_t requestoutCnt,
204ca1c9b0cSelric 	      int *returnvalue,
205ca1c9b0cSelric 	      heim_ipc_message_inband_t replyin,
206ca1c9b0cSelric 	      mach_msg_type_number_t *replyinCnt,
207ca1c9b0cSelric 	      heim_ipc_message_outband_t *replyout,
208ca1c9b0cSelric 	      mach_msg_type_number_t *replyoutCnt)
209ca1c9b0cSelric {
210ca1c9b0cSelric     heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
211ca1c9b0cSelric     struct mach_call_ctx *s;
212ca1c9b0cSelric     kern_return_t kr;
213ca1c9b0cSelric     uid_t uid;
214ca1c9b0cSelric     gid_t gid;
215ca1c9b0cSelric     pid_t pid;
216ca1c9b0cSelric     au_asid_t session;
217ca1c9b0cSelric 
218ca1c9b0cSelric     *replyout = NULL;
219ca1c9b0cSelric     *replyoutCnt = 0;
220ca1c9b0cSelric     *replyinCnt = 0;
221ca1c9b0cSelric 
222ca1c9b0cSelric     s = malloc(sizeof(*s));
223ca1c9b0cSelric     if (s == NULL)
224ca1c9b0cSelric 	return KERN_MEMORY_FAILURE; /* XXX */
225ca1c9b0cSelric 
226ca1c9b0cSelric     s->reply_port = reply_port;
227ca1c9b0cSelric 
228ca1c9b0cSelric     audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
229ca1c9b0cSelric 
230ca1c9b0cSelric     kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
231ca1c9b0cSelric     if (kr) {
232ca1c9b0cSelric 	free(s);
233ca1c9b0cSelric 	return kr;
234ca1c9b0cSelric     }
235ca1c9b0cSelric 
236ca1c9b0cSelric     suspend_timer();
237ca1c9b0cSelric 
238ca1c9b0cSelric     if (requestinCnt) {
239ca1c9b0cSelric 	s->req.data = malloc(requestinCnt);
240ca1c9b0cSelric 	memcpy(s->req.data, requestin, requestinCnt);
241ca1c9b0cSelric 	s->req.length = requestinCnt;
242ca1c9b0cSelric     } else {
243ca1c9b0cSelric 	s->req.data = malloc(requestoutCnt);
244ca1c9b0cSelric 	memcpy(s->req.data, requestout, requestoutCnt);
245ca1c9b0cSelric 	s->req.length = requestoutCnt;
246ca1c9b0cSelric     }
247ca1c9b0cSelric 
248ca1c9b0cSelric     dispatch_async(workq, ^{
249ca1c9b0cSelric 	(ctx->callback)(ctx->userctx, &s->req, s->cred,
250ca1c9b0cSelric 			mach_complete_sync, (heim_sipc_call)s);
251ca1c9b0cSelric     });
252ca1c9b0cSelric 
253ca1c9b0cSelric     return MIG_NO_REPLY;
254ca1c9b0cSelric }
255ca1c9b0cSelric 
256ca1c9b0cSelric kern_return_t
mheim_do_call_request(mach_port_t server_port,audit_token_t client_creds,mach_port_t reply_port,heim_ipc_message_inband_t requestin,mach_msg_type_number_t requestinCnt,heim_ipc_message_outband_t requestout,mach_msg_type_number_t requestoutCnt)257ca1c9b0cSelric mheim_do_call_request(mach_port_t server_port,
258ca1c9b0cSelric 		      audit_token_t client_creds,
259ca1c9b0cSelric 		      mach_port_t reply_port,
260ca1c9b0cSelric 		      heim_ipc_message_inband_t requestin,
261ca1c9b0cSelric 		      mach_msg_type_number_t requestinCnt,
262ca1c9b0cSelric 		      heim_ipc_message_outband_t requestout,
263ca1c9b0cSelric 		      mach_msg_type_number_t requestoutCnt)
264ca1c9b0cSelric {
265ca1c9b0cSelric     heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
266ca1c9b0cSelric     struct mach_call_ctx *s;
267ca1c9b0cSelric     kern_return_t kr;
268ca1c9b0cSelric     uid_t uid;
269ca1c9b0cSelric     gid_t gid;
270ca1c9b0cSelric     pid_t pid;
271ca1c9b0cSelric     au_asid_t session;
272ca1c9b0cSelric 
273ca1c9b0cSelric     s = malloc(sizeof(*s));
274ca1c9b0cSelric     if (s == NULL)
275ca1c9b0cSelric 	return KERN_MEMORY_FAILURE; /* XXX */
276ca1c9b0cSelric 
277ca1c9b0cSelric     s->reply_port = reply_port;
278ca1c9b0cSelric 
279ca1c9b0cSelric     audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
280ca1c9b0cSelric 
281ca1c9b0cSelric     kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
282ca1c9b0cSelric     if (kr) {
283ca1c9b0cSelric 	free(s);
284ca1c9b0cSelric 	return kr;
285ca1c9b0cSelric     }
286ca1c9b0cSelric 
287ca1c9b0cSelric     suspend_timer();
288ca1c9b0cSelric 
289ca1c9b0cSelric     if (requestinCnt) {
290ca1c9b0cSelric 	s->req.data = malloc(requestinCnt);
291ca1c9b0cSelric 	memcpy(s->req.data, requestin, requestinCnt);
292ca1c9b0cSelric 	s->req.length = requestinCnt;
293ca1c9b0cSelric     } else {
294ca1c9b0cSelric 	s->req.data = malloc(requestoutCnt);
295ca1c9b0cSelric 	memcpy(s->req.data, requestout, requestoutCnt);
296ca1c9b0cSelric 	s->req.length = requestoutCnt;
297ca1c9b0cSelric     }
298ca1c9b0cSelric 
299ca1c9b0cSelric     dispatch_async(workq, ^{
300ca1c9b0cSelric 	(ctx->callback)(ctx->userctx, &s->req, s->cred,
301ca1c9b0cSelric 			mach_complete_async, (heim_sipc_call)s);
302ca1c9b0cSelric     });
303ca1c9b0cSelric 
304ca1c9b0cSelric     return KERN_SUCCESS;
305ca1c9b0cSelric }
306ca1c9b0cSelric 
307ca1c9b0cSelric static int
mach_init(const char * service,mach_port_t sport,heim_sipc ctx)308ca1c9b0cSelric mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
309ca1c9b0cSelric {
310ca1c9b0cSelric     struct mach_service *s;
311ca1c9b0cSelric     char *name;
312ca1c9b0cSelric 
313ca1c9b0cSelric     init_globals();
314ca1c9b0cSelric 
315ca1c9b0cSelric     s = calloc(1, sizeof(*s));
316ca1c9b0cSelric     if (s == NULL)
317ca1c9b0cSelric 	return ENOMEM;
318ca1c9b0cSelric 
319ca1c9b0cSelric     asprintf(&name, "heim-ipc-mach-%s", service);
320ca1c9b0cSelric 
321ca1c9b0cSelric     s->queue = dispatch_queue_create(name, NULL);
322ca1c9b0cSelric     free(name);
323ca1c9b0cSelric     s->sport = sport;
324ca1c9b0cSelric 
325ca1c9b0cSelric     s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
326ca1c9b0cSelric 				       s->sport, 0, s->queue);
327ca1c9b0cSelric     if (s->source == NULL) {
328ca1c9b0cSelric 	dispatch_release(s->queue);
329ca1c9b0cSelric 	free(s);
330ca1c9b0cSelric 	return ENOMEM;
331ca1c9b0cSelric     }
332ca1c9b0cSelric     ctx->mech = s;
333ca1c9b0cSelric 
334ca1c9b0cSelric     dispatch_set_context(s->queue, ctx);
335ca1c9b0cSelric     dispatch_set_context(s->source, s);
336ca1c9b0cSelric 
337ca1c9b0cSelric     dispatch_source_set_event_handler(s->source, ^{
338ca1c9b0cSelric 	    dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
339ca1c9b0cSelric 	});
340ca1c9b0cSelric 
341ca1c9b0cSelric     dispatch_source_set_cancel_handler(s->source, ^{
342b9d004c6Schristos 	    heim_sipc sctx = dispatch_get_context(dispatch_get_current_queue());
343b9d004c6Schristos 	    struct mach_service *st = sctx->mech;
344ca1c9b0cSelric 	    mach_port_mod_refs(mach_task_self(), st->sport,
345ca1c9b0cSelric 			       MACH_PORT_RIGHT_RECEIVE, -1);
346ca1c9b0cSelric 	    dispatch_release(st->queue);
347ca1c9b0cSelric 	    dispatch_release(st->source);
348ca1c9b0cSelric 	    free(st);
349b9d004c6Schristos 	    free(sctx);
350ca1c9b0cSelric 	});
351ca1c9b0cSelric 
352ca1c9b0cSelric     dispatch_resume(s->source);
353ca1c9b0cSelric 
354ca1c9b0cSelric     return 0;
355ca1c9b0cSelric }
356ca1c9b0cSelric 
357ca1c9b0cSelric static int
mach_release(heim_sipc ctx)358ca1c9b0cSelric mach_release(heim_sipc ctx)
359ca1c9b0cSelric {
360ca1c9b0cSelric     struct mach_service *s = ctx->mech;
361ca1c9b0cSelric     dispatch_source_cancel(s->source);
362ca1c9b0cSelric     dispatch_release(s->source);
363ca1c9b0cSelric     return 0;
364ca1c9b0cSelric }
365ca1c9b0cSelric 
366ca1c9b0cSelric static mach_port_t
mach_checkin_or_register(const char * service)367ca1c9b0cSelric mach_checkin_or_register(const char *service)
368ca1c9b0cSelric {
369ca1c9b0cSelric     mach_port_t mp;
370ca1c9b0cSelric     kern_return_t kr;
371ca1c9b0cSelric 
372ca1c9b0cSelric     kr = bootstrap_check_in(bootstrap_port, service, &mp);
373ca1c9b0cSelric     if (kr == KERN_SUCCESS)
374ca1c9b0cSelric 	return mp;
375ca1c9b0cSelric 
376ca1c9b0cSelric #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
377ca1c9b0cSelric     /* Pre SnowLeopard version */
378ca1c9b0cSelric     kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
379ca1c9b0cSelric     if (kr != KERN_SUCCESS)
380ca1c9b0cSelric 	return MACH_PORT_NULL;
381ca1c9b0cSelric 
382ca1c9b0cSelric     kr = mach_port_insert_right(mach_task_self(), mp, mp,
383ca1c9b0cSelric 				MACH_MSG_TYPE_MAKE_SEND);
384ca1c9b0cSelric     if (kr != KERN_SUCCESS) {
385ca1c9b0cSelric 	mach_port_destroy(mach_task_self(), mp);
386ca1c9b0cSelric 	return MACH_PORT_NULL;
387ca1c9b0cSelric     }
388ca1c9b0cSelric 
389ca1c9b0cSelric     kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
390ca1c9b0cSelric     if (kr != KERN_SUCCESS) {
391ca1c9b0cSelric 	mach_port_destroy(mach_task_self(), mp);
392ca1c9b0cSelric 	return MACH_PORT_NULL;
393ca1c9b0cSelric     }
394ca1c9b0cSelric 
395ca1c9b0cSelric     return mp;
396ca1c9b0cSelric #else
397ca1c9b0cSelric     return MACH_PORT_NULL;
398ca1c9b0cSelric #endif
399ca1c9b0cSelric }
400ca1c9b0cSelric 
401ca1c9b0cSelric 
402ca1c9b0cSelric #endif /* __APPLE__ && HAVE_GCD */
403ca1c9b0cSelric 
404ca1c9b0cSelric 
405ca1c9b0cSelric int
heim_sipc_launchd_mach_init(const char * service,heim_ipc_callback callback,void * user,heim_sipc * ctx)406ca1c9b0cSelric heim_sipc_launchd_mach_init(const char *service,
407ca1c9b0cSelric 			    heim_ipc_callback callback,
408ca1c9b0cSelric 			    void *user, heim_sipc *ctx)
409ca1c9b0cSelric {
410ca1c9b0cSelric #if defined(__APPLE__) && defined(HAVE_GCD)
411ca1c9b0cSelric     mach_port_t sport = MACH_PORT_NULL;
412ca1c9b0cSelric     heim_sipc c = NULL;
413ca1c9b0cSelric     int ret;
414ca1c9b0cSelric 
415ca1c9b0cSelric     *ctx = NULL;
416ca1c9b0cSelric 
417ca1c9b0cSelric     sport = mach_checkin_or_register(service);
418ca1c9b0cSelric     if (sport == MACH_PORT_NULL) {
419ca1c9b0cSelric 	ret = ENOENT;
420ca1c9b0cSelric 	goto error;
421ca1c9b0cSelric     }
422ca1c9b0cSelric 
423ca1c9b0cSelric     c = calloc(1, sizeof(*c));
424ca1c9b0cSelric     if (c == NULL) {
425ca1c9b0cSelric 	ret = ENOMEM;
426ca1c9b0cSelric 	goto error;
427ca1c9b0cSelric     }
428ca1c9b0cSelric     c->release = mach_release;
429ca1c9b0cSelric     c->userctx = user;
430ca1c9b0cSelric     c->callback = callback;
431ca1c9b0cSelric 
432ca1c9b0cSelric     ret = mach_init(service, sport, c);
433ca1c9b0cSelric     if (ret)
434ca1c9b0cSelric 	goto error;
435ca1c9b0cSelric 
436ca1c9b0cSelric     *ctx = c;
437ca1c9b0cSelric     return 0;
438ca1c9b0cSelric  error:
439ca1c9b0cSelric     if (c)
440ca1c9b0cSelric 	free(c);
441ca1c9b0cSelric     if (sport != MACH_PORT_NULL)
442ca1c9b0cSelric 	mach_port_mod_refs(mach_task_self(), sport,
443ca1c9b0cSelric 			   MACH_PORT_RIGHT_RECEIVE, -1);
444ca1c9b0cSelric     return ret;
445ca1c9b0cSelric #else /* !(__APPLE__ && HAVE_GCD) */
446ca1c9b0cSelric     *ctx = NULL;
447ca1c9b0cSelric     return EINVAL;
448ca1c9b0cSelric #endif /* __APPLE__ && HAVE_GCD */
449ca1c9b0cSelric }
450ca1c9b0cSelric 
451ca1c9b0cSelric struct client {
452ca1c9b0cSelric     int fd;
453ca1c9b0cSelric     heim_ipc_callback callback;
454ca1c9b0cSelric     void *userctx;
455ca1c9b0cSelric     int flags;
456ca1c9b0cSelric #define LISTEN_SOCKET	1
457ca1c9b0cSelric #define WAITING_READ	2
458ca1c9b0cSelric #define WAITING_WRITE	4
459ca1c9b0cSelric #define WAITING_CLOSE	8
460ca1c9b0cSelric 
461ca1c9b0cSelric #define HTTP_REPLY	16
462ca1c9b0cSelric 
463ca1c9b0cSelric #define INHERIT_MASK	0xffff0000
464ca1c9b0cSelric #define INCLUDE_ERROR_CODE (1 << 16)
465ca1c9b0cSelric #define ALLOW_HTTP	(1<<17)
466ca1c9b0cSelric #define UNIX_SOCKET	(1<<18)
467ca1c9b0cSelric     unsigned calls;
468ca1c9b0cSelric     size_t ptr, len;
469ca1c9b0cSelric     uint8_t *inmsg;
470ca1c9b0cSelric     size_t olen;
471ca1c9b0cSelric     uint8_t *outmsg;
472ca1c9b0cSelric #ifdef HAVE_GCD
473ca1c9b0cSelric     dispatch_source_t in;
474ca1c9b0cSelric     dispatch_source_t out;
475ca1c9b0cSelric #endif
476ca1c9b0cSelric     struct {
477ca1c9b0cSelric 	uid_t uid;
478ca1c9b0cSelric 	gid_t gid;
479ca1c9b0cSelric 	pid_t pid;
480ca1c9b0cSelric     } unixrights;
481ca1c9b0cSelric };
482ca1c9b0cSelric 
483ca1c9b0cSelric #ifndef HAVE_GCD
484ca1c9b0cSelric static unsigned num_clients = 0;
485ca1c9b0cSelric static struct client **clients = NULL;
486ca1c9b0cSelric #endif
487ca1c9b0cSelric 
488ca1c9b0cSelric static void handle_read(struct client *);
489ca1c9b0cSelric static void handle_write(struct client *);
490ca1c9b0cSelric static int maybe_close(struct client *);
491ca1c9b0cSelric 
492ca1c9b0cSelric /*
493ca1c9b0cSelric  * Update peer credentials from socket.
494ca1c9b0cSelric  *
495ca1c9b0cSelric  * SCM_CREDS can only be updated the first time there is read data to
496ca1c9b0cSelric  * read from the filedescriptor, so if we read do it before this
497ca1c9b0cSelric  * point, the cred data might not be is not there yet.
498ca1c9b0cSelric  */
499ca1c9b0cSelric 
500ca1c9b0cSelric static int
update_client_creds(struct client * c)501ca1c9b0cSelric update_client_creds(struct client *c)
502ca1c9b0cSelric {
503ca1c9b0cSelric #ifdef HAVE_GETPEERUCRED
504ca1c9b0cSelric     /* Solaris 10 */
505ca1c9b0cSelric     {
506ca1c9b0cSelric 	ucred_t *peercred;
507ca1c9b0cSelric 
508ca1c9b0cSelric 	if (getpeerucred(c->fd, &peercred) != 0) {
509ca1c9b0cSelric 	    c->unixrights.uid = ucred_geteuid(peercred);
510ca1c9b0cSelric 	    c->unixrights.gid = ucred_getegid(peercred);
511ca1c9b0cSelric 	    c->unixrights.pid = 0;
512ca1c9b0cSelric 	    ucred_free(peercred);
513ca1c9b0cSelric 	    return 1;
514ca1c9b0cSelric 	}
515ca1c9b0cSelric     }
516ca1c9b0cSelric #endif
517ca1c9b0cSelric #ifdef HAVE_GETPEEREID
518ca1c9b0cSelric     /* FreeBSD, OpenBSD */
519ca1c9b0cSelric     {
520ca1c9b0cSelric 	uid_t uid;
521ca1c9b0cSelric 	gid_t gid;
522ca1c9b0cSelric 
523ca1c9b0cSelric 	if (getpeereid(c->fd, &uid, &gid) == 0) {
524ca1c9b0cSelric 	    c->unixrights.uid = uid;
525ca1c9b0cSelric 	    c->unixrights.gid = gid;
526ca1c9b0cSelric 	    c->unixrights.pid = 0;
527ca1c9b0cSelric 	    return 1;
528ca1c9b0cSelric 	}
529ca1c9b0cSelric     }
530ca1c9b0cSelric #endif
5314f77a458Spettai #if defined(SO_PEERCRED) && defined(__linux__)
532ca1c9b0cSelric     /* Linux */
533ca1c9b0cSelric     {
534ca1c9b0cSelric 	struct ucred pc;
535ca1c9b0cSelric 	socklen_t pclen = sizeof(pc);
536ca1c9b0cSelric 
537ca1c9b0cSelric 	if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
538ca1c9b0cSelric 	    c->unixrights.uid = pc.uid;
539ca1c9b0cSelric 	    c->unixrights.gid = pc.gid;
540ca1c9b0cSelric 	    c->unixrights.pid = pc.pid;
541ca1c9b0cSelric 	    return 1;
542ca1c9b0cSelric 	}
543ca1c9b0cSelric     }
544ca1c9b0cSelric #endif
545ca1c9b0cSelric #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
546ca1c9b0cSelric     {
547ca1c9b0cSelric 	struct xucred peercred;
548ca1c9b0cSelric 	socklen_t peercredlen = sizeof(peercred);
549ca1c9b0cSelric 
550ca1c9b0cSelric 	if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
551ca1c9b0cSelric 		       (void *)&peercred, &peercredlen) == 0
552ca1c9b0cSelric 	    && peercred.cr_version == XUCRED_VERSION)
553ca1c9b0cSelric 	{
554ca1c9b0cSelric 	    c->unixrights.uid = peercred.cr_uid;
555ca1c9b0cSelric 	    c->unixrights.gid = peercred.cr_gid;
556ca1c9b0cSelric 	    c->unixrights.pid = 0;
557ca1c9b0cSelric 	    return 1;
558ca1c9b0cSelric 	}
559ca1c9b0cSelric     }
560ca1c9b0cSelric #endif
561ca1c9b0cSelric #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
562ca1c9b0cSelric     /* NetBSD */
5634f77a458Spettai     if (c->unixrights.uid == (uid_t)-1) {
564ca1c9b0cSelric 	struct msghdr msg;
565ca1c9b0cSelric 	socklen_t crmsgsize;
566ca1c9b0cSelric 	void *crmsg;
567ca1c9b0cSelric 	struct cmsghdr *cmp;
568ca1c9b0cSelric 	struct sockcred *sc;
569ca1c9b0cSelric 
570ca1c9b0cSelric 	memset(&msg, 0, sizeof(msg));
571ca1c9b0cSelric 	crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
572ca1c9b0cSelric 	if (crmsgsize == 0)
573ca1c9b0cSelric 	    return 1 ;
574ca1c9b0cSelric 
575ca1c9b0cSelric 	crmsg = malloc(crmsgsize);
576ca1c9b0cSelric 	if (crmsg == NULL)
577ca1c9b0cSelric 	    goto failed_scm_creds;
578ca1c9b0cSelric 
579ca1c9b0cSelric 	memset(crmsg, 0, crmsgsize);
580ca1c9b0cSelric 
581ca1c9b0cSelric 	msg.msg_control = crmsg;
582ca1c9b0cSelric 	msg.msg_controllen = crmsgsize;
583ca1c9b0cSelric 
584ca1c9b0cSelric 	if (recvmsg(c->fd, &msg, 0) < 0) {
585ca1c9b0cSelric 	    free(crmsg);
586ca1c9b0cSelric 	    goto failed_scm_creds;
587ca1c9b0cSelric 	}
588ca1c9b0cSelric 
589ca1c9b0cSelric 	if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
590ca1c9b0cSelric 	    free(crmsg);
591ca1c9b0cSelric 	    goto failed_scm_creds;
592ca1c9b0cSelric 	}
593ca1c9b0cSelric 
594ca1c9b0cSelric 	cmp = CMSG_FIRSTHDR(&msg);
595ca1c9b0cSelric 	if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
596ca1c9b0cSelric 	    free(crmsg);
597ca1c9b0cSelric 	    goto failed_scm_creds;
598ca1c9b0cSelric 	}
599ca1c9b0cSelric 
600ca1c9b0cSelric 	sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
601ca1c9b0cSelric 
602ca1c9b0cSelric 	c->unixrights.uid = sc->sc_euid;
603ca1c9b0cSelric 	c->unixrights.gid = sc->sc_egid;
604ca1c9b0cSelric 	c->unixrights.pid = 0;
605ca1c9b0cSelric 
606ca1c9b0cSelric 	free(crmsg);
607ca1c9b0cSelric 	return 1;
608ca1c9b0cSelric     } else {
609ca1c9b0cSelric 	/* we already got the cred, just return it */
610ca1c9b0cSelric 	return 1;
611ca1c9b0cSelric     }
612ca1c9b0cSelric  failed_scm_creds:
613ca1c9b0cSelric #endif
614ca1c9b0cSelric     return 0;
615ca1c9b0cSelric }
616ca1c9b0cSelric 
617ca1c9b0cSelric 
618ca1c9b0cSelric static struct client *
add_new_socket(int fd,int flags,heim_ipc_callback callback,void * userctx)619ca1c9b0cSelric add_new_socket(int fd,
620ca1c9b0cSelric 	       int flags,
621ca1c9b0cSelric 	       heim_ipc_callback callback,
622ca1c9b0cSelric 	       void *userctx)
623ca1c9b0cSelric {
624ca1c9b0cSelric     struct client *c;
625ca1c9b0cSelric     int fileflags;
626ca1c9b0cSelric 
627ca1c9b0cSelric     c = calloc(1, sizeof(*c));
628ca1c9b0cSelric     if (c == NULL)
629ca1c9b0cSelric 	return NULL;
630ca1c9b0cSelric 
631ca1c9b0cSelric     if (flags & LISTEN_SOCKET) {
632ca1c9b0cSelric 	c->fd = fd;
633ca1c9b0cSelric     } else {
634ca1c9b0cSelric 	c->fd = accept(fd, NULL, NULL);
635ca1c9b0cSelric 	if(c->fd < 0) {
636ca1c9b0cSelric 	    free(c);
637ca1c9b0cSelric 	    return NULL;
638ca1c9b0cSelric 	}
639ca1c9b0cSelric     }
640ca1c9b0cSelric 
641ca1c9b0cSelric     c->flags = flags;
642ca1c9b0cSelric     c->callback = callback;
643ca1c9b0cSelric     c->userctx = userctx;
644ca1c9b0cSelric 
645ca1c9b0cSelric     fileflags = fcntl(c->fd, F_GETFL, 0);
646ca1c9b0cSelric     fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
647ca1c9b0cSelric 
648ca1c9b0cSelric #ifdef HAVE_GCD
649ca1c9b0cSelric     init_globals();
650ca1c9b0cSelric 
651ca1c9b0cSelric     c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
652ca1c9b0cSelric 				   c->fd, 0, eventq);
653ca1c9b0cSelric     c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
654ca1c9b0cSelric 				    c->fd, 0, eventq);
655ca1c9b0cSelric 
656ca1c9b0cSelric     dispatch_source_set_event_handler(c->in, ^{
657ca1c9b0cSelric 	    int rw = (c->flags & WAITING_WRITE);
658ca1c9b0cSelric 	    handle_read(c);
659ca1c9b0cSelric 	    if (rw == 0 && (c->flags & WAITING_WRITE))
660ca1c9b0cSelric 		dispatch_resume(c->out);
661ca1c9b0cSelric 	    if ((c->flags & WAITING_READ) == 0)
662ca1c9b0cSelric 		dispatch_suspend(c->in);
663ca1c9b0cSelric 	    maybe_close(c);
664ca1c9b0cSelric 	});
665ca1c9b0cSelric     dispatch_source_set_event_handler(c->out, ^{
666ca1c9b0cSelric 	    handle_write(c);
667ca1c9b0cSelric 	    if ((c->flags & WAITING_WRITE) == 0) {
668ca1c9b0cSelric 		dispatch_suspend(c->out);
669ca1c9b0cSelric 	    }
670ca1c9b0cSelric 	    maybe_close(c);
671ca1c9b0cSelric 	});
672ca1c9b0cSelric 
673ca1c9b0cSelric     dispatch_resume(c->in);
674ca1c9b0cSelric #else
675ca1c9b0cSelric     clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
676ca1c9b0cSelric     clients[num_clients] = c;
677ca1c9b0cSelric     num_clients++;
678ca1c9b0cSelric #endif
679ca1c9b0cSelric 
680ca1c9b0cSelric     return c;
681ca1c9b0cSelric }
682ca1c9b0cSelric 
683ca1c9b0cSelric static int
maybe_close(struct client * c)684ca1c9b0cSelric maybe_close(struct client *c)
685ca1c9b0cSelric {
686ca1c9b0cSelric     if (c->calls != 0)
687ca1c9b0cSelric 	return 0;
688ca1c9b0cSelric     if (c->flags & (WAITING_READ|WAITING_WRITE))
689ca1c9b0cSelric 	return 0;
690ca1c9b0cSelric 
691ca1c9b0cSelric #ifdef HAVE_GCD
692ca1c9b0cSelric     dispatch_source_cancel(c->in);
693ca1c9b0cSelric     if ((c->flags & WAITING_READ) == 0)
694ca1c9b0cSelric 	dispatch_resume(c->in);
695ca1c9b0cSelric     dispatch_release(c->in);
696ca1c9b0cSelric 
697ca1c9b0cSelric     dispatch_source_cancel(c->out);
698ca1c9b0cSelric     if ((c->flags & WAITING_WRITE) == 0)
699ca1c9b0cSelric 	dispatch_resume(c->out);
700ca1c9b0cSelric     dispatch_release(c->out);
701ca1c9b0cSelric #endif
702ca1c9b0cSelric     close(c->fd); /* ref count fd close */
703ca1c9b0cSelric     free(c);
704ca1c9b0cSelric     return 1;
705ca1c9b0cSelric }
706ca1c9b0cSelric 
707ca1c9b0cSelric 
708ca1c9b0cSelric struct socket_call {
709ca1c9b0cSelric     heim_idata in;
710ca1c9b0cSelric     struct client *c;
711ca1c9b0cSelric     heim_icred cred;
712ca1c9b0cSelric };
713ca1c9b0cSelric 
714ca1c9b0cSelric static void
output_data(struct client * c,const void * data,size_t len)715ca1c9b0cSelric output_data(struct client *c, const void *data, size_t len)
716ca1c9b0cSelric {
717ca1c9b0cSelric     if (c->olen + len < c->olen)
718ca1c9b0cSelric 	abort();
719ca1c9b0cSelric     c->outmsg = erealloc(c->outmsg, c->olen + len);
720ca1c9b0cSelric     memcpy(&c->outmsg[c->olen], data, len);
721ca1c9b0cSelric     c->olen += len;
722ca1c9b0cSelric     c->flags |= WAITING_WRITE;
723ca1c9b0cSelric }
724ca1c9b0cSelric 
725ca1c9b0cSelric static void
socket_complete(heim_sipc_call ctx,int returnvalue,heim_idata * reply)726ca1c9b0cSelric socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
727ca1c9b0cSelric {
728ca1c9b0cSelric     struct socket_call *sc = (struct socket_call *)ctx;
729ca1c9b0cSelric     struct client *c = sc->c;
730ca1c9b0cSelric 
731ca1c9b0cSelric     /* double complete ? */
732ca1c9b0cSelric     if (c == NULL)
733ca1c9b0cSelric 	abort();
734ca1c9b0cSelric 
735ca1c9b0cSelric     if ((c->flags & WAITING_CLOSE) == 0) {
736ca1c9b0cSelric 	uint32_t u32;
737ca1c9b0cSelric 
738ca1c9b0cSelric 	/* length */
739ca1c9b0cSelric 	u32 = htonl(reply->length);
740ca1c9b0cSelric 	output_data(c, &u32, sizeof(u32));
741ca1c9b0cSelric 
742ca1c9b0cSelric 	/* return value */
743ca1c9b0cSelric 	if (c->flags & INCLUDE_ERROR_CODE) {
744ca1c9b0cSelric 	    u32 = htonl(returnvalue);
745ca1c9b0cSelric 	    output_data(c, &u32, sizeof(u32));
746ca1c9b0cSelric 	}
747ca1c9b0cSelric 
748ca1c9b0cSelric 	/* data */
749ca1c9b0cSelric 	output_data(c, reply->data, reply->length);
750ca1c9b0cSelric 
751ca1c9b0cSelric 	/* if HTTP, close connection */
752ca1c9b0cSelric 	if (c->flags & HTTP_REPLY) {
753ca1c9b0cSelric 	    c->flags |= WAITING_CLOSE;
754ca1c9b0cSelric 	    c->flags &= ~WAITING_READ;
755ca1c9b0cSelric 	}
756ca1c9b0cSelric     }
757ca1c9b0cSelric 
758ca1c9b0cSelric     c->calls--;
759ca1c9b0cSelric     if (sc->cred)
760ca1c9b0cSelric 	heim_ipc_free_cred(sc->cred);
761ca1c9b0cSelric     free(sc->in.data);
762ca1c9b0cSelric     sc->c = NULL; /* so we can catch double complete */
763ca1c9b0cSelric     free(sc);
764ca1c9b0cSelric 
765ca1c9b0cSelric     maybe_close(c);
766ca1c9b0cSelric }
767ca1c9b0cSelric 
768ca1c9b0cSelric /* remove HTTP %-quoting from buf */
769ca1c9b0cSelric static int
de_http(char * buf)770ca1c9b0cSelric de_http(char *buf)
771ca1c9b0cSelric {
772ca1c9b0cSelric     unsigned char *p, *q;
773ca1c9b0cSelric     for(p = q = (unsigned char *)buf; *p; p++, q++) {
774ca1c9b0cSelric 	if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
775ca1c9b0cSelric 	    unsigned int x;
776ca1c9b0cSelric 	    if(sscanf((char *)p + 1, "%2x", &x) != 1)
777ca1c9b0cSelric 		return -1;
778ca1c9b0cSelric 	    *q = x;
779ca1c9b0cSelric 	    p += 2;
780ca1c9b0cSelric 	} else
781ca1c9b0cSelric 	    *q = *p;
782ca1c9b0cSelric     }
783ca1c9b0cSelric     *q = '\0';
784ca1c9b0cSelric     return 0;
785ca1c9b0cSelric }
786ca1c9b0cSelric 
787ca1c9b0cSelric static struct socket_call *
handle_http_tcp(struct client * c)788ca1c9b0cSelric handle_http_tcp(struct client *c)
789ca1c9b0cSelric {
790ca1c9b0cSelric     struct socket_call *cs;
791ca1c9b0cSelric     char *s, *p, *t;
792ca1c9b0cSelric     void *data;
793ca1c9b0cSelric     char *proto;
794ca1c9b0cSelric     int len;
795ca1c9b0cSelric 
796ca1c9b0cSelric     s = (char *)c->inmsg;
797ca1c9b0cSelric 
798ca1c9b0cSelric     p = strstr(s, "\r\n");
799ca1c9b0cSelric     if (p == NULL)
800ca1c9b0cSelric 	return NULL;
801ca1c9b0cSelric 
802ca1c9b0cSelric     *p = 0;
803ca1c9b0cSelric 
804ca1c9b0cSelric     p = NULL;
805ca1c9b0cSelric     t = strtok_r(s, " \t", &p);
806ca1c9b0cSelric     if (t == NULL)
807ca1c9b0cSelric 	return NULL;
808ca1c9b0cSelric 
809ca1c9b0cSelric     t = strtok_r(NULL, " \t", &p);
810ca1c9b0cSelric     if (t == NULL)
811ca1c9b0cSelric 	return NULL;
812ca1c9b0cSelric 
813ca1c9b0cSelric     data = malloc(strlen(t));
814ca1c9b0cSelric     if (data == NULL)
815ca1c9b0cSelric 	return NULL;
816ca1c9b0cSelric 
817ca1c9b0cSelric     if(*t == '/')
818ca1c9b0cSelric 	t++;
819ca1c9b0cSelric     if(de_http(t) != 0) {
820ca1c9b0cSelric 	free(data);
821ca1c9b0cSelric 	return NULL;
822ca1c9b0cSelric     }
823ca1c9b0cSelric     proto = strtok_r(NULL, " \t", &p);
824ca1c9b0cSelric     if (proto == NULL) {
825ca1c9b0cSelric 	free(data);
826ca1c9b0cSelric 	return NULL;
827ca1c9b0cSelric     }
828b9d004c6Schristos     len = rk_base64_decode(t, data);
829ca1c9b0cSelric     if(len <= 0){
830ca1c9b0cSelric 	const char *msg =
831ca1c9b0cSelric 	    " 404 Not found\r\n"
832ca1c9b0cSelric 	    "Server: Heimdal/" VERSION "\r\n"
833ca1c9b0cSelric 	    "Cache-Control: no-cache\r\n"
834ca1c9b0cSelric 	    "Pragma: no-cache\r\n"
835ca1c9b0cSelric 	    "Content-type: text/html\r\n"
836ca1c9b0cSelric 	    "Content-transfer-encoding: 8bit\r\n\r\n"
837ca1c9b0cSelric 	    "<TITLE>404 Not found</TITLE>\r\n"
838ca1c9b0cSelric 	    "<H1>404 Not found</H1>\r\n"
839ca1c9b0cSelric 	    "That page doesn't exist, maybe you are looking for "
840ca1c9b0cSelric 	    "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
841ca1c9b0cSelric 	free(data);
842ca1c9b0cSelric 	output_data(c, proto, strlen(proto));
843ca1c9b0cSelric 	output_data(c, msg, strlen(msg));
844ca1c9b0cSelric 	return NULL;
845ca1c9b0cSelric     }
846ca1c9b0cSelric 
847ca1c9b0cSelric     cs = emalloc(sizeof(*cs));
848ca1c9b0cSelric     cs->c = c;
849ca1c9b0cSelric     cs->in.data = data;
850ca1c9b0cSelric     cs->in.length = len;
851ca1c9b0cSelric     c->ptr = 0;
852ca1c9b0cSelric 
853ca1c9b0cSelric     {
854ca1c9b0cSelric 	const char *msg =
855ca1c9b0cSelric 	    " 200 OK\r\n"
856ca1c9b0cSelric 	    "Server: Heimdal/" VERSION "\r\n"
857ca1c9b0cSelric 	    "Cache-Control: no-cache\r\n"
858ca1c9b0cSelric 	    "Pragma: no-cache\r\n"
859ca1c9b0cSelric 	    "Content-type: application/octet-stream\r\n"
860ca1c9b0cSelric 	    "Content-transfer-encoding: binary\r\n\r\n";
861ca1c9b0cSelric 	output_data(c, proto, strlen(proto));
862ca1c9b0cSelric 	output_data(c, msg, strlen(msg));
863ca1c9b0cSelric     }
864ca1c9b0cSelric 
865ca1c9b0cSelric     return cs;
866ca1c9b0cSelric }
867ca1c9b0cSelric 
868ca1c9b0cSelric 
869ca1c9b0cSelric static void
handle_read(struct client * c)870ca1c9b0cSelric handle_read(struct client *c)
871ca1c9b0cSelric {
872ca1c9b0cSelric     ssize_t len;
873ca1c9b0cSelric     uint32_t dlen;
874ca1c9b0cSelric 
875ca1c9b0cSelric     if (c->flags & LISTEN_SOCKET) {
876ca1c9b0cSelric 	add_new_socket(c->fd,
877ca1c9b0cSelric 		       WAITING_READ | (c->flags & INHERIT_MASK),
878ca1c9b0cSelric 		       c->callback,
879ca1c9b0cSelric 		       c->userctx);
880ca1c9b0cSelric 	return;
881ca1c9b0cSelric     }
882ca1c9b0cSelric 
883ca1c9b0cSelric     if (c->ptr - c->len < 1024) {
884ca1c9b0cSelric 	c->inmsg = erealloc(c->inmsg,
885ca1c9b0cSelric 			    c->len + 1024);
886ca1c9b0cSelric 	c->len += 1024;
887ca1c9b0cSelric     }
888ca1c9b0cSelric 
889ca1c9b0cSelric     len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
890ca1c9b0cSelric     if (len <= 0) {
891ca1c9b0cSelric 	c->flags |= WAITING_CLOSE;
892ca1c9b0cSelric 	c->flags &= ~WAITING_READ;
893ca1c9b0cSelric 	return;
894ca1c9b0cSelric     }
895ca1c9b0cSelric     c->ptr += len;
896ca1c9b0cSelric     if (c->ptr > c->len)
897ca1c9b0cSelric 	abort();
898ca1c9b0cSelric 
899ca1c9b0cSelric     while (c->ptr >= sizeof(dlen)) {
900ca1c9b0cSelric 	struct socket_call *cs;
901ca1c9b0cSelric 
902ca1c9b0cSelric 	if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
903ca1c9b0cSelric 	   strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
904ca1c9b0cSelric 	   strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
905ca1c9b0cSelric 
906ca1c9b0cSelric 	    /* remove the trailing \r\n\r\n so the string is NUL terminated */
907ca1c9b0cSelric 	    c->inmsg[c->ptr - 4] = '\0';
908ca1c9b0cSelric 
909ca1c9b0cSelric 	    c->flags |= HTTP_REPLY;
910ca1c9b0cSelric 
911ca1c9b0cSelric 	    cs = handle_http_tcp(c);
912ca1c9b0cSelric 	    if (cs == NULL) {
913ca1c9b0cSelric 		c->flags |= WAITING_CLOSE;
914ca1c9b0cSelric 		c->flags &= ~WAITING_READ;
915ca1c9b0cSelric 		break;
916ca1c9b0cSelric 	    }
917ca1c9b0cSelric 	} else {
918ca1c9b0cSelric 	    memcpy(&dlen, c->inmsg, sizeof(dlen));
919ca1c9b0cSelric 	    dlen = ntohl(dlen);
920ca1c9b0cSelric 
921ca1c9b0cSelric 	    if (dlen > MAX_PACKET_SIZE) {
922ca1c9b0cSelric 		c->flags |= WAITING_CLOSE;
923ca1c9b0cSelric 		c->flags &= ~WAITING_READ;
924ca1c9b0cSelric 		return;
925ca1c9b0cSelric 	    }
926ca1c9b0cSelric 	    if (dlen > c->ptr - sizeof(dlen)) {
927ca1c9b0cSelric 		break;
928ca1c9b0cSelric 	    }
929ca1c9b0cSelric 
930ca1c9b0cSelric 	    cs = emalloc(sizeof(*cs));
931ca1c9b0cSelric 	    cs->c = c;
932ca1c9b0cSelric 	    cs->in.data = emalloc(dlen);
933ca1c9b0cSelric 	    memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
934ca1c9b0cSelric 	    cs->in.length = dlen;
935ca1c9b0cSelric 
936ca1c9b0cSelric 	    c->ptr -= sizeof(dlen) + dlen;
937ca1c9b0cSelric 	    memmove(c->inmsg,
938ca1c9b0cSelric 		    c->inmsg + sizeof(dlen) + dlen,
939ca1c9b0cSelric 		    c->ptr);
940ca1c9b0cSelric 	}
941ca1c9b0cSelric 
942ca1c9b0cSelric 	c->calls++;
943ca1c9b0cSelric 
944ca1c9b0cSelric 	if ((c->flags & UNIX_SOCKET) != 0) {
945ca1c9b0cSelric 	    if (update_client_creds(c))
946ca1c9b0cSelric 		_heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
947ca1c9b0cSelric 				      c->unixrights.pid, -1, &cs->cred);
948ca1c9b0cSelric 	}
949ca1c9b0cSelric 
950ca1c9b0cSelric 	c->callback(c->userctx, &cs->in,
951ca1c9b0cSelric 		    cs->cred, socket_complete,
952ca1c9b0cSelric 		    (heim_sipc_call)cs);
953ca1c9b0cSelric     }
954ca1c9b0cSelric }
955ca1c9b0cSelric 
956ca1c9b0cSelric static void
handle_write(struct client * c)957ca1c9b0cSelric handle_write(struct client *c)
958ca1c9b0cSelric {
959ca1c9b0cSelric     ssize_t len;
960ca1c9b0cSelric 
961ca1c9b0cSelric     len = write(c->fd, c->outmsg, c->olen);
962ca1c9b0cSelric     if (len <= 0) {
963ca1c9b0cSelric 	c->flags |= WAITING_CLOSE;
964ca1c9b0cSelric 	c->flags &= ~(WAITING_WRITE);
9654f77a458Spettai     } else if (c->olen != (size_t)len) {
966ca1c9b0cSelric 	memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
967ca1c9b0cSelric 	c->olen -= len;
968ca1c9b0cSelric     } else {
969ca1c9b0cSelric 	c->olen = 0;
970ca1c9b0cSelric 	free(c->outmsg);
971ca1c9b0cSelric 	c->outmsg = NULL;
972ca1c9b0cSelric 	c->flags &= ~(WAITING_WRITE);
973ca1c9b0cSelric     }
974ca1c9b0cSelric }
975ca1c9b0cSelric 
976ca1c9b0cSelric 
977ca1c9b0cSelric #ifndef HAVE_GCD
978ca1c9b0cSelric 
979ca1c9b0cSelric static void
process_loop(void)980ca1c9b0cSelric process_loop(void)
981ca1c9b0cSelric {
982ca1c9b0cSelric     struct pollfd *fds;
983ca1c9b0cSelric     unsigned n;
984ca1c9b0cSelric     unsigned num_fds;
985ca1c9b0cSelric 
986ca1c9b0cSelric     while (num_clients > 0) {
987ca1c9b0cSelric 
988ca1c9b0cSelric 	fds = malloc(num_clients * sizeof(fds[0]));
989ca1c9b0cSelric 	if(fds == NULL)
990ca1c9b0cSelric 	    abort();
991ca1c9b0cSelric 
992ca1c9b0cSelric 	num_fds = num_clients;
993ca1c9b0cSelric 
994ca1c9b0cSelric 	for (n = 0 ; n < num_fds; n++) {
995ca1c9b0cSelric 	    fds[n].fd = clients[n]->fd;
996ca1c9b0cSelric 	    fds[n].events = 0;
997ca1c9b0cSelric 	    if (clients[n]->flags & WAITING_READ)
998ca1c9b0cSelric 		fds[n].events |= POLLIN;
999ca1c9b0cSelric 	    if (clients[n]->flags & WAITING_WRITE)
1000ca1c9b0cSelric 		fds[n].events |= POLLOUT;
1001ca1c9b0cSelric 
1002ca1c9b0cSelric 	    fds[n].revents = 0;
1003ca1c9b0cSelric 	}
1004ca1c9b0cSelric 
1005b9d004c6Schristos 	while (poll(fds, num_fds, -1) == -1) {
1006b9d004c6Schristos             if (errno == EINTR || errno == EAGAIN)
1007b9d004c6Schristos                 continue;
1008b9d004c6Schristos             err(1, "poll(2) failed");
1009b9d004c6Schristos         }
1010ca1c9b0cSelric 
1011ca1c9b0cSelric 	for (n = 0 ; n < num_fds; n++) {
1012ca1c9b0cSelric 	    if (clients[n] == NULL)
1013ca1c9b0cSelric 		continue;
1014ca1c9b0cSelric 	    if (fds[n].revents & POLLERR) {
1015ca1c9b0cSelric 		clients[n]->flags |= WAITING_CLOSE;
1016ca1c9b0cSelric 		continue;
1017ca1c9b0cSelric 	    }
1018ca1c9b0cSelric 
1019ca1c9b0cSelric 	    if (fds[n].revents & POLLIN)
1020ca1c9b0cSelric 		handle_read(clients[n]);
1021ca1c9b0cSelric 	    if (fds[n].revents & POLLOUT)
1022ca1c9b0cSelric 		handle_write(clients[n]);
1023ca1c9b0cSelric 	}
1024ca1c9b0cSelric 
1025ca1c9b0cSelric 	n = 0;
1026ca1c9b0cSelric 	while (n < num_clients) {
1027ca1c9b0cSelric 	    struct client *c = clients[n];
1028ca1c9b0cSelric 	    if (maybe_close(c)) {
1029ca1c9b0cSelric 		if (n < num_clients - 1)
1030ca1c9b0cSelric 		    clients[n] = clients[num_clients - 1];
1031ca1c9b0cSelric 		num_clients--;
1032ca1c9b0cSelric 	    } else
1033ca1c9b0cSelric 		n++;
1034ca1c9b0cSelric 	}
1035ca1c9b0cSelric 
1036ca1c9b0cSelric 	free(fds);
1037ca1c9b0cSelric     }
1038ca1c9b0cSelric }
1039ca1c9b0cSelric 
1040ca1c9b0cSelric #endif
1041ca1c9b0cSelric 
1042ca1c9b0cSelric static int
socket_release(heim_sipc ctx)1043ca1c9b0cSelric socket_release(heim_sipc ctx)
1044ca1c9b0cSelric {
1045ca1c9b0cSelric     struct client *c = ctx->mech;
1046ca1c9b0cSelric     c->flags |= WAITING_CLOSE;
1047ca1c9b0cSelric     return 0;
1048ca1c9b0cSelric }
1049ca1c9b0cSelric 
1050ca1c9b0cSelric int
heim_sipc_stream_listener(int fd,int type,heim_ipc_callback callback,void * user,heim_sipc * ctx)1051ca1c9b0cSelric heim_sipc_stream_listener(int fd, int type,
1052ca1c9b0cSelric 			  heim_ipc_callback callback,
1053ca1c9b0cSelric 			  void *user, heim_sipc *ctx)
1054ca1c9b0cSelric {
1055ca1c9b0cSelric     heim_sipc ct = calloc(1, sizeof(*ct));
1056ca1c9b0cSelric     struct client *c;
1057ca1c9b0cSelric 
1058ca1c9b0cSelric     if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
1059ca1c9b0cSelric 	return EINVAL;
1060ca1c9b0cSelric 
1061ca1c9b0cSelric     switch (type) {
1062ca1c9b0cSelric     case HEIM_SIPC_TYPE_IPC:
1063ca1c9b0cSelric 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
1064ca1c9b0cSelric 	break;
1065ca1c9b0cSelric     case HEIM_SIPC_TYPE_UINT32:
1066ca1c9b0cSelric 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
1067ca1c9b0cSelric 	break;
1068ca1c9b0cSelric     case HEIM_SIPC_TYPE_HTTP:
1069ca1c9b0cSelric     case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
1070ca1c9b0cSelric 	c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
1071ca1c9b0cSelric 	break;
1072ca1c9b0cSelric     default:
1073ca1c9b0cSelric 	free(ct);
1074ca1c9b0cSelric 	return EINVAL;
1075ca1c9b0cSelric     }
1076ca1c9b0cSelric 
1077ca1c9b0cSelric     ct->mech = c;
1078ca1c9b0cSelric     ct->release = socket_release;
1079ca1c9b0cSelric 
1080ca1c9b0cSelric     c->unixrights.uid = (uid_t) -1;
1081ca1c9b0cSelric     c->unixrights.gid = (gid_t) -1;
1082ca1c9b0cSelric     c->unixrights.pid = (pid_t) 0;
1083ca1c9b0cSelric 
1084ca1c9b0cSelric     *ctx = ct;
1085ca1c9b0cSelric     return 0;
1086ca1c9b0cSelric }
1087ca1c9b0cSelric 
1088ca1c9b0cSelric int
heim_sipc_service_unix(const char * service,heim_ipc_callback callback,void * user,heim_sipc * ctx)1089ca1c9b0cSelric heim_sipc_service_unix(const char *service,
1090ca1c9b0cSelric 		       heim_ipc_callback callback,
1091ca1c9b0cSelric 		       void *user, heim_sipc *ctx)
1092ca1c9b0cSelric {
1093ca1c9b0cSelric     struct sockaddr_un un;
1094ca1c9b0cSelric     int fd, ret;
1095ca1c9b0cSelric 
1096ca1c9b0cSelric     un.sun_family = AF_UNIX;
1097ca1c9b0cSelric 
1098ca1c9b0cSelric     snprintf(un.sun_path, sizeof(un.sun_path),
1099ca1c9b0cSelric 	     "/var/run/.heim_%s-socket", service);
1100ca1c9b0cSelric     fd = socket(AF_UNIX, SOCK_STREAM, 0);
1101ca1c9b0cSelric     if (fd < 0)
1102ca1c9b0cSelric 	return errno;
1103ca1c9b0cSelric 
1104ca1c9b0cSelric     socket_set_reuseaddr(fd, 1);
1105ca1c9b0cSelric #ifdef LOCAL_CREDS
1106ca1c9b0cSelric     {
1107ca1c9b0cSelric 	int one = 1;
1108ca1c9b0cSelric 	setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
1109ca1c9b0cSelric     }
1110ca1c9b0cSelric #endif
1111ca1c9b0cSelric 
1112ca1c9b0cSelric     unlink(un.sun_path);
1113ca1c9b0cSelric 
1114ca1c9b0cSelric     if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
1115ca1c9b0cSelric 	close(fd);
1116ca1c9b0cSelric 	return errno;
1117ca1c9b0cSelric     }
1118ca1c9b0cSelric 
1119ca1c9b0cSelric     if (listen(fd, SOMAXCONN) < 0) {
1120ca1c9b0cSelric 	close(fd);
1121ca1c9b0cSelric 	return errno;
1122ca1c9b0cSelric     }
1123ca1c9b0cSelric 
1124ca1c9b0cSelric     chmod(un.sun_path, 0666);
1125ca1c9b0cSelric 
1126ca1c9b0cSelric     ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
1127ca1c9b0cSelric 				    callback, user, ctx);
1128ca1c9b0cSelric     if (ret == 0) {
1129ca1c9b0cSelric 	struct client *c = (*ctx)->mech;
1130ca1c9b0cSelric 	c->flags |= UNIX_SOCKET;
1131ca1c9b0cSelric     }
1132ca1c9b0cSelric 
1133ca1c9b0cSelric     return ret;
1134ca1c9b0cSelric }
1135ca1c9b0cSelric 
1136ca1c9b0cSelric /**
1137ca1c9b0cSelric  * Set the idle timeout value
1138ca1c9b0cSelric 
1139ca1c9b0cSelric  * The timeout event handler is triggered recurrently every idle
1140ca1c9b0cSelric  * period `t'. The default action is rather draconian and just calls
1141ca1c9b0cSelric  * exit(0), so you might want to change this to something more
1142ca1c9b0cSelric  * graceful using heim_sipc_set_timeout_handler().
1143ca1c9b0cSelric  */
1144ca1c9b0cSelric 
1145ca1c9b0cSelric void
heim_sipc_timeout(time_t t)1146ca1c9b0cSelric heim_sipc_timeout(time_t t)
1147ca1c9b0cSelric {
1148ca1c9b0cSelric #ifdef HAVE_GCD
1149ca1c9b0cSelric     static dispatch_once_t timeoutonce;
1150ca1c9b0cSelric     init_globals();
1151ca1c9b0cSelric     dispatch_sync(timerq, ^{
1152ca1c9b0cSelric 	    timeoutvalue = t;
1153ca1c9b0cSelric 	    set_timer();
1154ca1c9b0cSelric 	});
1155ca1c9b0cSelric     dispatch_once(&timeoutonce, ^{  dispatch_resume(timer); });
1156ca1c9b0cSelric #else
1157ca1c9b0cSelric     abort();
1158ca1c9b0cSelric #endif
1159ca1c9b0cSelric }
1160ca1c9b0cSelric 
1161ca1c9b0cSelric /**
1162ca1c9b0cSelric  * Set the timeout event handler
1163ca1c9b0cSelric  *
1164ca1c9b0cSelric  * Replaces the default idle timeout action.
1165ca1c9b0cSelric  */
1166ca1c9b0cSelric 
1167ca1c9b0cSelric void
heim_sipc_set_timeout_handler(void (* func)(void))1168ca1c9b0cSelric heim_sipc_set_timeout_handler(void (*func)(void))
1169ca1c9b0cSelric {
1170ca1c9b0cSelric #ifdef HAVE_GCD
1171ca1c9b0cSelric     init_globals();
1172ca1c9b0cSelric     dispatch_sync(timerq, ^{ timer_ev = func; });
1173ca1c9b0cSelric #else
1174ca1c9b0cSelric     abort();
1175ca1c9b0cSelric #endif
1176ca1c9b0cSelric }
1177ca1c9b0cSelric 
1178ca1c9b0cSelric 
1179ca1c9b0cSelric void
heim_sipc_free_context(heim_sipc ctx)1180ca1c9b0cSelric heim_sipc_free_context(heim_sipc ctx)
1181ca1c9b0cSelric {
1182ca1c9b0cSelric     (ctx->release)(ctx);
1183ca1c9b0cSelric }
1184ca1c9b0cSelric 
1185ca1c9b0cSelric void
heim_ipc_main(void)1186ca1c9b0cSelric heim_ipc_main(void)
1187ca1c9b0cSelric {
1188ca1c9b0cSelric #ifdef HAVE_GCD
1189ca1c9b0cSelric     dispatch_main();
1190ca1c9b0cSelric #else
1191ca1c9b0cSelric     process_loop();
1192ca1c9b0cSelric #endif
1193ca1c9b0cSelric }
1194ca1c9b0cSelric 
1195