~ubuntu-branches/ubuntu/maverick/krb5/maverick

« back to all changes in this revision

Viewing changes to src/util/mac/k5_mig_client.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman
  • Date: 2009-05-07 16:16:34 UTC
  • mfrom: (13.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20090507161634-xqyk0s9na0le4flj
Tags: 1.7dfsg~beta1-4
When  decrypting the TGS response fails with the subkey, try with the
session key to work around Heimdal bug, Closes: #527353 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Header$
 
3
 *
 
4
 * Copyright 2006 Massachusetts Institute of Technology.
 
5
 * All Rights Reserved.
 
6
 *
 
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.
 
11
 *
 
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.
 
25
 */
 
26
 
 
27
#ifndef LEAN_CLIENT
 
28
 
 
29
#include "k5_mig_client.h"
 
30
#include "k5_mig_request.h"
 
31
#include "k5_mig_replyServer.h"
 
32
#include "k5-thread.h"
 
33
 
 
34
#include <mach/mach.h>
 
35
#include <servers/bootstrap.h>
 
36
 
 
37
 
 
38
 
 
39
/* Number of services available.  Update if modifying the lists below */
 
40
#define KIPC_SERVICE_COUNT 2
 
41
 
 
42
/* ------------------------------------------------------------------------ */
 
43
 
 
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 */
 
48
 
 
49
typedef struct k5_ipc_service_port {
 
50
    const char *service_id;
 
51
    mach_port_t service_port;
 
52
} k5_ipc_service_port;
 
53
 
 
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 } };
 
59
 
 
60
/* ------------------------------------------------------------------------ */
 
61
 
 
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 
 
65
 * application. */
 
66
 
 
67
typedef struct k5_ipc_connection {
 
68
    const char *service_id;
 
69
    mach_port_t port;
 
70
} *k5_ipc_connection;
 
71
 
 
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;
 
77
 
 
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" };
 
82
 
 
83
/* ------------------------------------------------------------------------ */
 
84
 
 
85
static void k5_ipc_client_cinfo_free (void *io_cinfo)
 
86
{
 
87
    if (io_cinfo) {
 
88
        k5_ipc_connection_info cinfo = io_cinfo;
 
89
        int i;
 
90
        
 
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;
 
97
            }
 
98
        }
 
99
        /* reply_stream will always be freed by k5_ipc_send_request() */
 
100
        free (cinfo);
 
101
    }
 
102
}
 
103
 
 
104
/* ------------------------------------------------------------------------ */
 
105
 
 
106
static int k5_ipc_client_cinfo_allocate (k5_ipc_connection_info *out_cinfo)
 
107
{
 
108
    int err = 0;
 
109
    k5_ipc_connection_info cinfo = NULL;
 
110
    
 
111
    cinfo = malloc (sizeof (*cinfo));
 
112
    if (!cinfo) { err = ENOMEM; }
 
113
    
 
114
    if (!err) {
 
115
        int i;
 
116
        
 
117
        cinfo->server_died = 0;
 
118
        cinfo->reply_stream = NULL;
 
119
        
 
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;
 
123
        }
 
124
    }
 
125
    
 
126
    if (!err) {
 
127
        *out_cinfo = cinfo;
 
128
        cinfo = NULL;
 
129
    }
 
130
    
 
131
    k5_ipc_client_cinfo_free (cinfo);
 
132
    
 
133
    return err;
 
134
}
 
135
 
 
136
 
 
137
#pragma mark -
 
138
 
 
139
MAKE_INIT_FUNCTION(k5_cli_ipc_thread_init);
 
140
MAKE_FINI_FUNCTION(k5_cli_ipc_thread_fini);
 
141
 
 
142
/* ------------------------------------------------------------------------ */
 
143
 
 
144
static int k5_cli_ipc_thread_init (void)
 
145
{
 
146
    int err = 0;
 
147
    
 
148
    err = k5_key_register (K5_KEY_IPC_CONNECTION_INFO, 
 
149
                           k5_ipc_client_cinfo_free);
 
150
    
 
151
    if (!err) {
 
152
        err = k5_mutex_finish_init (&g_service_ports_mutex);
 
153
    }
 
154
    
 
155
    return err;
 
156
}
 
