4
* Copyright 2006 Massachusetts Institute of Technology.
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
29
#include "k5_mig_client.h"
30
#include "k5_mig_request.h"
31
#include "k5_mig_replyServer.h"
32
#include "k5-thread.h"
34
#include <mach/mach.h>
35
#include <servers/bootstrap.h>
39
/* Number of services available. Update if modifying the lists below */
40
#define KIPC_SERVICE_COUNT 2
42
/* ------------------------------------------------------------------------ */
44
/* This struct exists to store the global service port shared between all
45
* threads. Note that there is one of these ports per server, whereas
46
* there is one connection port per thread. Thus this is global and mutexed,
47
* whereas the connection ports below are in TLS */
49
typedef struct k5_ipc_service_port {
50
const char *service_id;
51
mach_port_t service_port;
52
} k5_ipc_service_port;
54
/* global service ports and mutex to protect it */
55
static k5_mutex_t g_service_ports_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
56
static k5_ipc_service_port g_service_ports[KIPC_SERVICE_COUNT] = {
57
{ "edu.mit.Kerberos.CCacheServer", MACH_PORT_NULL },
58
{ "edu.mit.Kerberos.KerberosAgent", MACH_PORT_NULL } };
60
/* ------------------------------------------------------------------------ */
62
/* This struct exists to hold the per-thread connection port used for ipc
63
* messages to the server. Each thread is issued a separate connection
64
* port so that the server can distinguish between threads in the same
67
typedef struct k5_ipc_connection {
68
const char *service_id;
72
typedef struct k5_ipc_connection_info {
73
struct k5_ipc_connection connections[KIPC_SERVICE_COUNT];
74
boolean_t server_died;
75
k5_ipc_stream reply_stream;
76
} *k5_ipc_connection_info;
78
/* initializer for k5_ipc_request_port to fill in server names in TLS */
79
static const char *k5_ipc_known_services[KIPC_SERVICE_COUNT] = {
80
"edu.mit.Kerberos.CCacheServer",
81
"edu.mit.Kerberos.KerberosAgent" };
83
/* ------------------------------------------------------------------------ */
85
static void k5_ipc_client_cinfo_free (void *io_cinfo)
88
k5_ipc_connection_info cinfo = io_cinfo;
91
for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
92
if (MACH_PORT_VALID (cinfo->connections[i].port)) {
93
mach_port_mod_refs (mach_task_self(),
94
cinfo->connections[i].port,
95
MACH_PORT_RIGHT_SEND, -1 );
96
cinfo->connections[i].port = MACH_PORT_NULL;
99
/* reply_stream will always be freed by k5_ipc_send_request() */
104
/* ------------------------------------------------------------------------ */
106
static int k5_ipc_client_cinfo_allocate (k5_ipc_connection_info *out_cinfo)
109
k5_ipc_connection_info cinfo = NULL;
111
cinfo = malloc (sizeof (*cinfo));
112
if (!cinfo) { err = ENOMEM; }
117
cinfo->server_died = 0;
118
cinfo->reply_stream = NULL;
120
for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
121
cinfo->connections[i].service_id = k5_ipc_known_services[i];
122
cinfo->connections[i].port = MACH_PORT_NULL;
131
k5_ipc_client_cinfo_free (cinfo);
139
MAKE_INIT_FUNCTION(k5_cli_ipc_thread_init);
140
MAKE_FINI_FUNCTION(k5_cli_ipc_thread_fini);
142
/* ------------------------------------------------------------------------ */
144
static int k5_cli_ipc_thread_init (void)
148
err = k5_key_register (K5_KEY_IPC_CONNECTION_INFO,
149
k5_ipc_client_cinfo_free);
152
err = k5_mutex_finish_init (&g_service_ports_mutex);
158
/* ------------------------------------------------------------------------ */
160
static void k5_cli_ipc_thread_fini (void)
164
err = k5_mutex_lock (&g_service_ports_mutex);
169
for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
170
if (MACH_PORT_VALID (g_service_ports[i].service_port)) {
171
mach_port_destroy (mach_task_self (),
172
g_service_ports[i].service_port);
173
g_service_ports[i].service_port = MACH_PORT_NULL;
176
k5_mutex_unlock (&g_service_ports_mutex);
179
k5_key_delete (K5_KEY_IPC_CONNECTION_INFO);
180
k5_mutex_destroy (&g_service_ports_mutex);
185
/* ------------------------------------------------------------------------ */
187
static kern_return_t k5_ipc_client_lookup_server (const char *in_service_id,
188
boolean_t in_launch_if_necessary,
189
boolean_t in_use_cached_port,
190
mach_port_t *out_service_port)
192
kern_return_t err = 0;
193
kern_return_t lock_err = 0;
194
mach_port_t k5_service_port = MACH_PORT_NULL;
195
boolean_t found_entry = 0;
198
if (!in_service_id ) { err = EINVAL; }
199
if (!out_service_port) { err = EINVAL; }
202
lock_err = k5_mutex_lock (&g_service_ports_mutex);
203
if (lock_err) { err = lock_err; }
206
for (i = 0; !err && i < KIPC_SERVICE_COUNT; i++) {
207
if (!strcmp (in_service_id, g_service_ports[i].service_id)) {
209
if (in_use_cached_port) {
210
k5_service_port = g_service_ports[i].service_port;
216
if (!err && (!MACH_PORT_VALID (k5_service_port) || !in_use_cached_port)) {
217
mach_port_t boot_port = MACH_PORT_NULL;
218
char *service = NULL;
220
/* Get our bootstrap port */
221
err = task_get_bootstrap_port (mach_task_self (), &boot_port);
223
if (!err && !in_launch_if_necessary) {
225
mach_port_t lookup_port = MACH_PORT_NULL;
227
int w = asprintf (&lookup, "%s%s",
228
in_service_id, K5_MIG_LOOKUP_SUFFIX);
229
if (w < 0) { err = ENOMEM; }
232
/* Use the lookup name because the service name will return
233
* a valid port even if the server isn't running */
234
err = bootstrap_look_up (boot_port, lookup, &lookup_port);
238
if (MACH_PORT_VALID (lookup_port)) {
239
mach_port_deallocate (mach_task_self (), lookup_port);
244
int w = asprintf (&service, "%s%s",
245
in_service_id, K5_MIG_SERVICE_SUFFIX);
246
if (w < 0) { err = ENOMEM; }
250
err = bootstrap_look_up (boot_port, service, &k5_service_port);
252
if (!err && found_entry) {
253
/* Free old port if it is valid */
254
if (!err && MACH_PORT_VALID (g_service_ports[i].service_port)) {
255
mach_port_deallocate (mach_task_self (),
256
g_service_ports[i].service_port);
259
g_service_ports[i].service_port = k5_service_port;
264
if (MACH_PORT_VALID (boot_port)) { mach_port_deallocate (mach_task_self (),
269
*out_service_port = k5_service_port;
272
if (!lock_err) { k5_mutex_unlock (&g_service_ports_mutex); }
279
/* ------------------------------------------------------------------------ */
281
static boolean_t k5_ipc_reply_demux (mach_msg_header_t *request,
282
mach_msg_header_t *reply)
284
boolean_t handled = 0;
286
if (CALL_INIT_FUNCTION (k5_cli_ipc_thread_init) != 0) {
290
if (!handled && request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
291
k5_ipc_connection_info cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
293
cinfo->server_died = 1;
296
handled = 1; /* server died */
300
handled = k5_ipc_reply_server (request, reply);
306
/* ------------------------------------------------------------------------ */
308
kern_return_t k5_ipc_client_reply (mach_port_t in_reply_port,
309
k5_ipc_inl_reply_t in_inl_reply,
310
mach_msg_type_number_t in_inl_replyCnt,
311
k5_ipc_ool_reply_t in_ool_reply,
312
mach_msg_type_number_t in_ool_replyCnt)
314
kern_return_t err = KERN_SUCCESS;
315
k5_ipc_connection_info cinfo = NULL;
318
err = CALL_INIT_FUNCTION (k5_cli_ipc_thread_init);
322
cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
323
if (!cinfo || !cinfo->reply_stream) { err = EINVAL; }
327
if (in_inl_replyCnt) {
328
err = k5_ipc_stream_write (cinfo->reply_stream,
329
in_inl_reply, in_inl_replyCnt);
331
} else if (in_ool_replyCnt) {
332
err = k5_ipc_stream_write (cinfo->reply_stream,
333
in_ool_reply, in_ool_replyCnt);
340
if (in_ool_replyCnt) { vm_deallocate (mach_task_self (),
341
(vm_address_t) in_ool_reply,
349
/* ------------------------------------------------------------------------ */
351
int32_t k5_ipc_send_request (const char *in_service_id,
352
int32_t in_launch_server,
353
k5_ipc_stream in_request_stream,
354
k5_ipc_stream *out_reply_stream)
358
int32_t try_count = 0;
359
mach_port_t server_port = MACH_PORT_NULL;
360
k5_ipc_connection_info cinfo = NULL;
361
k5_ipc_connection connection = NULL;
362
mach_port_t reply_port = MACH_PORT_NULL;
363
const char *inl_request = NULL; /* char * so we can pass the buffer in directly */
364
mach_msg_type_number_t inl_request_length = 0;
365
k5_ipc_ool_request_t ool_request = NULL;
366
mach_msg_type_number_t ool_request_length = 0;
368
if (!in_request_stream) { err = EINVAL; }
369
if (!out_reply_stream ) { err = EINVAL; }
372
err = CALL_INIT_FUNCTION (k5_cli_ipc_thread_init);
376
/* depending on how big the message is, use the fast inline buffer or
377
* the slow dynamically allocated buffer */
378
mach_msg_type_number_t request_length = k5_ipc_stream_size (in_request_stream);
380
if (request_length > K5_IPC_MAX_INL_MSG_SIZE) {
381
/*dprintf ("%s choosing out of line buffer (size is %d)",
382
* __FUNCTION__, request_length); */
384
err = vm_read (mach_task_self (),
385
(vm_address_t) k5_ipc_stream_data (in_request_stream),
387
(vm_address_t *) &ool_request,
388
&ool_request_length);
390
/*dprintf ("%s choosing in line buffer (size is %d)",
391
* __FUNCTION__, request_length); */
393
inl_request_length = request_length;
394
inl_request = k5_ipc_stream_data (in_request_stream);
399
cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
402
err = k5_ipc_client_cinfo_allocate (&cinfo);
405
err = k5_setspecific (K5_KEY_IPC_CONNECTION_INFO, cinfo);
412
for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
413
if (!strcmp (in_service_id, cinfo->connections[i].service_id)) {
415
connection = &cinfo->connections[i];
420
if (!found) { err = EINVAL; }
425
err = k5_ipc_client_lookup_server (in_service_id, in_launch_server,
430
err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
434
while (!err && !done) {
435
if (!err && !MACH_PORT_VALID (connection->port)) {
436
err = k5_ipc_client_create_client_connection (server_port,
441
err = k5_ipc_client_request (connection->port, reply_port,
442
inl_request, inl_request_length,
443
ool_request, ool_request_length);
447
if (err == MACH_SEND_INVALID_DEST) {
453
if (MACH_PORT_VALID (connection->port)) {
454
mach_port_mod_refs (mach_task_self(), connection->port,
455
MACH_PORT_RIGHT_SEND, -1 );
456
connection->port = MACH_PORT_NULL;
459
/* Look up server name again without using the cached copy */
460
err = k5_ipc_client_lookup_server (in_service_id,
462
FALSE, &server_port);
465
/* Talked to server, though we may have gotten an error */
468
/* Because we use ",dealloc" ool_request will be freed by mach.
469
* Don't double free it. */
471
ool_request_length = 0;
476
err = k5_ipc_stream_new (&cinfo->reply_stream);
480
mach_port_t old_notification_target = MACH_PORT_NULL;
482
/* request no-senders notification so we know when server dies */
483
err = mach_port_request_notification (mach_task_self (), reply_port,
484
MACH_NOTIFY_NO_SENDERS, 1,
486
MACH_MSG_TYPE_MAKE_SEND_ONCE,
487
&old_notification_target);
489
if (!err && old_notification_target != MACH_PORT_NULL) {
490
mach_port_deallocate (mach_task_self (), old_notification_target);
495
cinfo->server_died = 0;
497
err = mach_msg_server_once (k5_ipc_reply_demux, K5_IPC_MAX_MSG_SIZE,
498
reply_port, MACH_MSG_TIMEOUT_NONE);
500
if (!err && cinfo->server_died) {
505
if (err == BOOTSTRAP_UNKNOWN_SERVICE && !in_launch_server) {
506
err = 0; /* If server is not running just return an empty stream. */
510
*out_reply_stream = cinfo->reply_stream;
511
cinfo->reply_stream = NULL;
514
if (reply_port != MACH_PORT_NULL) {
515
mach_port_destroy (mach_task_self (), reply_port);
517
if (ool_request_length) {
518
vm_deallocate (mach_task_self (),
519
(vm_address_t) ool_request, ool_request_length);
521
if (cinfo && cinfo->reply_stream) {
522
k5_ipc_stream_release (cinfo->reply_stream);
523
cinfo->reply_stream = NULL;
529
#endif /* LEAN CLIENT */