1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights
3 * reserved.
4 */
5
6
7 /*
8 * lib/krb5/rcache/rc_file.c
9 *
10 * This file of the Kerberos V5 software is derived from public-domain code
11 * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
12 *
13 */
14
15
16 /*
17 * An implementation for the default replay cache type.
18 */
19 /* Solaris Kerberos */
20 #define FREE_RC(x) ((void) free((char *) (x)))
21 #include "rc_common.h"
22 #include "rc_file.h"
23
24 /* Solaris Kerberos */
25 #include <kstat.h>
26 #include <atomic.h>
27 #include <assert.h>
28 #include <syslog.h>
29
30 /*
31 * Solaris: The NOIOSTUFF macro has been taken out for the Solaris version
32 * of this module, because this has been split into a separate mem rcache.
33 */
34
35 /* of course, list is backwards from file */
36 /* hash could be forwards since we have to search on match, but naaaah */
37
38 static int
rc_store(krb5_context context,krb5_rcache id,krb5_donot_replay * rep)39 rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
40 {
41 struct file_data *t = (struct file_data *)id->data;
42 int rephash;
43 struct authlist *ta;
44 krb5_int32 time;
45
46 rephash = hash(rep, t->hsize);
47
48 /* Solaris: calling krb_timeofday() here, once for better perf. */
49 krb5_timeofday(context, &time);
50
51 /* Solaris: calling alive() on rep since it doesn't make sense to store an
52 * expired replay.
53 */
54 if (alive(context, rep, t->lifespan, time) == CMP_EXPIRED){
55 return CMP_EXPIRED;
56 }
57
58 for (ta = t->h[rephash]; ta; ta = ta->nh) {
59 switch(cmp(&ta->rep, rep))
60 {
61 case CMP_REPLAY:
62 return CMP_REPLAY;
63 case CMP_HOHUM:
64 if (alive(context, &ta->rep, t->lifespan, time) == CMP_EXPIRED)
65 t->nummisses++;
66 else
67 t->numhits++;
68 break;
69 default:
70 ; /* wtf? */
71 }
72 }
73
74 if (!(ta = (struct authlist *) malloc(sizeof(struct authlist))))
75 return CMP_MALLOC;
76 ta->rep = *rep;
77 if (!(ta->rep.client = strdup(rep->client))) {
78 FREE_RC(ta);
79 return CMP_MALLOC;
80 }
81 if (!(ta->rep.server = strdup(rep->server))) {
82 FREE_RC(ta->rep.client);
83 FREE_RC(ta);
84 return CMP_MALLOC;
85 }
86 ta->na = t->a; t->a = ta;
87 ta->nh = t->h[rephash]; t->h[rephash] = ta;
88
89 return CMP_HOHUM;
90 }
91
92 /*ARGSUSED*/
93 char * KRB5_CALLCONV
krb5_rc_file_get_name(krb5_context context,krb5_rcache id)94 krb5_rc_file_get_name(krb5_context context, krb5_rcache id)
95 {
96 return ((struct file_data *) (id->data))->name;
97 }
98
99 /*ARGSUSED*/
100 krb5_error_code KRB5_CALLCONV
krb5_rc_file_get_span(krb5_context context,krb5_rcache id,krb5_deltat * lifespan)101 krb5_rc_file_get_span(krb5_context context, krb5_rcache id,
102 krb5_deltat *lifespan)
103 {
104 krb5_error_code err;
105 struct file_data *t;
106
107 err = k5_mutex_lock(&id->lock);
108 if (err)
109 return err;
110 t = (struct file_data *) id->data;
111 *lifespan = t->lifespan;
112 k5_mutex_unlock(&id->lock);
113 return 0;
114 }
115
116 static krb5_error_code KRB5_CALLCONV
krb5_rc_file_init_locked(krb5_context context,krb5_rcache id,krb5_deltat lifespan)117 krb5_rc_file_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
118 {
119 struct file_data *t = (struct file_data *)id->data;
120 krb5_error_code retval;
121
122 t->lifespan = lifespan ? lifespan : context->clockskew;
123 /* default to clockskew from the context */
124 if ((retval = krb5_rc_io_creat(context, &t->d, &t->name))) {
125 return retval;
126 }
127 if ((krb5_rc_io_write(context, &t->d,
128 (krb5_pointer) &t->lifespan, sizeof(t->lifespan))
129 || krb5_rc_io_sync(context, &t->d))) {
130 return KRB5_RC_IO;
131 }
132 return 0;
133 }
134
135 krb5_error_code KRB5_CALLCONV
krb5_rc_file_init(krb5_context context,krb5_rcache id,krb5_deltat lifespan)136 krb5_rc_file_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
137 {
138 krb5_error_code retval;
139
140 retval = k5_mutex_lock(&id->lock);
141 if (retval)
142 return retval;
143 retval = krb5_rc_file_init_locked(context, id, lifespan);
144 k5_mutex_unlock(&id->lock);
145 return retval;
146 }
147
148 /* Called with the mutex already locked. */
149 krb5_error_code
krb5_rc_file_close_no_free(krb5_context context,krb5_rcache id)150 krb5_rc_file_close_no_free(krb5_context context, krb5_rcache id)
151 {
152 struct file_data *t = (struct file_data *)id->data;
153 struct authlist *q;
154
155 if (t->h)
156 FREE_RC(t->h);
157 if (t->name)
158 FREE_RC(t->name);
159 while ((q = t->a))
160 {
161 t->a = q->na;
162 FREE_RC(q->rep.client);
163 FREE_RC(q->rep.server);
164 FREE_RC(q);
165 }
166 if (t->d.fd >= 0)
167 (void) krb5_rc_io_close(context, &t->d);
168 FREE_RC(t);
169 id->data = NULL;
170 return 0;
171 }
172
173 krb5_error_code KRB5_CALLCONV
krb5_rc_file_close(krb5_context context,krb5_rcache id)174 krb5_rc_file_close(krb5_context context, krb5_rcache id)
175 {
176 krb5_error_code retval;
177 retval = k5_mutex_lock(&id->lock);
178 if (retval)
179 return retval;
180 krb5_rc_file_close_no_free(context, id);
181 k5_mutex_unlock(&id->lock);
182 k5_mutex_destroy(&id->lock);
183 free(id);
184 return 0;
185 }
186
187 krb5_error_code KRB5_CALLCONV
krb5_rc_file_destroy(krb5_context context,krb5_rcache id)188 krb5_rc_file_destroy(krb5_context context, krb5_rcache id)
189 {
190 if (krb5_rc_io_destroy(context, &((struct file_data *) (id->data))->d))
191 return KRB5_RC_IO;
192 return krb5_rc_file_close(context, id);
193 }
194
195 /*ARGSUSED*/
196 krb5_error_code KRB5_CALLCONV
krb5_rc_file_resolve(krb5_context context,krb5_rcache id,char * name)197 krb5_rc_file_resolve(krb5_context context, krb5_rcache id, char *name)
198 {
199 struct file_data *t = 0;
200 krb5_error_code retval;
201
202 /* allocate id? no */
203 if (!(t = (struct file_data *) malloc(sizeof(struct file_data))))
204 return KRB5_RC_MALLOC;
205 id->data = (krb5_pointer) t;
206 memset(t, 0, sizeof(struct file_data));
207 if (name) {
208 t->name = malloc(strlen(name)+1);
209 if (!t->name) {
210 retval = KRB5_RC_MALLOC;
211 goto cleanup;
212 }
213 strcpy(t->name, name);
214 } else
215 t->name = 0;
216 t->numhits = t->nummisses = 0;
217 t->hsize = HASHSIZE; /* no need to store---it's memory-only */
218 t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *));
219 if (!t->h) {
220 retval = KRB5_RC_MALLOC;
221 goto cleanup;
222 }
223 memset(t->h, 0, t->hsize*sizeof(struct authlist *));
224 t->a = (struct authlist *) 0;
225 t->d.fd = -1;
226 t->recovering = 0;
227 return 0;
228
229 cleanup:
230 if (t) {
231 if (t->name)
232 krb5_xfree(t->name);
233 if (t->h)
234 krb5_xfree(t->h);
235 krb5_xfree(t);
236 id->data = NULL;
237 }
238 return retval;
239 }
240
241 /*ARGSUSED*/
242 void
krb5_rc_free_entry(krb5_context context,krb5_donot_replay ** rep)243 krb5_rc_free_entry(krb5_context context, krb5_donot_replay **rep)
244 {
245 krb5_donot_replay *rp = *rep;
246
247 *rep = NULL;
248 if (rp)
249 {
250 if (rp->client)
251 free(rp->client);
252
253 if (rp->server)
254 free(rp->server);
255 rp->client = NULL;
256 rp->server = NULL;
257 free(rp);
258 }
259 }
260
261 static krb5_error_code
krb5_rc_io_fetch(krb5_context context,struct file_data * t,krb5_donot_replay * rep,int maxlen)262 krb5_rc_io_fetch(krb5_context context, struct file_data *t,
263 krb5_donot_replay *rep, int maxlen)
264 {
265 unsigned int len;
266 krb5_error_code retval;
267
268 rep->client = rep->server = 0;
269
270 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len,
271 sizeof(len));
272 if (retval)
273 return retval;
274
275 if ((len <= 0) || (len >= maxlen))
276 return KRB5_RC_IO_EOF;
277
278 rep->client = malloc (len);
279 if (!rep->client)
280 return KRB5_RC_MALLOC;
281
282 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->client, len);
283 if (retval)
284 goto errout;
285
286 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len,
287 sizeof(len));
288 if (retval)
289 goto errout;
290
291 if ((len <= 0) || (len >= maxlen)) {
292 retval = KRB5_RC_IO_EOF;
293 goto errout;
294 }
295
296 rep->server = malloc (len);
297 if (!rep->server) {
298 retval = KRB5_RC_MALLOC;
299 goto errout;
300 }
301
302 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->server, len);
303 if (retval)
304 goto errout;
305
306 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->cusec,
307 sizeof(rep->cusec));
308 if (retval)
309 goto errout;
310
311 retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->ctime,
312 sizeof(rep->ctime));
313 if (retval)
314 goto errout;
315
316 return 0;
317
318 errout:
319 if (rep->client)
320 krb5_xfree(rep->client);
321 if (rep->server)
322 krb5_xfree(rep->server);
323 rep->client = rep->server = 0;
324 return retval;
325 }
326
327
328 static krb5_error_code
329 krb5_rc_file_expunge_locked(krb5_context context, krb5_rcache id);
330
331 static krb5_error_code
krb5_rc_file_recover_locked(krb5_context context,krb5_rcache id)332 krb5_rc_file_recover_locked(krb5_context context, krb5_rcache id)
333 {
334 struct file_data *t = (struct file_data *)id->data;
335 krb5_donot_replay *rep = 0;
336 krb5_error_code retval;
337 long max_size;
338 int expired_entries = 0;
339
340 if ((retval = krb5_rc_io_open(context, &t->d, t->name))) {
341 return retval;
342 }
343
344 t->recovering = 1;
345
346 max_size = krb5_rc_io_size(context, &t->d);
347
348 rep = NULL;
349 if (krb5_rc_io_read(context, &t->d, (krb5_pointer) &t->lifespan,
350 sizeof(t->lifespan))) {
351 retval = KRB5_RC_IO;
352 goto io_fail;
353 }
354
355 if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) {
356 retval = KRB5_RC_MALLOC;
357 goto io_fail;
358 }
359 rep->client = NULL;
360 rep->server = NULL;
361
362 /* now read in each auth_replay and insert into table */
363 for (;;) {
364 if (krb5_rc_io_mark(context, &t->d)) {
365 retval = KRB5_RC_IO;
366 goto io_fail;
367 }
368
369 retval = krb5_rc_io_fetch (context, t, rep, (int) max_size);
370
371 if (retval == KRB5_RC_IO_EOF)
372 break;
373 else if (retval != 0)
374 goto io_fail;
375
376 /* Solaris: made the change below for better perf. */
377 switch (rc_store(context, id, rep)) {
378 case CMP_EXPIRED:
379 expired_entries++;
380 break;
381 case CMP_MALLOC:
382 retval = KRB5_RC_MALLOC;
383 goto io_fail;
384 break;
385 }
386 /*
387 * free fields allocated by rc_io_fetch
388 */
389 FREE_RC(rep->server);
390 FREE_RC(rep->client);
391 rep->server = 0;
392 rep->client = 0;
393 }
394 retval = 0;
395 krb5_rc_io_unmark(context, &t->d);
396 /*
397 * An automatic expunge here could remove the need for
398 * mark/unmark but that would be inefficient.
399 */
400 io_fail:
401 krb5_rc_free_entry(context, &rep);
402 if (retval)
403 krb5_rc_io_close(context, &t->d);
404 else if (expired_entries > EXCESSREPS)
405 retval = krb5_rc_file_expunge_locked(context, id);
406 t->recovering = 0;
407 return retval;
408 }
409
410 krb5_error_code KRB5_CALLCONV
krb5_rc_file_recover(krb5_context context,krb5_rcache id)411 krb5_rc_file_recover(krb5_context context, krb5_rcache id)
412 {
413 krb5_error_code ret;
414 ret = k5_mutex_lock(&id->lock);
415 if (ret)
416 return ret;
417 ret = krb5_rc_file_recover_locked(context, id);
418 k5_mutex_unlock(&id->lock);
419 return ret;
420 }
421
422 krb5_error_code KRB5_CALLCONV
krb5_rc_file_recover_or_init(krb5_context context,krb5_rcache id,krb5_deltat lifespan)423 krb5_rc_file_recover_or_init(krb5_context context, krb5_rcache id,
424 krb5_deltat lifespan)
425 {
426 krb5_error_code retval;
427
428 retval = k5_mutex_lock(&id->lock);
429 if (retval)
430 return retval;
431 retval = krb5_rc_file_recover_locked(context, id);
432 if (retval)
433 retval = krb5_rc_file_init_locked(context, id, lifespan);
434 k5_mutex_unlock(&id->lock);
435 return retval;
436 }
437
438 static krb5_error_code
krb5_rc_io_store(krb5_context context,struct file_data * t,krb5_donot_replay * rep)439 krb5_rc_io_store(krb5_context context, struct file_data *t,
440 krb5_donot_replay *rep)
441 {
442 int clientlen, serverlen, len;
443 char *buf, *ptr;
444 krb5_error_code ret;
445
446 clientlen = strlen(rep->client) + 1;
447 serverlen = strlen(rep->server) + 1;
448 len = sizeof(clientlen) + clientlen + sizeof(serverlen) + serverlen +
449 sizeof(rep->cusec) + sizeof(rep->ctime);
450 buf = malloc(len);
451 if (buf == 0)
452 return KRB5_RC_MALLOC;
453 ptr = buf;
454 memcpy(ptr, &clientlen, sizeof(clientlen)); ptr += sizeof(clientlen);
455 memcpy(ptr, rep->client, clientlen); ptr += clientlen;
456 memcpy(ptr, &serverlen, sizeof(serverlen)); ptr += sizeof(serverlen);
457 memcpy(ptr, rep->server, serverlen); ptr += serverlen;
458 memcpy(ptr, &rep->cusec, sizeof(rep->cusec)); ptr += sizeof(rep->cusec);
459 memcpy(ptr, &rep->ctime, sizeof(rep->ctime)); ptr += sizeof(rep->ctime);
460
461 ret = krb5_rc_io_write(context, &t->d, buf, len);
462 free(buf);
463 return ret;
464 }
465
466 static krb5_error_code krb5_rc_file_expunge_locked(krb5_context, krb5_rcache);
467
468 /*
469 * Solaris Kerberos
470 *
471 * Get time of boot. This is needed for fsync()-less operation. See below.
472 *
473 * Cstyle note: MIT style used here.
474 */
475 static
476 krb5_timestamp
get_boot_time(krb5_timestamp now)477 get_boot_time(krb5_timestamp now)
478 {
479 krb5_timestamp bt;
480 kstat_ctl_t *kc;
481 kstat_t *k;
482 kstat_named_t *kn;
483 kid_t rc;
484
485 /*
486 * We use the boot_time kstat from the "unix" module.
487 *
488 * It's hard to determine the interface stability of kstats. To be safe
489 * we treat boot_time with extra care: if it disappears or is renamed,
490 * or if its type changes, or if its value appears to be in the future,
491 * then we fail to get boot time and the rcache falls back on slow
492 * behavior (fsync()ing at every write). If this kstat should produce a
493 * time less than the actual boot time then this increases the chance of
494 * post-crash replays of Authenticators whose rcache entries were not
495 * fsync()ed and were lost.
496 *
497 * We consider it extremely unlikely that this kstat will ever change at
498 * all however, much less to change in such a way that it will return
499 * the wrong boot time as an unsigned 32-bit integer. If we fail to
500 * find the kstat we expect we log loudly even though the rcache remains
501 * functional.
502 */
503 if ((kc = kstat_open()) == NULL ||
504 (k = kstat_lookup(kc, "unix", 0, "system_misc")) == NULL ||
505 (rc = kstat_read(kc, k, NULL)) == -1 ||
506 (kn = kstat_data_lookup(k, "boot_time")) == NULL ||
507 /* check that the kstat's type hasn't changed */
508 kn->data_type != KSTAT_DATA_UINT32 ||
509 /* boot_time value sanity check */
510 kn->value.i32 > now ||
511 /* krb5_timestamp is int32_t, this kstat is uint32_t; 2038 problem! */
512 kn->value.i32 < 0) {
513
514 /* Return boot time to 1 to indicate failure to get actual boot time */
515 bt = 1;
516 syslog(LOG_ALERT, "Alert: Unable to determine boot_time (boot_time "
517 "kstat removed or changed?); rcache will be functional, but slow");
518 } else {
519 bt = kn->value.i32;
520 }
521
522 if (kc != NULL)
523 (void) kstat_close(kc);
524
525 return (bt);
526 }
527
528 /*
529 * Solaris Kerberos
530 *
531 * We optimize the rcache by foregoing fsync() in the most common cases.
532 * Foregoing fsync() requires an early boot procedure to ensure that we
533 * never accept an authenticator that could be a replay of one whose
534 * rcache entry we've lost.
535 *
536 * We do this by picking an arbitrary, small time delta such that
537 * storing any krb5_donot_replays whose ctime is further into the future
538 * than now + that small delta causes an fsync() of the rcache. Early
539 * after booting we must reject all krb5_donot_replays whose ctime falls
540 * before time of boot + that delta.
541 *
542 * This works well as long as client clocks are reasonably synchronized
543 * or as long as they use kdc_timesync. Clients with clocks faster than
544 * this delta will find their AP exchanges are slower than clients with
545 * good or slow clocks. Clients with very slow clocks will find that
546 * their AP-REQs are rejected by servers that have just booted. In all
547 * other cases clients will notice only that AP exchanges are much
548 * faster as a result of the missing fsync()s.
549 *
550 * KRB5_RC_FSYNCLESS_FAST_SKEW is that time delta, in seconds. Five
551 * seconds seems like a reasonable delta. If it takes more than five
552 * seconds from the time the kernel initializes itself to the time when
553 * a kerberized system starts, and clients have good clocks or use
554 * kdc_timesync, then no authenticators will be rejected.
555 */
556 #define KRB5_RC_FSYNCLESS_FAST_SKEW 5
557
558 krb5_error_code KRB5_CALLCONV
krb5_rc_file_store(krb5_context context,krb5_rcache id,krb5_donot_replay * rep)559 krb5_rc_file_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
560 {
561 krb5_error_code ret;
562 struct file_data *t;
563 static krb5_timestamp boot_time = 0;
564 krb5_timestamp now;
565
566 if (krb5_timeofday(context, &now) != 0)
567 /* No time of day -> broken rcache */
568 return KRB5KRB_AP_ERR_REPEAT;
569
570 /*
571 * Solaris Kerberos
572 *
573 * if boot_time <= 1 -> we always fsync() (see below)
574 * if boot_time == 1 -> don't bother trying to get it again (as it could be
575 * a slow operation)
576 */
577 if (boot_time == 0) {
578 krb5_timestamp btime = get_boot_time(now);
579
580 assert(sizeof (boot_time) == sizeof (krb5_int32) && sizeof (krb5_int32) == sizeof (uint32_t));
581 (void) atomic_cas_32((uint32_t *)&boot_time, 0, btime);
582 }
583
584 /*
585 * Solaris Kerberos
586 *
587 * fsync()-less-ness requires safety. If we just booted then we want to
588 * reject all Authenticators whose timestamps are old enough that we might
589 * not have fsync()ed rcache entries for them prior to booting. See
590 * comment above where KRB5_RC_FSYNCLESS_FAST_SKEW is defined. See
591 * also below, where krb5_rc_io_sync() is called.
592 *
593 * If we could tell here the time of the last system crash then we
594 * could do better because we could know that the rcache has been
595 * synced to disk. But there's no reliable way to detect past
596 * crashes in this code; getting the time of boot is hard enough.
597 */
598 if (boot_time > 1 &&
599 rep->ctime < (boot_time + KRB5_RC_FSYNCLESS_FAST_SKEW))
600 /*
601 * A better error code would be nice; clients might then know
602 * that nothing's necessarily wrong with their (or our) clocks
603 * and that they should just wait a while (or even set their
604 * clock offset slow so that their timestamps then appear into
605 * the future, where we'd accept them.
606 *
607 * KRB5KRB_AP_ERR_SKEW will just have to do.
608 */
609 return KRB5KRB_AP_ERR_SKEW;
610
611 ret = k5_mutex_lock(&id->lock);
612 if (ret)
613 return ret;
614
615 t = (struct file_data *)id->data;
616
617 switch(rc_store(context, id,rep)) {
618 case CMP_MALLOC:
619 k5_mutex_unlock(&id->lock);
620 return KRB5_RC_MALLOC;
621 case CMP_REPLAY:
622 k5_mutex_unlock(&id->lock);
623 return KRB5KRB_AP_ERR_REPEAT;
624 case CMP_EXPIRED:
625 k5_mutex_unlock(&id->lock);
626 return KRB5KRB_AP_ERR_SKEW;
627 case CMP_HOHUM: break;
628 default: /* wtf? */ ;
629 }
630 ret = krb5_rc_io_store (context, t, rep);
631 if (ret) {
632 k5_mutex_unlock(&id->lock);
633 return ret;
634 }
635 /* Shall we automatically expunge? */
636 if (t->nummisses > t->numhits + EXCESSREPS)
637 {
638 /* Expunge calls krb5_rc_io_sync() */
639 ret = krb5_rc_file_expunge_locked(context, id);
640 k5_mutex_unlock(&id->lock);
641 return ret;
642 }
643 /* Solaris Kerberos */
644 else if (boot_time <= 1 || rep->ctime > (now + KRB5_RC_FSYNCLESS_FAST_SKEW))
645 {
646 /*
647 * fsync() only when necessary:
648 *
649 * - on expunge (see above)
650 * - if we don't know when we booted
651 * - if rep->ctime is too far into the future
652 */
653 if (krb5_rc_io_sync(context, &t->d)) {
654 k5_mutex_unlock(&id->lock);
655 return KRB5_RC_IO;
656 }
657 }
658 k5_mutex_unlock(&id->lock);
659 return 0;
660 }
661
662 static krb5_error_code
krb5_rc_file_expunge_locked(krb5_context context,krb5_rcache id)663 krb5_rc_file_expunge_locked(krb5_context context, krb5_rcache id)
664 {
665 struct file_data *t = (struct file_data *)id->data;
666 struct authlist *q;
667 char *name;
668 krb5_error_code retval = 0;
669 krb5_rcache tmp;
670 krb5_deltat lifespan = t->lifespan; /* save original lifespan */
671
672 if (! t->recovering) {
673 name = t->name;
674 t->name = 0; /* Clear name so it isn't freed */
675 (void) krb5_rc_file_close_no_free(context, id);
676 retval = krb5_rc_file_resolve(context, id, name);
677 free(name);
678 if (retval)
679 return retval;
680 retval = krb5_rc_file_recover_locked(context, id);
681 if (retval)
682 return retval;
683 t = (struct file_data *)id->data; /* point to recovered cache */
684 }
685
686 tmp = (krb5_rcache) malloc(sizeof(*tmp));
687 if (!tmp)
688 return ENOMEM;
689
690 retval = k5_mutex_init(&tmp->lock);
691 if (retval) {
692 free(tmp);
693 return retval;
694 }
695
696 tmp->ops = &krb5_rc_file_ops;
697 if ((retval = krb5_rc_file_resolve(context, tmp, 0)) != 0)
698 goto out;
699 if ((retval = krb5_rc_initialize(context, tmp, lifespan)) != 0)
700 goto out;
701 for (q = t->a;q;q = q->na) {
702 if (krb5_rc_io_store (context, (struct file_data *)tmp->data, &q->rep)) {
703 retval = KRB5_RC_IO;
704 goto out;
705 }
706 }
707 if (krb5_rc_io_sync(context, &t->d)) {
708 retval = KRB5_RC_IO;
709 goto out;
710 }
711 if (krb5_rc_io_move(context, &t->d, &((struct file_data *)tmp->data)->d))
712 retval = KRB5_RC_IO;
713
714 out:
715 /*
716 * krb5_rc_file_close() will free the tmp struct and it's members that the
717 * previous functions had allocated.
718 */
719 (void) krb5_rc_file_close(context, tmp);
720
721 return (retval);
722 }
723
724 krb5_error_code KRB5_CALLCONV
krb5_rc_file_expunge(krb5_context context,krb5_rcache id)725 krb5_rc_file_expunge(krb5_context context, krb5_rcache id)
726 {
727 krb5_error_code ret;
728 ret = k5_mutex_lock(&id->lock);
729 if (ret)
730 return ret;
731 ret = krb5_rc_file_expunge_locked(context, id);
732 k5_mutex_unlock(&id->lock);
733 return ret;
734 }
735