157
 
 
158
/* ------------------------------------------------------------------------ */
 
159
 
 
160
static void k5_cli_ipc_thread_fini (void)
 
161
{    
 
162
    int err = 0;
 
163
    
 
164
    err = k5_mutex_lock (&g_service_ports_mutex);
 
165
 
 
166
    if (!err) {
 
167
        int i;
 
168
        
 
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;
 
174
            }
 
175
        }
 
176
        k5_mutex_unlock (&g_service_ports_mutex);
 
177
    }
 
178
    
 
179
    k5_key_delete (K5_KEY_IPC_CONNECTION_INFO);
 
180
    k5_mutex_destroy (&g_service_ports_mutex);
 
181
}
 
182
 
 
183
#pragma mark -
 
184
 
 
185
/* ------------------------------------------------------------------------ */
 
186
 
 
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) 
 
191
{
 
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;
 
196
    int i;
 
197
    
 
198
    if (!in_service_id   ) { err = EINVAL; }
 
199
    if (!out_service_port) { err = EINVAL; }
 
200
    
 
201
    if (!err) {
 
202
        lock_err = k5_mutex_lock (&g_service_ports_mutex);
 
203
        if (lock_err) { err = lock_err; }
 
204
    }
 
205
    
 
206
    for (i = 0; !err && i < KIPC_SERVICE_COUNT; i++) {
 
207
        if (!strcmp (in_service_id, g_service_ports[i].service_id)) {
 
208
            found_entry = 1;
 
209
            if (in_use_cached_port) {
 
210
                k5_service_port = g_service_ports[i].service_port;
 
211
            }
 
212
            break;
 
213
        }
 
214
    }
 
215
    
 
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;
 
219
        
 
220
        /* Get our bootstrap port */
 
221
        err = task_get_bootstrap_port (mach_task_self (), &boot_port);
 
222
        
 
223
        if (!err && !in_launch_if_necessary) {
 
224
            char *lookup = NULL;
 
225
            mach_port_t lookup_port = MACH_PORT_NULL;
 
226
            
 
227
            int w = asprintf (&lookup, "%s%s", 
 
228
                              in_service_id, K5_MIG_LOOKUP_SUFFIX);
 
229
            if (w < 0) { err = ENOMEM; }
 
230
            
 
231
            if (!err) {
 
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);
 
235
            }
 
236
            
 
237
            free (lookup);
 
238
            if (MACH_PORT_VALID (lookup_port)) { 
 
239
                mach_port_deallocate (mach_task_self (), lookup_port); 
 
240
            }
 
241
        }
 
242
        
 
243
        if (!err) {
 
244
            int w = asprintf (&service, "%s%s", 
 
245
                              in_service_id, K5_MIG_SERVICE_SUFFIX);
 
246
            if (w < 0) { err = ENOMEM; }
 
247
        }
 
248
        
 
249
        if (!err) {
 
250
            err = bootstrap_look_up (boot_port, service, &k5_service_port);
 
251
            
 
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);
 
257
                }
 
258
                
 
259
                g_service_ports[i].service_port = k5_service_port;
 
260
            }
 
261
        }
 
262
        
 
263
        free (service);
 
264
        if (MACH_PORT_VALID (boot_port)) { mach_port_deallocate (mach_task_self (), 
 
265
                                                                 boot_port); }
 
266
    }
 
267
    
 
268
    if (!err) {
 
269
        *out_service_port = k5_service_port;
 
270
    }
 
271
    
 
272
    if (!lock_err) { k5_mutex_unlock (&g_service_ports_mutex); }
 
273
    
 
274
    return err;
 
275
}
 
276
 
 
277
#pragma mark -
 
278
 
 
279
/* ------------------------------------------------------------------------ */
 
280
 
 
281
static boolean_t k5_ipc_reply_demux (mach_msg_header_t *request, 
 
282
                                     mach_msg_header_t *reply) 
 
