Coverage Report

Created: 2026-06-07 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/third_party/heimdal/lib/ipc/client.c
Line
Count
Source
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
0
{
325
0
    struct sockaddr_un addr;
326
327
0
    addr.sun_family = AF_UNIX;
328
0
    strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
329
330
0
    s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
331
0
    if (s->fd < 0)
332
0
  return errno;
333
0
    rk_cloexec(s->fd);
334
335
0
    if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
336
0
  return errno;
337
338
0
    return 0;
339
0
}
340
341
static int
342
common_path_init(const char *base,
343
                 const char *service,
344
     const char *file,
345
     void **ctx)
346
0
{
347
0
    struct path_ctx *s;
348
349
0
    s = malloc(sizeof(*s));
350
0
    if (s == NULL)
351
0
  return ENOMEM;
352
0
    s->fd = -1;
353
354
0
    if (asprintf(&s->path, "%s/.heim_%s-%s", base, service, file) == -1) {
355
0
  free(s);
356
0
  return ENOMEM;
357
0
    }
358
359
0
    *ctx = s;
360
0
    return 0;
361
0
}
362
363
static int
364
unix_socket_init(const char *service,
365
     void **ctx)
366
0
{
367
0
    const char *base = secure_getenv("HEIM_IPC_DIR");
368
0
    int ret;
369
370
0
    ret = common_path_init(base ? base : _PATH_VARRUN, service, "socket", ctx);
371
0
    if (ret)
372
0
  return ret;
373
0
    ret = connect_unix(*ctx);
374
0
    if (ret)
375
0
  common_release(*ctx);
376
377
0
    return ret;
378
0
}
379
380
static int
381
unix_socket_ipc(void *ctx,
382
    const heim_idata *req, heim_idata *rep,
383
    heim_icred *cred)
384
0
{
385
0
    struct path_ctx *s = ctx;
386
0
    uint32_t len = htonl(req->length);
387
0
    uint32_t rv;
388
0
    int retval;
389
390
0
    if (cred)
391
0
  *cred = NULL;
392
393
0
    rep->data = NULL;
394
0
    rep->length = 0;
395
396
0
    if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
397
0
  return -1;
398
0
    if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
399
0
  return -1;
400
401
0
    if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
402
0
  return -1;
403
0
    if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
404
0
  return -1;
405
0
    retval = ntohl(rv);
406
407
0
    rep->length = ntohl(len);
408
0
    if (rep->length > 0) {
409
0
  rep->data = malloc(rep->length);
410
0
  if (rep->data == NULL)
411
0
      return -1;
412
0
  if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
413
0
      return -1;
414
0
    } else
415
0
  rep->data = NULL;
416
417
0
    return retval;
418
0
}
419
420
int
421
common_release(void *ctx)
422
0
{
423
0
    struct path_ctx *s = ctx;
424
0
    if (s->fd >= 0)
425
0
  close(s->fd);
426
0
    free(s->path);
427
0
    free(s);
428
0
    return 0;
429
0
}
430
431
#ifdef HAVE_DOOR_CREATE
432
433
#include <door.h>
434
435
#ifdef HAVE_SYS_MMAN_H
436
#include <sys/mman.h>
437
#endif
438
439
static int
440
door_init(const char *service,
441
    void **ctx)
442
{
443
    const char *base = secure_getenv("HEIM_IPC_DIR");
444
    int ret;
445
    struct path_ctx *d;
446
447
    ret = common_path_init(base ? base : _PATH_VARRUN, service, "door", ctx);
448
    if (ret)
449
  return ret;
450
451
    d = (struct path_ctx *)*ctx;
452
    d->fd = open(d->path, O_RDWR);
453
    if (d->fd < 0) {
454
        ret = errno;
455
  common_release(*ctx);
456
        return ret;
457
    }
458
459
    return 0;
460
}
461
462
struct door_reply {
463
    int returnvalue;
464
    size_t length;
465
    unsigned char data[1];
466
};
467
468
static int
469
door_ipc(void *ctx,
470
   const heim_idata *request, heim_idata *response,
471
   heim_icred *cred)
