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