283
{
 
284
    boolean_t handled = 0;
 
285
    
 
286
    if (CALL_INIT_FUNCTION (k5_cli_ipc_thread_init) != 0) {
 
287
        return 0;
 
288
    }    
 
289
    
 
290
    if (!handled && request->msgh_id == MACH_NOTIFY_NO_SENDERS) {
 
291
        k5_ipc_connection_info cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
 
292
        if (cinfo) { 
 
293
            cinfo->server_died = 1;
 
294
        }
 
295
        
 
296
        handled = 1; /* server died */
 
297
    }
 
298
    
 
299
    if (!handled) {
 
300
        handled = k5_ipc_reply_server (request, reply);
 
301
    }
 
302
    
 
303
    return handled;    
 
304
}
 
305
 
 
306
/* ------------------------------------------------------------------------ */
 
307
 
 
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)
 
313
{
 
314
    kern_return_t err = KERN_SUCCESS;
 
315
    k5_ipc_connection_info cinfo = NULL;
 
316
    
 
317
    if (!err) {
 
318
        err = CALL_INIT_FUNCTION (k5_cli_ipc_thread_init);
 
319
    }
 
320
    
 
321
    if (!err) {
 
322
        cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
 
323
        if (!cinfo || !cinfo->reply_stream) { err = EINVAL; }
 
324
    }
 
325
    
 
326
    if (!err) {
 
327
        if (in_inl_replyCnt) {
 
328
            err = k5_ipc_stream_write (cinfo->reply_stream, 
 
329
                                       in_inl_reply, in_inl_replyCnt);
 
330
            
 
331
        } else if (in_ool_replyCnt) {
 
332
            err = k5_ipc_stream_write (cinfo->reply_stream, 
 
333
                                       in_ool_reply, in_ool_replyCnt);
 
334
            
 
335
        } else {
 
336
            err = EINVAL;
 
337
        }
 
338
    }
 
339
    
 
340
    if (in_ool_replyCnt) { vm_deallocate (mach_task_self (), 
 
341
                                          (vm_address_t) in_ool_reply, 
 
342
                                          in_ool_replyCnt); }
 
343
    
 
344
    return err;
 
345
}
 
346
 
 
347
#pragma mark -
 
348
 
 
349
/* ------------------------------------------------------------------------ */
 
350
 
 
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)
 
355
{
 
356
    int err = 0;
 
357
    int32_t done = 0;
 
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;
 
367
 
 
368
    if (!in_request_stream) { err = EINVAL; }
 
369
    if (!out_reply_stream ) { err = EINVAL; }
 
370
    
 
371
    if (!err) {
 
372
        err = CALL_INIT_FUNCTION (k5_cli_ipc_thread_init);
 
373
    }    
 
374
    
 
375
    if (!err) {
 
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);
 
379
        
 
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); */
 
383
            
 
384
            err = vm_read (mach_task_self (), 
 
385
                           (vm_address_t) k5_ipc_stream_data (in_request_stream), 
 
386
                           request_length, 
 
387
                           (vm_address_t *) &ool_request, 
 
388
                           &ool_request_length);        
 
389
        } else {
 
390
            /*dprintf ("%s choosing in line buffer (size is %d)",
 
391
             *                  __FUNCTION__, request_length); */
 
392
            
 
393
            inl_request_length = request_length;
 
394
            inl_request = k5_ipc_stream_data (in_request_stream);
 
395
        }
 
396
    }
 
397
 
 
398
    if (!err) {
 
399
        cinfo = k5_getspecific (K5_KEY_IPC_CONNECTION_INFO);
 
400
 
 
401
        if (!cinfo) {
 
402
            err = k5_ipc_client_cinfo_allocate (&cinfo);
 
403
 
 
404
            if (!err) {
 
405
                err = k5_setspecific (K5_KEY_IPC_CONNECTION_INFO, cinfo);
 
406
            }
 
407
        }
 
408
        
 
409
        if (!err) {
 
410
            int i, found = 0;
 
411
 
 
412
            for (i = 0; i < KIPC_SERVICE_COUNT; i++) {
 
413
                if (!strcmp (in_service_id, cinfo->connections[i].service_id)) {
 
414
                    found = 1;
 
415
                    connection = &cinfo->connections[i];
 
416
                    break;
 
417
                }
 
418
            }
 
419
            
 
420
            if (!found) { err = EINVAL; }
 
421
        }
 
422
    }
 
