xref: /freebsd/crypto/krb5/src/ccapi/server/ccs_ccache.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
1 /* ccapi/server/ccs_ccache.c */
2 /*
3  * Copyright 2006, 2007 Massachusetts Institute of Technology.
4  * All Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  * require a specific license from the United States Government.
8  * It is the responsibility of any person or organization contemplating
9  * export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  */
25 
26 #include "ccs_common.h"
27 #include "ccs_os_notify.h"
28 
29 struct ccs_ccache_d {
30     cci_identifier_t identifier;
31     ccs_lock_state_t lock_state;
32     cc_uint32 creds_version;
33     char *name;
34     char *v5_principal;
35     cc_time_t last_default_time;
36     cc_time_t last_changed_time;
37     cc_uint32 kdc_time_offset_v5_valid;
38     cc_time_t kdc_time_offset_v5;
39     ccs_credentials_list_t credentials;
40     ccs_callback_array_t change_callbacks;
41 };
42 
43 struct ccs_ccache_d ccs_ccache_initializer = { NULL, NULL, 0, NULL, NULL, 0, 0, 0, 0, NULL, NULL };
44 
45 /* ------------------------------------------------------------------------ */
46 
47 cc_int32 ccs_ccache_new (ccs_ccache_t      *out_ccache,
48                          cc_uint32          in_creds_version,
49                          const char        *in_name,
50                          const char        *in_principal,
51                          ccs_ccache_list_t  io_ccache_list)
52 {
53     cc_int32 err = ccNoError;
54     ccs_ccache_t ccache = NULL;
55 
56     if (!out_ccache  ) { err = cci_check_error (ccErrBadParam); }
57     if (!in_name     ) { err = cci_check_error (ccErrBadParam); }
58     if (!in_principal) { err = cci_check_error (ccErrBadParam); }
59 
60     if (!err) {
61         ccache = malloc (sizeof (*ccache));
62         if (ccache) {
63             *ccache = ccs_ccache_initializer;
64         } else {
65             err = cci_check_error (ccErrNoMem);
66         }
67     }
68 
69     if (!err) {
70         err = ccs_server_new_identifier (&ccache->identifier);
71     }
72 
73     if (!err) {
74         err = ccs_lock_state_new (&ccache->lock_state,
75                                   ccErrInvalidCCache,
76                                   ccErrCCacheLocked,
77                                   ccErrCCacheUnlocked);
78     }
79 
80     if (!err) {
81         ccache->name = strdup (in_name);
82         if (!ccache->name) { err = cci_check_error (ccErrNoMem); }
83     }
84 
85     if (!err) {
86         ccache->creds_version = in_creds_version;
87 
88         if (ccache->creds_version == cc_credentials_v5) {
89             ccache->v5_principal = strdup (in_principal);
90             if (!ccache->v5_principal) { err = cci_check_error (ccErrNoMem); }
91 
92         } else {
93             err = cci_check_error (ccErrBadCredentialsVersion);
94         }
95     }
96 
97     if (!err) {
98         err = ccs_credentials_list_new (&ccache->credentials);
99     }
100 
101     if (!err) {
102         err = ccs_callback_array_new (&ccache->change_callbacks);
103     }
104 
105     if (!err) {
106         cc_uint64 now = time (NULL);
107         cc_uint64 count = 0;
108 
109         err = ccs_ccache_list_count (io_ccache_list, &count);
110 
111         if (!err) {
112             /* first cache is default */
113             ccache->last_default_time = (count == 0) ? now : 0;
114 	    cci_debug_printf ("%s ccache->last_default_time is %d.",
115 			     __FUNCTION__, ccache->last_default_time);
116             ccache->last_changed_time = now;
117         }
118     }
119 
120     if (!err) {
121         /* Add self to the list of ccaches */
122         err = ccs_ccache_list_add (io_ccache_list, ccache);
123     }
124 
125     if (!err) {
126         *out_ccache = ccache;
127         ccache = NULL;
128     }
129 
130     ccs_ccache_release (ccache);
131 
132     return cci_check_error (err);
133 }
134 
135 /* ------------------------------------------------------------------------ */
136 
137 cc_int32 ccs_ccache_reset (ccs_ccache_t            io_ccache,
138 			   ccs_cache_collection_t  io_cache_collection,
139                            cc_uint32               in_creds_version,
140                            const char             *in_principal)
141 {
142     cc_int32 err = ccNoError;
143     char *v5_principal = NULL;
144     ccs_credentials_list_t credentials = NULL;
145 
146     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
147     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
148     if (!in_principal       ) { err = cci_check_error (ccErrBadParam); }
149 
150     if (!err) {
151         io_ccache->creds_version = in_creds_version;
152 
153         if (io_ccache->creds_version == cc_credentials_v5) {
154             v5_principal = strdup (in_principal);
155             if (!v5_principal) { err = cci_check_error (ccErrNoMem); }
156 
157         } else {
158             err = cci_check_error (ccErrBadCredentialsVersion);
159         }
160     }
161 
162     if (!err) {
163         err = ccs_credentials_list_new (&credentials);
164     }
165 
166     if (!err) {
167         io_ccache->kdc_time_offset_v5 = 0;
168         io_ccache->kdc_time_offset_v5_valid = 0;
169 
170         if (io_ccache->v5_principal) { free (io_ccache->v5_principal); }
171         io_ccache->v5_principal = v5_principal;
172         v5_principal = NULL; /* take ownership */
173 
174         ccs_credentials_list_release (io_ccache->credentials);
175         io_ccache->credentials = credentials;
176         credentials = NULL; /* take ownership */
177 
178 	err = ccs_ccache_changed (io_ccache, io_cache_collection);
179     }
180 
181     free (v5_principal);
182     ccs_credentials_list_release (credentials);
183 
184     return cci_check_error (err);
185 }
186 
187 /* ------------------------------------------------------------------------ */
188 
189 cc_int32 ccs_ccache_swap_contents (ccs_ccache_t           io_source_ccache,
190                                    ccs_ccache_t           io_destination_ccache,
191 				   ccs_cache_collection_t io_cache_collection)
192 {
193     cc_int32 err = ccNoError;
194 
195     if (!io_source_ccache     ) { err = cci_check_error (ccErrBadParam); }
196     if (!io_destination_ccache) { err = cci_check_error (ccErrBadParam); }
197 
198     if (!err) {
199         struct ccs_ccache_d temp_ccache = *io_destination_ccache;
200 
201         /* swap everything */
202         *io_destination_ccache = *io_source_ccache;
203         *io_source_ccache = temp_ccache;
204 
205         /* swap back the name and identifier */
206         io_source_ccache->identifier = io_destination_ccache->identifier;
207         io_destination_ccache->identifier = temp_ccache.identifier;
208 
209         io_source_ccache->name = io_destination_ccache->name;
210         io_destination_ccache->name = temp_ccache.name;
211     }
212 
213     if (!err) {
214         err = ccs_ccache_changed (io_source_ccache, io_cache_collection);
215     }
216 
217     if (!err) {
218         err = ccs_ccache_changed (io_destination_ccache, io_cache_collection);
219     }
220 
221     return cci_check_error (err);
222 }
223 
224 /* ------------------------------------------------------------------------ */
225 
226 cc_int32 ccs_ccache_release (ccs_ccache_t io_ccache)
227 {
228     cc_int32 err = ccNoError;
229 
230     if (!err && io_ccache) {
231         cci_identifier_release (io_ccache->identifier);
232         ccs_lock_state_release (io_ccache->lock_state);
233         free (io_ccache->name);
234         free (io_ccache->v5_principal);
235         ccs_credentials_list_release (io_ccache->credentials);
236         ccs_callback_array_release (io_ccache->change_callbacks);
237         free (io_ccache);
238     }
239 
240     return cci_check_error (err);
241 }
242 
243 #ifdef TARGET_OS_MAC
244 #pragma mark -
245 #endif
246 
247 /* ------------------------------------------------------------------------ */
248 
249 cc_int32 ccs_ccache_compare_identifier (ccs_ccache_t      in_ccache,
250                                         cci_identifier_t  in_identifier,
251                                         cc_uint32        *out_equal)
252 {
253     cc_int32 err = ccNoError;
254 
255     if (!in_ccache    ) { err = cci_check_error (ccErrBadParam); }
256     if (!in_identifier) { err = cci_check_error (ccErrBadParam); }
257     if (!out_equal    ) { err = cci_check_error (ccErrBadParam); }
258 
259     if (!err) {
260         err = cci_identifier_compare (in_ccache->identifier,
261                                       in_identifier,
262                                       out_equal);
263     }
264 
265     return cci_check_error (err);
266 }
267 
268 /* ------------------------------------------------------------------------ */
269 
270 cc_int32 ccs_ccache_compare_name (ccs_ccache_t  in_ccache,
271                                   const char   *in_name,
272                                   cc_uint32    *out_equal)
273 {
274     cc_int32 err = ccNoError;
275 
276     if (!in_ccache) { err = cci_check_error (ccErrBadParam); }
277     if (!in_name  ) { err = cci_check_error (ccErrBadParam); }
278     if (!out_equal) { err = cci_check_error (ccErrBadParam); }
279 
280     if (!err) {
281         *out_equal = (strcmp (in_ccache->name, in_name) == 0);
282     }
283 
284     return cci_check_error (err);
285 }
286 
287 #ifdef TARGET_OS_MAC
288 #pragma mark -
289 #endif
290 
291 /* ------------------------------------------------------------------------ */
292 
293 cc_int32 ccs_ccache_changed (ccs_ccache_t           io_ccache,
294                              ccs_cache_collection_t io_cache_collection)
295 {
296     cc_int32 err = ccNoError;
297     k5_ipc_stream reply_data = NULL;
298 
299     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
300     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
301 
302     if (!err) {
303         cc_time_t now = time (NULL);
304 
305         if (io_ccache->last_changed_time < now) {
306             io_ccache->last_changed_time = now;
307         } else {
308             io_ccache->last_changed_time++;
309         }
310     }
311 
312     if (!err) {
313         err = ccs_cache_collection_changed (io_cache_collection);
314     }
315 
316     if (!err) {
317         err = krb5int_ipc_stream_new (&reply_data);
318     }
319 
320     if (!err) {
321 	err = krb5int_ipc_stream_write_time (reply_data, io_ccache->last_changed_time);
322     }
323 
324     if (!err) {
325 	/* Loop over callbacks sending messages to them */
326 	cc_uint64 i;
327         cc_uint64 count = ccs_callback_array_count (io_ccache->change_callbacks);
328 
329         for (i = 0; !err && i < count; i++) {
330             ccs_callback_t callback = ccs_callback_array_object_at_index (io_ccache->change_callbacks, i);
331 
332 	    err = ccs_callback_reply_to_client (callback, reply_data);
333 
334 	    if (!err) {
335 		cci_debug_printf ("%s: Removing callback reference %p.", __FUNCTION__, callback);
336 		err = ccs_callback_array_remove (io_ccache->change_callbacks, i);
337 		break;
338 	    }
339         }
340     }
341 
342     if (!err) {
343         err = ccs_os_notify_ccache_changed (io_cache_collection,
344                                             io_ccache->name);
345     }
346 
347     krb5int_ipc_stream_release (reply_data);
348 
349     return cci_check_error (err);
350 }
351 
352 /* ------------------------------------------------------------------------ */
353 
354 static cc_int32 ccs_ccache_invalidate_change_callback (ccs_callback_owner_t io_ccache,
355 						       ccs_callback_t       in_callback)
356 {
357     cc_int32 err = ccNoError;
358 
359     if (!io_ccache  ) { err = cci_check_error (ccErrBadParam); }
360     if (!in_callback) { err = cci_check_error (ccErrBadParam); }
361 
362     if (!err) {
363 	/* Remove callback */
364 	ccs_ccache_t ccache = (ccs_ccache_t) io_ccache;
365 	cc_uint64 i;
366         cc_uint64 count = ccs_callback_array_count (ccache->change_callbacks);
367 
368         for (i = 0; !err && i < count; i++) {
369             ccs_callback_t callback = ccs_callback_array_object_at_index (ccache->change_callbacks, i);
370 
371 	    if (callback == in_callback) {
372 		cci_debug_printf ("%s: Removing callback reference %p.", __FUNCTION__, callback);
373 		err = ccs_callback_array_remove (ccache->change_callbacks, i);
374 		break;
375 	    }
376         }
377     }
378 
379     return cci_check_error (err);
380 }
381 
382 /* ------------------------------------------------------------------------ */
383 
384 cc_int32 ccs_ccache_notify_default_state_changed (ccs_ccache_t           io_ccache,
385                                                   ccs_cache_collection_t io_cache_collection,
386                                                   cc_uint32              in_new_default_state)
387 {
388     cc_int32 err = ccNoError;
389 
390     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
391     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
392 
393     if (!err && in_new_default_state) {
394         cc_time_t now = time (NULL);
395 
396         if (io_ccache->last_default_time < now) {
397             io_ccache->last_default_time = now;
398         } else {
399             io_ccache->last_default_time++;
400         }
401     }
402 
403     if (!err) {
404         err = ccs_ccache_changed (io_ccache, io_cache_collection);
405     }
406 
407     return cci_check_error (err);
408 }
409 
410 #ifdef TARGET_OS_MAC
411 #pragma mark -
412 #endif
413 
414 /* ------------------------------------------------------------------------ */
415 
416 cc_int32 ccs_ccache_find_credentials_iterator (ccs_ccache_t                in_ccache,
417                                                cci_identifier_t            in_identifier,
418                                                ccs_credentials_iterator_t *out_credentials_iterator)
419 {
420     cc_int32 err = ccNoError;
421 
422     if (!in_ccache               ) { err = cci_check_error (ccErrBadParam); }
423     if (!in_identifier           ) { err = cci_check_error (ccErrBadParam); }
424     if (!out_credentials_iterator) { err = cci_check_error (ccErrBadParam); }
425 
426     if (!err) {
427         err = ccs_credentials_list_find_iterator (in_ccache->credentials,
428                                                   in_identifier,
429                                                   out_credentials_iterator);
430     }
431 
432     // Don't report ccErrInvalidCredentials to the log file.  Non-fatal.
433     return (err == ccErrInvalidCredentials) ? err : cci_check_error (err);
434 }
435 
436 #ifdef TARGET_OS_MAC
437 #pragma mark -
438 #endif
439 
440 /* ------------------------------------------------------------------------ */
441 
442 cc_int32 ccs_ccache_write (ccs_ccache_t in_ccache,
443                            k5_ipc_stream io_stream)
444 {
445     cc_int32 err = ccNoError;
446 
447     if (!in_ccache) { err = cci_check_error (ccErrBadParam); }
448     if (!io_stream) { err = cci_check_error (ccErrBadParam); }
449 
450     if (!err) {
451         err = cci_identifier_write (in_ccache->identifier, io_stream);
452     }
453 
454     return cci_check_error (err);
455 }
456 
457 
458 /* ------------------------------------------------------------------------ */
459 
460 cc_int32 ccs_ccache_write_name (ccs_ccache_t in_ccache,
461                                 k5_ipc_stream io_stream)
462 {
463     cc_int32 err = ccNoError;
464 
465     if (!in_ccache) { err = cci_check_error (ccErrBadParam); }
466     if (!io_stream) { err = cci_check_error (ccErrBadParam); }
467 
468     if (!err) {
469         err = krb5int_ipc_stream_write_string (io_stream, in_ccache->name);
470     }
471 
472     return cci_check_error (err);
473 }
474 
475 #ifdef TARGET_OS_MAC
476 #pragma mark -
477 #pragma mark -- IPC Messages --
478 #endif
479 
480 /* ------------------------------------------------------------------------ */
481 
482 static cc_int32 ccs_ccache_destroy (ccs_ccache_t           io_ccache,
483                                     ccs_cache_collection_t io_cache_collection,
484                                     k5_ipc_stream           in_request_data,
485                                     k5_ipc_stream           io_reply_data)
486 {
487     cc_int32 err = ccNoError;
488 
489     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
490     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
491     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
492     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
493 
494     if (!err) {
495         err = ccs_cache_collection_destroy_ccache (io_cache_collection,
496                                                    io_ccache->identifier);
497     }
498 
499     if (!err) {
500         /* ccache has been destroyed so just mark the cache collection */
501         err = ccs_cache_collection_changed (io_cache_collection);
502     }
503 
504     return cci_check_error (err);
505 }
506 
507 /* ------------------------------------------------------------------------ */
508 
509 static cc_int32 ccs_ccache_set_default (ccs_ccache_t           io_ccache,
510                                         ccs_cache_collection_t io_cache_collection,
511                                         k5_ipc_stream           in_request_data,
512                                         k5_ipc_stream           io_reply_data)
513 {
514     cc_int32 err = ccNoError;
515 
516     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
517     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
518     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
519     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
520 
521     if (!err) {
522         err = ccs_cache_collection_set_default_ccache (io_cache_collection,
523                                                        io_ccache->identifier);
524     }
525 
526     return cci_check_error (err);
527 }
528 
529 /* ------------------------------------------------------------------------ */
530 
531 static cc_int32 ccs_ccache_get_credentials_version (ccs_ccache_t           io_ccache,
532                                                     ccs_cache_collection_t io_cache_collection,
533                                                     k5_ipc_stream           in_request_data,
534                                                     k5_ipc_stream           io_reply_data)
535 {
536     cc_int32 err = ccNoError;
537 
538     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
539     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
540     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
541     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
542 
543     if (!err) {
544         err = krb5int_ipc_stream_write_uint32 (io_reply_data, io_ccache->creds_version);
545     }
546 
547     return cci_check_error (err);
548 }
549 
550 /* ------------------------------------------------------------------------ */
551 
552 static cc_int32 ccs_ccache_get_name (ccs_ccache_t           io_ccache,
553                                      ccs_cache_collection_t io_cache_collection,
554                                      k5_ipc_stream           in_request_data,
555                                      k5_ipc_stream           io_reply_data)
556 {
557     cc_int32 err = ccNoError;
558 
559     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
560     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
561     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
562     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
563 
564     if (!err) {
565         err = krb5int_ipc_stream_write_string (io_reply_data, io_ccache->name);
566     }
567 
568     return cci_check_error (err);
569 }
570 
571 /* ------------------------------------------------------------------------ */
572 
573 static cc_int32 ccs_ccache_get_principal (ccs_ccache_t           io_ccache,
574                                           ccs_cache_collection_t io_cache_collection,
575                                           k5_ipc_stream           in_request_data,
576                                           k5_ipc_stream           io_reply_data)
577 {
578     cc_int32 err = ccNoError;
579     cc_uint32 version = 0;
580 
581     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
582     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
583     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
584     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
585 
586     if (!err) {
587         err = krb5int_ipc_stream_read_uint32 (in_request_data, &version);
588     }
589 
590     if (!err) {
591         if (version == cc_credentials_v5) {
592             err = krb5int_ipc_stream_write_string (io_reply_data, io_ccache->v5_principal);
593 
594         } else {
595             err = cci_check_error (ccErrBadCredentialsVersion);
596         }
597     }
598 
599     return cci_check_error (err);
600 }
601 
602 /* ------------------------------------------------------------------------ */
603 
604 static cc_int32 ccs_ccache_set_principal (ccs_ccache_t           io_ccache,
605                                           ccs_cache_collection_t io_cache_collection,
606                                           k5_ipc_stream           in_request_data,
607                                           k5_ipc_stream           io_reply_data)
608 {
609     cc_int32 err = ccNoError;
610     cc_uint32 version = 0;
611     char *principal = NULL;
612 
613     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
614     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
615     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
616     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
617 
618     if (!err) {
619         err = krb5int_ipc_stream_read_uint32 (in_request_data, &version);
620     }
621 
622     if (!err) {
623         err = krb5int_ipc_stream_read_string (in_request_data, &principal);
624     }
625 
626     if (!err) {
627         /* reset KDC time offsets because they are per-KDC */
628         if (version == cc_credentials_v5) {
629             io_ccache->kdc_time_offset_v5 = 0;
630             io_ccache->kdc_time_offset_v5_valid = 0;
631 
632             if (io_ccache->v5_principal) { free (io_ccache->v5_principal); }
633             io_ccache->v5_principal = principal;
634             principal = NULL; /* take ownership */
635 
636         } else {
637             err = cci_check_error (ccErrBadCredentialsVersion);
638         }
639     }
640 
641     if (!err) {
642         io_ccache->creds_version |= version;
643 
644         err = ccs_ccache_changed (io_ccache, io_cache_collection);
645     }
646 
647     krb5int_ipc_stream_free_string (principal);
648 
649     return cci_check_error (err);
650 }
651 
652 /* ------------------------------------------------------------------------ */
653 
654 static cc_int32 ccs_ccache_store_credentials (ccs_ccache_t           io_ccache,
655                                               ccs_cache_collection_t io_cache_collection,
656                                               k5_ipc_stream           in_request_data,
657                                               k5_ipc_stream           io_reply_data)
658 {
659     cc_int32 err = ccNoError;
660     ccs_credentials_t credentials = NULL;
661 
662     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
663     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
664     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
665     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
666 
667     if (!err) {
668         err = ccs_credentials_new (&credentials, in_request_data,
669                                    io_ccache->creds_version,
670                                    io_ccache->credentials);
671     }
672 
673     if (!err) {
674         err = ccs_ccache_changed (io_ccache, io_cache_collection);
675     }
676 
677 
678     return cci_check_error (err);
679 }
680 
681 /* ------------------------------------------------------------------------ */
682 
683 static cc_int32 ccs_ccache_remove_credentials (ccs_ccache_t           io_ccache,
684                                                ccs_cache_collection_t io_cache_collection,
685                                                k5_ipc_stream           in_request_data,
686                                                k5_ipc_stream           io_reply_data)
687 {
688     cc_int32 err = ccNoError;
689     cci_identifier_t credentials_identifier = NULL;
690 
691     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
692     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
693     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
694     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
695 
696     if (!err) {
697         err = cci_identifier_read (&credentials_identifier, in_request_data);
698     }
699 
700     if (!err) {
701         err = ccs_credentials_list_remove (io_ccache->credentials, credentials_identifier);
702     }
703 
704     if (!err) {
705         err = ccs_ccache_changed (io_ccache, io_cache_collection);
706     }
707 
708     cci_identifier_release (credentials_identifier);
709 
710     return cci_check_error (err);
711 }
712 
713 /* ------------------------------------------------------------------------ */
714 
715 static cc_int32 ccs_ccache_new_credentials_iterator (ccs_ccache_t           io_ccache,
716                                                      ccs_cache_collection_t io_cache_collection,
717                                                      ccs_pipe_t             in_client_pipe,
718                                                      k5_ipc_stream           in_request_data,
719                                                      k5_ipc_stream           io_reply_data)
720 {
721     cc_int32 err = ccNoError;
722     ccs_credentials_iterator_t credentials_iterator = NULL;
723 
724     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
725     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
726     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
727     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
728 
729     if (!err) {
730         err = ccs_credentials_list_new_iterator (io_ccache->credentials,
731                                                  in_client_pipe,
732                                                  &credentials_iterator);
733     }
734 
735     if (!err) {
736         err = ccs_credentials_list_iterator_write (credentials_iterator, io_reply_data);
737     }
738 
739     return cci_check_error (err);
740 }
741 
742 /* ------------------------------------------------------------------------ */
743 
744 static cc_int32 ccs_ccache_move (ccs_ccache_t           io_ccache,
745                                  ccs_cache_collection_t io_cache_collection,
746                                  k5_ipc_stream           in_request_data,
747                                  k5_ipc_stream           io_reply_data)
748 {
749     cc_int32 err = ccNoError;
750     cci_identifier_t source_identifier = NULL;
751 
752     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
753     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
754     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
755     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
756 
757     if (!err) {
758         /* Note: message is sent as the destination ccache to avoid     */
759         /* extra work on the server when deleting it the source ccache. */
760         err = cci_identifier_read (&source_identifier, in_request_data);
761     }
762 
763     if (!err) {
764         err = ccs_ccache_collection_move_ccache (io_cache_collection,
765                                                  source_identifier,
766                                                  io_ccache);
767     }
768 
769     if (!err) {
770         err = ccs_ccache_changed (io_ccache, io_cache_collection);
771     }
772 
773     cci_identifier_release (source_identifier);
774 
775     return cci_check_error (err);
776 }
777 
778 /* ------------------------------------------------------------------------ */
779 
780 static cc_int32 ccs_ccache_lock (ccs_pipe_t             in_client_pipe,
781                                  ccs_pipe_t             in_reply_pipe,
782                                  ccs_ccache_t           io_ccache,
783                                  ccs_cache_collection_t io_cache_collection,
784                                  k5_ipc_stream           in_request_data,
785                                  cc_uint32              *out_will_block,
786                                  k5_ipc_stream           io_reply_data)
787 {
788     cc_int32 err = ccNoError;
789     cc_uint32 lock_type;
790     cc_uint32 block;
791 
792     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
793     if (!io_ccache                      ) { err = cci_check_error (ccErrBadParam); }
794     if (!io_cache_collection            ) { err = cci_check_error (ccErrBadParam); }
795     if (!in_request_data                ) { err = cci_check_error (ccErrBadParam); }
796     if (!out_will_block                 ) { err = cci_check_error (ccErrBadParam); }
797     if (!io_reply_data                  ) { err = cci_check_error (ccErrBadParam); }
798 
799     if (!err) {
800         err = krb5int_ipc_stream_read_uint32 (in_request_data, &lock_type);
801     }
802 
803     if (!err) {
804         err = krb5int_ipc_stream_read_uint32 (in_request_data, &block);
805     }
806 
807     if (!err) {
808         err = ccs_lock_state_add (io_ccache->lock_state,
809                                   in_client_pipe, in_reply_pipe,
810                                   lock_type, block, out_will_block);
811     }
812 
813     return cci_check_error (err);
814 }
815 
816 /* ------------------------------------------------------------------------ */
817 
818 static cc_int32 ccs_ccache_unlock (ccs_pipe_t             in_client_pipe,
819                                    ccs_ccache_t           io_ccache,
820                                    ccs_cache_collection_t io_cache_collection,
821                                    k5_ipc_stream           in_request_data,
822                                    k5_ipc_stream           io_reply_data)
823 {
824     cc_int32 err = ccNoError;
825 
826     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
827     if (!io_ccache                      ) { err = cci_check_error (ccErrBadParam); }
828     if (!io_cache_collection            ) { err = cci_check_error (ccErrBadParam); }
829     if (!in_request_data                ) { err = cci_check_error (ccErrBadParam); }
830     if (!io_reply_data                  ) { err = cci_check_error (ccErrBadParam); }
831 
832     if (!err) {
833         err = ccs_lock_state_remove (io_ccache->lock_state, in_client_pipe);
834     }
835 
836     return cci_check_error (err);
837 }
838 
839 /* ------------------------------------------------------------------------ */
840 
841 static cc_int32 ccs_ccache_get_last_default_time (ccs_ccache_t           io_ccache,
842                                                   ccs_cache_collection_t io_cache_collection,
843                                                   k5_ipc_stream           in_request_data,
844                                                   k5_ipc_stream           io_reply_data)
845 {
846     cc_int32 err = ccNoError;
847 
848     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
849     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
850     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
851     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
852 
853     if (!err && io_ccache->last_default_time == 0) {
854         err = cci_check_error (ccErrNeverDefault);
855     }
856 
857     if (!err) {
858         err = krb5int_ipc_stream_write_time (io_reply_data, io_ccache->last_default_time);
859     }
860 
861     return cci_check_error (err);
862 }
863 
864 /* ------------------------------------------------------------------------ */
865 
866 static cc_int32 ccs_ccache_get_change_time (ccs_ccache_t           io_ccache,
867                                             ccs_cache_collection_t io_cache_collection,
868                                             k5_ipc_stream           in_request_data,
869                                             k5_ipc_stream           io_reply_data)
870 {
871     cc_int32 err = ccNoError;
872 
873     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
874     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
875     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
876     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
877 
878     if (!err) {
879         err = krb5int_ipc_stream_write_time (io_reply_data, io_ccache->last_changed_time);
880     }
881 
882     return cci_check_error (err);
883 }
884 
885 /* ------------------------------------------------------------------------ */
886 
887 static cc_int32 ccs_ccache_wait_for_change (ccs_pipe_t              in_client_pipe,
888 					    ccs_pipe_t              in_reply_pipe,
889 					    ccs_ccache_t            io_ccache,
890 					    ccs_cache_collection_t  io_cache_collection,
891 					    k5_ipc_stream            in_request_data,
892 					    k5_ipc_stream            io_reply_data,
893 					    cc_uint32              *out_will_block)
894 {
895     cc_int32 err = ccNoError;
896     cc_time_t last_wait_for_change_time = 0;
897     cc_uint32 will_block = 0;
898 
899     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
900     if (!ccs_pipe_valid (in_reply_pipe )) { err = cci_check_error (ccErrBadParam); }
901     if (!io_ccache                      ) { err = cci_check_error (ccErrBadParam); }
902     if (!io_cache_collection            ) { err = cci_check_error (ccErrBadParam); }
903     if (!in_request_data                ) { err = cci_check_error (ccErrBadParam); }
904     if (!out_will_block                 ) { err = cci_check_error (ccErrBadParam); }
905 
906     if (!err) {
907         err = krb5int_ipc_stream_read_time (in_request_data, &last_wait_for_change_time);
908     }
909 
910     if (!err) {
911 	if (last_wait_for_change_time < io_ccache->last_changed_time) {
912 	    cci_debug_printf ("%s returning immediately", __FUNCTION__);
913 	    err = krb5int_ipc_stream_write_time (io_reply_data, io_ccache->last_changed_time);
914 
915 	} else {
916 	    ccs_callback_t callback = NULL;
917 
918 	    err = ccs_callback_new (&callback,
919 				    ccErrInvalidCCache,
920 				    in_client_pipe,
921 				    in_reply_pipe,
922 				    (ccs_callback_owner_t) io_ccache,
923 				    ccs_ccache_invalidate_change_callback);
924 
925 	    if (!err) {
926 		err = ccs_callback_array_insert (io_ccache->change_callbacks, callback,
927 						 ccs_callback_array_count (io_ccache->change_callbacks));
928 		if (!err) { callback = NULL; /* take ownership */ }
929 
930 		cci_debug_printf ("%s blocking", __FUNCTION__);
931 		will_block = 1;
932 	    }
933 
934 	    ccs_callback_release (callback);
935 	}
936     }
937 
938     if (!err) {
939 	*out_will_block = will_block;
940     }
941 
942     return cci_check_error (err);
943 }
944 
945 /* ------------------------------------------------------------------------ */
946 
947 static cc_int32 ccs_ccache_get_kdc_time_offset (ccs_ccache_t           io_ccache,
948                                                 ccs_cache_collection_t io_cache_collection,
949                                                 k5_ipc_stream           in_request_data,
950                                                 k5_ipc_stream           io_reply_data)
951 {
952     cc_int32 err = ccNoError;
953     cc_uint32 cred_vers = 0;
954 
955     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
956     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
957     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
958     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
959 
960     if (!err) {
961         err = krb5int_ipc_stream_read_uint32 (in_request_data, &cred_vers);
962     }
963 
964     if (!err) {
965         if (cred_vers == cc_credentials_v5) {
966             if (io_ccache->kdc_time_offset_v5_valid) {
967                 err = krb5int_ipc_stream_write_time (io_reply_data, io_ccache->kdc_time_offset_v5);
968             } else {
969                 err = cci_check_error (ccErrTimeOffsetNotSet);
970             }
971 
972         } else {
973             err = cci_check_error (ccErrBadCredentialsVersion);
974         }
975     }
976 
977     return cci_check_error (err);
978 }
979 
980 /* ------------------------------------------------------------------------ */
981 
982 static cc_int32 ccs_ccache_set_kdc_time_offset (ccs_ccache_t           io_ccache,
983                                                 ccs_cache_collection_t io_cache_collection,
984                                                 k5_ipc_stream           in_request_data,
985                                                 k5_ipc_stream           io_reply_data)
986 {
987     cc_int32 err = ccNoError;
988     cc_uint32 cred_vers = 0;
989 
990     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
991     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
992     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
993     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
994 
995     if (!err) {
996         err = krb5int_ipc_stream_read_uint32 (in_request_data, &cred_vers);
997     }
998 
999     if (!err) {
1000         if (cred_vers == cc_credentials_v5) {
1001             err = krb5int_ipc_stream_read_time (in_request_data, &io_ccache->kdc_time_offset_v5);
1002 
1003             if (!err) {
1004                 io_ccache->kdc_time_offset_v5_valid = 1;
1005             }
1006         } else {
1007             err = cci_check_error (ccErrBadCredentialsVersion);
1008         }
1009     }
1010 
1011     if (!err) {
1012         err = ccs_ccache_changed (io_ccache, io_cache_collection);
1013     }
1014 
1015     return cci_check_error (err);
1016 }
1017 
1018 /* ------------------------------------------------------------------------ */
1019 
1020 static cc_int32 ccs_ccache_clear_kdc_time_offset (ccs_ccache_t           io_ccache,
1021                                                   ccs_cache_collection_t io_cache_collection,
1022                                                   k5_ipc_stream           in_request_data,
1023                                                   k5_ipc_stream           io_reply_data)
1024 {
1025     cc_int32 err = ccNoError;
1026     cc_uint32 cred_vers = 0;
1027 
1028     if (!io_ccache          ) { err = cci_check_error (ccErrBadParam); }
1029     if (!io_cache_collection) { err = cci_check_error (ccErrBadParam); }
1030     if (!in_request_data    ) { err = cci_check_error (ccErrBadParam); }
1031     if (!io_reply_data      ) { err = cci_check_error (ccErrBadParam); }
1032 
1033     if (!err) {
1034         err = krb5int_ipc_stream_read_uint32 (in_request_data, &cred_vers);
1035     }
1036 
1037     if (!err) {
1038         if (cred_vers == cc_credentials_v5) {
1039             io_ccache->kdc_time_offset_v5 = 0;
1040             io_ccache->kdc_time_offset_v5_valid = 0;
1041 
1042         } else {
1043             err = cci_check_error (ccErrBadCredentialsVersion);
1044         }
1045     }
1046 
1047     if (!err) {
1048         err = ccs_ccache_changed (io_ccache, io_cache_collection);
1049     }
1050 
1051     return cci_check_error (err);
1052 }
1053 
1054 #ifdef TARGET_OS_MAC
1055 #pragma mark -
1056 #endif
1057 
1058 /* ------------------------------------------------------------------------ */
1059 
1060 cc_int32 ccs_ccache_handle_message (ccs_pipe_t              in_client_pipe,
1061                                     ccs_pipe_t              in_reply_pipe,
1062                                     ccs_ccache_t            io_ccache,
1063                                     ccs_cache_collection_t  io_cache_collection,
1064                                     enum cci_msg_id_t       in_request_name,
1065                                     k5_ipc_stream            in_request_data,
1066                                     cc_uint32              *out_will_block,
1067                                     k5_ipc_stream           *out_reply_data)
1068 {
1069     cc_int32 err = ccNoError;
1070     cc_uint32 will_block = 0;
1071     k5_ipc_stream reply_data = NULL;
1072 
1073     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
1074     if (!ccs_pipe_valid (in_reply_pipe) ) { err = cci_check_error (ccErrBadParam); }
1075     if (!io_cache_collection            ) { err = cci_check_error (ccErrBadParam); }
1076     if (!in_request_data                ) { err = cci_check_error (ccErrBadParam); }
1077     if (!out_will_block                 ) { err = cci_check_error (ccErrBadParam); }
1078     if (!out_reply_data                 ) { err = cci_check_error (ccErrBadParam); }
1079 
1080     if (!err) {
1081         err = krb5int_ipc_stream_new (&reply_data);
1082     }
1083 
1084     if (!err) {
1085         if (in_request_name == cci_ccache_destroy_msg_id) {
1086             err = ccs_ccache_destroy (io_ccache, io_cache_collection,
1087                                       in_request_data, reply_data);
1088 
1089         } else if (in_request_name == cci_ccache_set_default_msg_id) {
1090             err = ccs_ccache_set_default (io_ccache, io_cache_collection,
1091                                           in_request_data, reply_data);
1092 
1093         } else if (in_request_name == cci_ccache_get_credentials_version_msg_id) {
1094             err = ccs_ccache_get_credentials_version (io_ccache, io_cache_collection,
1095                                                       in_request_data, reply_data);
1096 
1097         } else if (in_request_name == cci_ccache_get_name_msg_id) {
1098             err = ccs_ccache_get_name (io_ccache, io_cache_collection,
1099                                        in_request_data, reply_data);
1100 
1101         } else if (in_request_name == cci_ccache_get_principal_msg_id) {
1102             err = ccs_ccache_get_principal (io_ccache, io_cache_collection,
1103                                             in_request_data, reply_data);
1104 
1105         } else if (in_request_name == cci_ccache_set_principal_msg_id) {
1106             err = ccs_ccache_set_principal (io_ccache, io_cache_collection,
1107                                             in_request_data, reply_data);
1108 
1109         } else if (in_request_name == cci_ccache_store_credentials_msg_id) {
1110             err = ccs_ccache_store_credentials (io_ccache, io_cache_collection,
1111                                                 in_request_data, reply_data);
1112 
1113         } else if (in_request_name == cci_ccache_remove_credentials_msg_id) {
1114             err = ccs_ccache_remove_credentials (io_ccache, io_cache_collection,
1115                                                  in_request_data, reply_data);
1116 
1117         } else if (in_request_name == cci_ccache_new_credentials_iterator_msg_id) {
1118             err = ccs_ccache_new_credentials_iterator (io_ccache,
1119                                                        io_cache_collection,
1120                                                        in_client_pipe,
1121                                                        in_request_data,
1122                                                        reply_data);
1123 
1124         } else if (in_request_name == cci_ccache_move_msg_id) {
1125             err = ccs_ccache_move (io_ccache, io_cache_collection,
1126                                    in_request_data, reply_data);
1127 
1128         } else if (in_request_name == cci_ccache_lock_msg_id) {
1129             err = ccs_ccache_lock (in_client_pipe, in_reply_pipe,
1130                                    io_ccache, io_cache_collection,
1131                                    in_request_data,
1132                                    &will_block, reply_data);
1133 
1134         } else if (in_request_name == cci_ccache_unlock_msg_id) {
1135             err = ccs_ccache_unlock (in_client_pipe,
1136                                      io_ccache, io_cache_collection,
1137                                      in_request_data, reply_data);
1138 
1139         } else if (in_request_name == cci_ccache_get_last_default_time_msg_id) {
1140             err = ccs_ccache_get_last_default_time (io_ccache, io_cache_collection,
1141                                                     in_request_data, reply_data);
1142 
1143         } else if (in_request_name == cci_ccache_get_change_time_msg_id) {
1144             err = ccs_ccache_get_change_time (io_ccache, io_cache_collection,
1145                                               in_request_data, reply_data);
1146 
1147         } else if (in_request_name == cci_ccache_wait_for_change_msg_id) {
1148             err = ccs_ccache_wait_for_change (in_client_pipe, in_reply_pipe,
1149 					      io_ccache, io_cache_collection,
1150 					      in_request_data, reply_data,
1151 					      &will_block);
1152 
1153         } else if (in_request_name == cci_ccache_get_kdc_time_offset_msg_id) {
1154             err = ccs_ccache_get_kdc_time_offset (io_ccache, io_cache_collection,
1155                                                   in_request_data, reply_data);
1156 
1157         } else if (in_request_name == cci_ccache_set_kdc_time_offset_msg_id) {
1158             err = ccs_ccache_set_kdc_time_offset (io_ccache, io_cache_collection,
1159                                                   in_request_data, reply_data);
1160 
1161         } else if (in_request_name == cci_ccache_clear_kdc_time_offset_msg_id) {
1162             err = ccs_ccache_clear_kdc_time_offset (io_ccache, io_cache_collection,
1163                                                     in_request_data, reply_data);
1164 
1165         } else {
1166             err = ccErrBadInternalMessage;
1167         }
1168     }
1169 
1170     if (!err) {
1171         *out_will_block = will_block;
1172         if (!will_block) {
1173             *out_reply_data = reply_data;
1174             reply_data = NULL; /* take ownership */
1175         } else {
1176             *out_reply_data = NULL;
1177         }
1178     }
1179 
1180     krb5int_ipc_stream_release (reply_data);
1181 
1182     return cci_check_error (err);
1183 }
1184