Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/lib/ipc/client.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
38
#if defined(__APPLE__) && defined(HAVE_GCD)
39
40
#include "heim_ipc.h"
41
#include "heim_ipc_asyncServer.h"
42
43
#include <dispatch/dispatch.h>
44
#include <mach/mach.h>
45
46
static dispatch_once_t jobqinited = 0;
47
static dispatch_queue_t jobq = NULL;
48
static dispatch_queue_t syncq;
49
50
struct mach_ctx {
51
mach_port_t server;
52
char *name;
53
};
54
55
static int
56
mach_release(void *ctx);
57
58
static int
59
mach_init(const char *service, void **ctx)
60
{
61
struct mach_ctx *ipc;
62
mach_port_t sport;
63
int ret;
64
65
dispatch_once(&jobqinited, ^{
66
jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
67
syncq = dispatch_queue_create("heim-ipc-syncq", NULL);
68
});
69
70
ret = bootstrap_look_up(bootstrap_port, service, &sport);
71
if (ret)
72
return ret;
73
74
ipc = malloc(sizeof(*ipc));
75
if (ipc == NULL) {
76
mach_port_destroy(mach_task_self(), sport);
77
return ENOMEM;
78
}
79
80
ipc->server = sport;
81
ipc->name = strdup(service);
82
if (ipc->name == NULL) {
83
mach_release(ipc);
84
return ENOMEM;
85
}
86
87
*ctx = ipc;
88
89
return 0;
90
}
91
92
static int
93
mach_ipc(void *ctx,
94
const heim_idata *request, heim_idata *response,
95
heim_icred *cred)
96
{
97
struct mach_ctx *ipc = ctx;
98
heim_ipc_message_inband_t requestin;
99
mach_msg_type_number_t requestin_length = 0;
100
heim_ipc_message_outband_t requestout = NULL;
101
mach_msg_type_number_t requestout_length = 0;
102
heim_ipc_message_inband_t replyin;
103
mach_msg_type_number_t replyin_length;
104
heim_ipc_message_outband_t replyout;
105
mach_msg_type_number_t replyout_length;
106
int ret, errorcode, retries = 0;
107
108
memcpy(requestin, request->data, request->length);
109
requestin_length = request->length;
110
111
while (retries < 2) {
112
__block mach_port_t sport;
113
114
dispatch_sync(syncq, ^{ sport = ipc->server; });
115
116
ret = mheim_ipc_call(sport,
117
requestin, requestin_length,
118
requestout, requestout_length,
119
&errorcode,
120
replyin, &replyin_length,
121
&replyout, &replyout_length);
122
if (ret == MACH_SEND_INVALID_DEST) {
123
mach_port_t nport;
124
/* race other threads to get a new port */
125
ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport);
126
if (ret)
127
return ret;
128
dispatch_sync(syncq, ^{
129
/* check if we lost the race to lookup the port */
130
if (sport != ipc->server) {
131
mach_port_deallocate(mach_task_self(), nport);
132
} else {
133
mach_port_deallocate(mach_task_self(), ipc->server);
134
ipc->server = nport;
135
}
136
});
137
retries++;
138
} else if (ret) {
139
return ret;
140
} else
141
break;
142
}
143
if (retries >= 2)
144
return EINVAL;
145
146
if (errorcode) {
147
if (replyout_length)
148
vm_deallocate (mach_task_self (), (vm_address_t) replyout,
149
replyout_length);
150
return errorcode;
151
}
152
153
if (replyout_length) {
154
response->data = malloc(replyout_length);
155
if (response->data == NULL) {
156
vm_deallocate (mach_task_self (), (vm_address_t) replyout,
157
replyout_length);
158
return ENOMEM;
159
}
160
memcpy(response->data, replyout, replyout_length);
161
response->length = replyout_length;
162
vm_deallocate (mach_task_self (), (vm_address_t) replyout,
163
replyout_length);
164
} else {
165
response->data = malloc(replyin_length);
166
if (response->data == NULL)
167
return ENOMEM;
168
memcpy(response->data, replyin, replyin_length);
169
response->length = replyin_length;
170
}
171
172
return 0;
173
}
174
175
struct async_client {
176
mach_port_t mp;
177
dispatch_source_t source;
178
dispatch_queue_t queue;
179
void (*func)(void *, int, heim_idata *, heim_icred);
180
void *userctx;
181
};
182
183
kern_return_t
184
mheim_ado_acall_reply(mach_port_t server_port,
185
audit_token_t client_creds,
186
int returnvalue,
187
heim_ipc_message_inband_t replyin,
188
mach_msg_type_number_t replyinCnt,
189
heim_ipc_message_outband_t replyout,
190
mach_msg_type_number_t replyoutCnt)
191
{
192
struct async_client *c = dispatch_get_context(dispatch_get_current_queue());
193
heim_idata response;
194
195
if (returnvalue) {
196
response.data = NULL;
197
response.length = 0;
198
} else if (replyoutCnt) {
199
response.data = replyout;
200
response.length = replyoutCnt;
201
} else {
202
response.data = replyin;
203
response.length = replyinCnt;
204
}
205
206
(*c->func)(c->userctx, returnvalue, &response, NULL);
207
208
if (replyoutCnt)
209
vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt);
210
211
dispatch_source_cancel(c->source);
212
213
return 0;
214
215
216
}
217
218
219
static int
220
mach_async(void *ctx, const heim_idata *request, void *userctx,
221
void (*func)(void *, int, heim_idata *, heim_icred))
222
{
223
struct mach_ctx *ipc = ctx;
224
heim_ipc_message_inband_t requestin;
225
mach_msg_type_number_t requestin_length = 0;
226
heim_ipc_message_outband_t requestout = NULL;
227
mach_msg_type_number_t requestout_length = 0;
228
int ret, retries = 0;
229
kern_return_t kr;
230
struct async_client *c;
231
232
/* first create the service that will catch the reply from the server */
233
/* XXX these object should be cached and reused */
234
235
c = malloc(sizeof(*c));
236
if (c == NULL)
237
return ENOMEM;
238
239
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp);
240
if (kr != KERN_SUCCESS)
241
return EINVAL;
242
243
c->queue = dispatch_queue_create("heim-ipc-async-client", NULL);
244
c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue);
245
dispatch_set_context(c->queue, c);
246
247
dispatch_source_set_event_handler(c->source, ^{
248
dispatch_mig_server(c->source,
249
sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem),
250
mheim_aipc_server);
251
});
252
253
dispatch_source_set_cancel_handler(c->source, ^{
254
mach_port_mod_refs(mach_task_self(), c->mp,
255
MACH_PORT_RIGHT_RECEIVE, -1);
256
dispatch_release(c->queue);
257
dispatch_release(c->source);
258
free(c);
259
});
260
261
c->func = func;
262
c->userctx = userctx;
263
264
dispatch_resume(c->source);
265
266
/* ok, send the message */
267
268
memcpy(requestin, request->data, request->length);
269
requestin_length = request->length;
270
271
while (retries < 2) {
272
__block mach_port_t sport;
273
274
dispatch_sync(syncq, ^{ sport = ipc->server; });
275
276
ret = mheim_ipc_call_request(sport, c->mp,
277
requestin, requestin_length,
278
requestout, requestout_length);
279
if (ret == MACH_SEND_INVALID_DEST) {
280
ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport);
281
if (ret) {
282
dispatch_source_cancel(c->source);
283
return ret;
284
}
285
mach_port_deallocate(mach_task_self(), ipc->server);
286
ipc->server = sport;
287
retries++;
288
} else if (ret) {
289
dispatch_source_cancel(c->source);
290
return ret;
291
} else
292
break;
293
}
294
if (retries >= 2) {
295
dispatch_source_cancel(c->source);
296
return EINVAL;
297
}
298
299
return 0;
300
}
301
302
static int
303
mach_release(void *ctx)
304
{
305
struct mach_ctx *ipc = ctx;
306
if (ipc->server != MACH_PORT_NULL)
307
mach_port_deallocate(mach_task_self(), ipc->server);
308
free(ipc->name);
309
free(ipc);
310
return 0;
311
}
312
313
#endif
314
315
struct path_ctx {
316
char *path;
317
int fd;
318
};
319
320
static int common_release(void *);
321
322
static int
323
connect_unix(struct path_ctx *s)
324
{
325
struct sockaddr_un addr;
326
327
addr.sun_family = AF_UNIX;
328
strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
329
330
s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
331
if (s->fd < 0)
332
return errno;
333
rk_cloexec(s->fd);
334
335
if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
336
return errno;
337
338
return 0;
339
}
340
341
static int
342
common_path_init(const char *service,
343
const char *file,
344
void **ctx)
345
{
346
struct path_ctx *s;
347
348
s = malloc(sizeof(*s));
349
if (s == NULL)
350
return ENOMEM;
351
s->fd = -1;
352
353
asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
354
355
*ctx = s;
356
357
return 0;
358
}
359
360
static int
361
unix_socket_init(const char *service,
362
void **ctx)
363
{
364
int ret;
365
366
ret = common_path_init(service, "socket", ctx);
367
if (ret)
368
return ret;
369
ret = connect_unix(*ctx);
370
if (ret)
371
common_release(*ctx);
372
373
return ret;
374
}
375
376
static int
377
unix_socket_ipc(void *ctx,
378
const heim_idata *req, heim_idata *rep,
379
heim_icred *cred)
380
{
381
struct path_ctx *s = ctx;
382
uint32_t len = htonl(req->length);
383
uint32_t rv;
384
int retval;
385
386
if (cred)
387
*cred = NULL;
388
389
rep->data = NULL;
390
rep->length = 0;
391
392
if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
393
return -1;
394
if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
395
return -1;
396
397
if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
398
return -1;
399
if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
400
return -1;
401
retval = ntohl(rv);
402
403
rep->length = ntohl(len);
404
if (rep->length > 0) {
405
rep->data = malloc(rep->length);
406
if (rep->data == NULL)
407
return -1;
408
if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
409
return -1;
410
} else
411
rep->data = NULL;
412
413
return retval;
414
}
415
416
int
417
common_release(void *ctx)
418
{
419
struct path_ctx *s = ctx;
420
if (s->fd >= 0)
421
close(s->fd);
422
free(s->path);
423
free(s);
424
return 0;
425
}
426
427
#ifdef HAVE_DOOR
428
429
static int
430
door_init(const char *service,
431
void **ctx)
432
{
433
ret = common_path_init(context, service, "door", ctx);
434
if (ret)
435
return ret;
436
ret = connect_door(*ctx);
437
if (ret)
438
common_release(*ctx);
439
return ret;
440
}
441
442
static int
443
door_ipc(void *ctx,
444
const heim_idata *request, heim_idata *response,
445
heim_icred *cred)
446
{
447
door_arg_t arg;
448
int ret;
449
450
arg.data_ptr = request->data;
451
arg.data_size = request->length;
452
arg.desc_ptr = NULL;
453
arg.desc_num = 0;
454
arg.rbuf = NULL;
455
arg.rsize = 0;
456
457
ret = door_call(fd, &arg);
458
close(fd);
459
if (ret != 0)
460
return errno;
461
462
response->data = malloc(arg.rsize);
463
if (response->data == NULL) {
464
munmap(arg.rbuf, arg.rsize);
465
return ENOMEM;
466
}
467
memcpy(response->data, arg.rbuf, arg.rsize);
468
response->length = arg.rsize;
469
munmap(arg.rbuf, arg.rsize);
470
471
return ret;
472
}
473
474
#endif
475
476
struct hipc_ops {
477
const char *prefix;
478
int (*init)(const char *, void **);
479
int (*release)(void *);
480
int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *);
481
int (*async)(void *, const heim_idata *, void *,
482
void (*)(void *, int, heim_idata *, heim_icred));
483
};
484
485
struct hipc_ops ipcs[] = {
486
#if defined(__APPLE__) && defined(HAVE_GCD)
487
{ "MACH", mach_init, mach_release, mach_ipc, mach_async },
488
#endif
489
#ifdef HAVE_DOOR
490
{ "DOOR", door_init, common_release, door_ipc, NULL }
491
#endif
492
{ "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
493
};
494
495
struct heim_ipc {
496
struct hipc_ops *ops;
497
void *ctx;
498
};
499
500
501
int
502
heim_ipc_init_context(const char *name, heim_ipc *ctx)
503
{
504
unsigned int i;
505
int ret, any = 0;
506
507
for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
508
size_t prefix_len = strlen(ipcs[i].prefix);
509
heim_ipc c;
510
if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
511
&& name[prefix_len] == ':') {
512
} else if (strncmp("ANY:", name, 4) == 0) {
513
prefix_len = 3;
514
any = 1;
515
} else
516
continue;
517
518
c = calloc(1, sizeof(*c));
519
if (c == NULL)
520
return ENOMEM;
521
522
c->ops = &ipcs[i];
523
524
ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
525
if (ret) {
526
free(c);
527
if (any)
528
continue;
529
return ret;
530
}
531
532
*ctx = c;
533
return 0;
534
}
535
536
return ENOENT;
537
}
538
539
void
540
heim_ipc_free_context(heim_ipc ctx)
541
{
542
(ctx->ops->release)(ctx->ctx);
543
free(ctx);
544
}
545
546
int
547
heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
548
heim_icred *cred)
549
{
550
if (cred)
551
*cred = NULL;
552
return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred);
553
}
554
555
int
556
heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx,
557
void (*func)(void *, int, heim_idata *, heim_icred))
558
{
559
if (ctx->ops->async == NULL) {
560
heim_idata rcv;
561
heim_icred cred = NULL;
562
int ret;
563
564
ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
565
(*func)(userctx, ret, &rcv, cred);
566
heim_ipc_free_cred(cred);
567
free(rcv.data);
568
return ret;
569
} else {
570
return (ctx->ops->async)(ctx->ctx, snd, userctx, func);
571
}
572
}
573
574