423
    
 
424
    if (!err) {
 
425
        err = k5_ipc_client_lookup_server (in_service_id, in_launch_server, 
 
426
                                           TRUE, &server_port);
 
427
    }
 
428
 
 
429
    if (!err) {
 
430
        err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, 
 
431
                                  &reply_port);
 
432
    }
 
433
    
 
434
    while (!err && !done) {
 
435
        if (!err && !MACH_PORT_VALID (connection->port)) {
 
436
            err = k5_ipc_client_create_client_connection (server_port, 
 
437
                                                          &connection->port);
 
438
        }
 
439
        
 
440
        if (!err) {
 
441
            err = k5_ipc_client_request (connection->port, reply_port,
 
442
                                         inl_request, inl_request_length,
 
443
                                         ool_request, ool_request_length);
 
444
            
 
445
        }
 
446
        
 
447
        if (err == MACH_SEND_INVALID_DEST) {
 
448
            if (try_count < 2) { 
 
449
                try_count++;
 
450
                err = 0;
 
451
            }
 
452
 
 
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;
 
457
            }    
 
458
            
 
459
            /* Look up server name again without using the cached copy */
 
460
            err = k5_ipc_client_lookup_server (in_service_id,  
 
461
                                               in_launch_server, 
 
462
                                               FALSE, &server_port);
 
463
            
 
464
        } else {
 
465
            /* Talked to server, though we may have gotten an error */
 
466
            done = 1;
 
467
            
 
468
            /* Because we use ",dealloc" ool_request will be freed by mach. 
 
469
            * Don't double free it. */
 
470
            ool_request = NULL; 
 
471
            ool_request_length = 0;                
 
472
        }            
 
473
    }
 
474
    
 
475
    if (!err) {
 
476
        err = k5_ipc_stream_new (&cinfo->reply_stream);
 
477
    }
 
478
    
 
479
    if (!err) {
 
480
        mach_port_t old_notification_target = MACH_PORT_NULL;
 
481
 
 
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, 
 
485
                                              reply_port, 
 
486
                                              MACH_MSG_TYPE_MAKE_SEND_ONCE, 
 
487
                                              &old_notification_target);
 
488
        
 
489
        if (!err && old_notification_target != MACH_PORT_NULL) {
 
490
            mach_port_deallocate (mach_task_self (), old_notification_target);
 
491
        }
 
492
    }
 
493
    
 
494
    if (!err) {
 
495
        cinfo->server_died = 0;
 
496
        
 
497
        err = mach_msg_server_once (k5_ipc_reply_demux, K5_IPC_MAX_MSG_SIZE, 
 
498
                                    reply_port, MACH_MSG_TIMEOUT_NONE);
 
499
        
 
500
        if (!err && cinfo->server_died) {
 
501
            err = ENOTCONN;
 
502
        }
 
503
    }
 
504
    
 
505
    if (err == BOOTSTRAP_UNKNOWN_SERVICE && !in_launch_server) {
 
506
        err = 0;  /* If server is not running just return an empty stream. */
 
507
    }
 
508
    
 
509
    if (!err) {
 
510
        *out_reply_stream = cinfo->reply_stream;
 
511
        cinfo->reply_stream = NULL;
 
512
    }
 
513
 
 
514
    if (reply_port != MACH_PORT_NULL) { 
 
515
        mach_port_destroy (mach_task_self (), reply_port); 
 
516
    }
 
517
    if (ool_request_length) { 
 
518
        vm_deallocate (mach_task_self (), 
 
519
                       (vm_address_t) ool_request, ool_request_length); 
 
520
    }
 
521
    if (cinfo && cinfo->reply_stream) { 
 
522
        k5_ipc_stream_release (cinfo->reply_stream); 
 
523
        cinfo->reply_stream = NULL;
 
524
    }
 
525
    
 
526
    return err;    
 
527
}
 
528
 
 
529
#endif /* LEAN CLIENT */