472
{
473
    struct path_ctx *d = (struct path_ctx *)ctx;
474
    door_arg_t arg;
475
    int ret;
476
    struct door_reply *r;
477
478
    arg.data_ptr = request->data;
479
    arg.data_size = request->length;
480
    arg.desc_ptr = NULL;
481
    arg.desc_num = 0;
482
    arg.rbuf = NULL;
483
    arg.rsize = 0;
484
485
    ret = door_call(d->fd, &arg);
486
    if (ret != 0)
487
  return errno;
488
489
    if (arg.rsize < offsetof(struct door_reply, data))
490
        return EINVAL;
491
492
    r = (struct door_reply *)arg.rbuf;
493
    if (r->returnvalue != 0)
494
        return r->returnvalue;
495
496
    if (arg.rsize < offsetof(struct door_reply, data) + r->length)
497
        return ERANGE;
498
499
    response->data = malloc(r->length);
500
    if (response->data == NULL) {
501
  munmap(arg.rbuf, arg.rsize);
502
  return ENOMEM;
503
    }
504
505
    memcpy(response->data, r->data, r->length);
506
    response->length = r->length;
507
    munmap(arg.rbuf, arg.rsize);
508
509
    return 0;
510
}
511
512
#endif /* HAVE_DOOR_CREATE */
513
514
struct hipc_ops {
515
    const char *prefix;
516
    int (*init)(const char *, void **);
517
    int (*release)(void *);
518
    int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *);
519
    int (*async)(void *, const heim_idata *, void *,
520
     void (*)(void *, int, heim_idata *, heim_icred));
521
};
522
523
static const struct hipc_ops ipcs[] = {
524
#if defined(__APPLE__) && defined(HAVE_GCD)
525
    { "MACH", mach_init, mach_release, mach_ipc, mach_async },
526
#endif
527
#ifdef HAVE_DOOR_CREATE
528
    { "DOOR", door_init, common_release, door_ipc, NULL },
529
#endif
530
    { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
531
};
532
533
struct heim_ipc {
534
    const struct hipc_ops *ops;
535
    void *ctx;
536
};
537
538
539
int
540
heim_ipc_init_context(const char *name, heim_ipc *ctx)
541
0
{
542
0
    unsigned int i;
543
0
    int ret, any = 0;
544
545
0
    for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
546
0
  size_t prefix_len = strlen(ipcs[i].prefix);
547
0
  heim_ipc c;
548
0
  if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
549
0
     && name[prefix_len] == ':')  {
550
0
  } else if (strncmp("ANY:", name, 4) == 0) {
551
0
      prefix_len = 3;
552
0
      any = 1;
553
0
  } else
554
0
      continue;
555
556
0
  c = calloc(1, sizeof(*c));
557
0
  if (c == NULL)
558
0
      return ENOMEM;
559
560
0
  c->ops = &ipcs[i];
561
562
0
  ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
563
0
  if (ret) {
564
0
      free(c);
565
0
      if (any)
566
0
    continue;
567
0
      return ret;
568
0
  }
569
570
0
  *ctx = c;
571
0
  return 0;
572
0
    }
573
574
0
    return ENOENT;
575
0
}
576
577
void
578
heim_ipc_free_context(heim_ipc ctx)
579
0
{
580
0
    (ctx->ops->release)(ctx->ctx);
581
0
    free(ctx);
582
0
}
583
584
int
585
heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
586
        heim_icred *cred)
587
0
{
588
0
    if (cred)
589
0
  *cred = NULL;
590
0
    return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred);
591
0
}
592
593
int
594
heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx,
595
         void (*func)(void *, int, heim_idata *, heim_icred))
596
0
{
597
0
    if (ctx->ops->async == NULL) {
598
0
  heim_idata rcv;
599
0
  heim_icred cred = NULL;
600
0
  int ret;
601
602
0
  ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
603
0
  (*func)(userctx, ret, &rcv, cred);
604
0
  heim_ipc_free_cred(cred);
605
0
  free(rcv.data);
606
0
  return ret;
607
0
    } else {
608
0
  return (ctx->ops->async)(ctx->ctx, snd, userctx, func);
609
0
    }
610
0
}