cc42f461f21f9959bc842fb2d02964d3de7907e0
[platform/upstream/krb5.git] / src / lib / krb5 / rcache / rc_dfl.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/rcache/rc_dfl.c */
3 /*
4  * This file of the Kerberos V5 software is derived from public-domain code
5  * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
6  *
7  */
8
9 /*
10  * An implementation for the default replay cache type.
11  */
12 #include "rc_base.h"
13 #include "rc_dfl.h"
14 #include "rc_io.h"
15 #include "k5-int.h"
16 #include "rc-int.h"
17
18 /*
19  * If NOIOSTUFF is defined at compile time, dfl rcaches will be per-process.
20  */
21
22 /*
23   Local stuff:
24
25   static int hash(krb5_donot_replay *rep, int hsize)
26   returns hash value of *rep, between 0 and hsize - 1
27   HASHSIZE
28   size of hash table (constant), can be preset
29   static int cmp(krb5_donot_replay *old, krb5_donot_replay *new, krb5_deltat t)
30   compare old and new; return CMP_REPLAY or CMP_HOHUM
31   static int alive(krb5_context, krb5_donot_replay *new, krb5_deltat t)
32   see if new is still alive; return CMP_EXPIRED or CMP_HOHUM
33   CMP_MALLOC, CMP_EXPIRED, CMP_REPLAY, CMP_HOHUM
34   return codes from cmp(), alive(), and store()
35   struct dfl_data
36   data stored in this cache type, namely "dfl"
37   struct authlist
38   multilinked list of reps
39   static int rc_store(context, krb5_rcache id, krb5_donot_replay *rep)
40   store rep in cache id; return CMP_REPLAY if replay, else CMP_MALLOC/CMP_HOHUM
41
42 */
43
44 #ifndef HASHSIZE
45 #define HASHSIZE 997 /* a convenient prime */
46 #endif
47
48 #ifndef EXCESSREPS
49 #define EXCESSREPS 30
50 #endif
51
52 /*
53  * The rcache will be automatically expunged when the number of
54  * expired krb5_donot_replays encountered incidentally in searching
55  * exceeds the number of live krb5_donot_replays by EXCESSREPS. With
56  * the defaults here, a typical cache might build up some 10K of
57  * expired krb5_donot_replays before an automatic expunge, with the
58  * waste basically independent of the number of stores per minute.
59  *
60  * The rcache will also automatically be expunged when it encounters
61  * more than EXCESSREPS expired entries when recovering a cache in
62  * dfl_recover.
63  */
64
65 static unsigned int
66 hash(krb5_donot_replay *rep, unsigned int hsize)
67 {
68     unsigned int h = rep->cusec + rep->ctime;
69     h += *rep->server;
70     h += *rep->client;
71     return h % hsize;
72 }
73
74 #define CMP_MALLOC -3
75 #define CMP_EXPIRED -2
76 #define CMP_REPLAY -1
77 #define CMP_HOHUM 0
78
79 /*ARGSUSED*/
80 static int
81 cmp(krb5_donot_replay *old, krb5_donot_replay *new1, krb5_deltat t)
82 {
83     if ((old->cusec == new1->cusec) && /* most likely to distinguish */
84         (old->ctime == new1->ctime) &&
85         (strcmp(old->client, new1->client) == 0) &&
86         (strcmp(old->server, new1->server) == 0)) { /* always true */
87         /* If both records include message hashes, compare them as well. */
88         if (old->msghash == NULL || new1->msghash == NULL ||
89             strcmp(old->msghash, new1->msghash) == 0)
90             return CMP_REPLAY;
91     }
92     return CMP_HOHUM;
93 }
94
95 static int
96 alive(krb5_int32 mytime, krb5_donot_replay *new1, krb5_deltat t)
97 {
98     if (mytime == 0)
99         return CMP_HOHUM; /* who cares? */
100     /* I hope we don't have to worry about overflow */
101     if (new1->ctime + t < mytime)
102         return CMP_EXPIRED;
103     return CMP_HOHUM;
104 }
105
106 struct dfl_data
107 {
108     char *name;
109     krb5_deltat lifespan;
110     unsigned int hsize;
111     int numhits;
112     int nummisses;
113     struct authlist **h;
114     struct authlist *a;
115 #ifndef NOIOSTUFF
116     krb5_rc_iostuff d;
117 #endif
118     char recovering;
119 };
120
121 struct authlist
122 {
123     krb5_donot_replay rep;
124     struct authlist *na;
125     struct authlist *nh;
126 };
127
128 /* of course, list is backwards from file */
129 /* hash could be forwards since we have to search on match, but naaaah */
130
131 static int
132 rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep,
133          krb5_int32 now, krb5_boolean fromfile)
134 {
135     struct dfl_data *t = (struct dfl_data *)id->data;
136     unsigned int rephash;
137     struct authlist *ta;
138
139     rephash = hash(rep, t->hsize);
140
141     for (ta = t->h[rephash]; ta; ta = ta->nh) {
142         switch(cmp(&ta->rep, rep, t->lifespan))
143         {
144         case CMP_REPLAY:
145             if (fromfile) {
146                 /*
147                  * This is an expected collision between a hash
148                  * extension record and a normal-format record.  Make
149                  * sure the message hash is included in the stored
150                  * record and carry on.
151                  */
152                 if (!ta->rep.msghash && rep->msghash) {
153                     if (!(ta->rep.msghash = strdup(rep->msghash)))
154                         return CMP_MALLOC;
155                 }
156                 return CMP_HOHUM;
157             } else
158                 return CMP_REPLAY;
159         case CMP_HOHUM:
160             if (alive(now, &ta->rep, t->lifespan) == CMP_EXPIRED)
161                 t->nummisses++;
162             else
163                 t->numhits++;
164             break;
165         default:
166             ; /* wtf? */
167         }
168     }
169
170     if (!(ta = (struct authlist *) malloc(sizeof(struct authlist))))
171         return CMP_MALLOC;
172     ta->rep = *rep;
173     ta->rep.client = ta->rep.server = ta->rep.msghash = NULL;
174     if (!(ta->rep.client = strdup(rep->client)))
175         goto error;
176     if (!(ta->rep.server = strdup(rep->server)))
177         goto error;
178     if (rep->msghash && !(ta->rep.msghash = strdup(rep->msghash)))
179         goto error;
180     ta->na = t->a; t->a = ta;
181     ta->nh = t->h[rephash]; t->h[rephash] = ta;
182     return CMP_HOHUM;
183 error:
184     if (ta->rep.client)
185         free(ta->rep.client);
186     if (ta->rep.server)
187         free(ta->rep.server);
188     if (ta->rep.msghash)
189         free(ta->rep.msghash);
190     free(ta);
191     return CMP_MALLOC;
192 }
193
194 char * KRB5_CALLCONV
195 krb5_rc_dfl_get_name(krb5_context context, krb5_rcache id)
196 {
197     return ((struct dfl_data *) (id->data))->name;
198 }
199
200 krb5_error_code KRB5_CALLCONV
201 krb5_rc_dfl_get_span(krb5_context context, krb5_rcache id,
202                      krb5_deltat *lifespan)
203 {
204     krb5_error_code err;
205     struct dfl_data *t;
206
207     err = k5_mutex_lock(&id->lock);
208     if (err)
209         return err;
210     t = (struct dfl_data *) id->data;
211     *lifespan = t->lifespan;
212     k5_mutex_unlock(&id->lock);
213     return 0;
214 }
215
216 static krb5_error_code KRB5_CALLCONV
217 krb5_rc_dfl_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
218 {
219     struct dfl_data *t = (struct dfl_data *)id->data;
220     krb5_error_code retval;
221
222     t->lifespan = lifespan ? lifespan : context->clockskew;
223     /* default to clockskew from the context */
224 #ifndef NOIOSTUFF
225     if ((retval = krb5_rc_io_creat(context, &t->d, &t->name))) {
226         return retval;
227     }
228     if ((krb5_rc_io_write(context, &t->d,
229                           (krb5_pointer) &t->lifespan, sizeof(t->lifespan))
230          || krb5_rc_io_sync(context, &t->d))) {
231         return KRB5_RC_IO;
232     }
233 #endif
234     return 0;
235 }
236
237 krb5_error_code KRB5_CALLCONV
238 krb5_rc_dfl_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
239 {
240     krb5_error_code retval;
241
242     retval = k5_mutex_lock(&id->lock);
243     if (retval)
244         return retval;
245     retval = krb5_rc_dfl_init_locked(context, id, lifespan);
246     k5_mutex_unlock(&id->lock);
247     return retval;
248 }
249
250 /* Called with the mutex already locked.  */
251 krb5_error_code
252 krb5_rc_dfl_close_no_free(krb5_context context, krb5_rcache id)
253 {
254     struct dfl_data *t = (struct dfl_data *)id->data;
255     struct authlist *q;
256
257     free(t->h);
258     if (t->name)
259         free(t->name);
260     while ((q = t->a))
261     {
262         t->a = q->na;
263         free(q->rep.client);
264         free(q->rep.server);
265         if (q->rep.msghash)
266             free(q->rep.msghash);
267         free(q);
268     }
269 #ifndef NOIOSTUFF
270     (void) krb5_rc_io_close(context, &t->d);
271 #endif
272     free(t);
273     return 0;
274 }
275
276 krb5_error_code KRB5_CALLCONV
277 krb5_rc_dfl_close(krb5_context context, krb5_rcache id)
278 {
279     krb5_error_code retval;
280     retval = k5_mutex_lock(&id->lock);
281     if (retval)
282         return retval;
283     krb5_rc_dfl_close_no_free(context, id);
284     k5_mutex_unlock(&id->lock);
285     k5_mutex_destroy(&id->lock);
286     free(id);
287     return 0;
288 }
289
290 krb5_error_code KRB5_CALLCONV
291 krb5_rc_dfl_destroy(krb5_context context, krb5_rcache id)
292 {
293 #ifndef NOIOSTUFF
294     if (krb5_rc_io_destroy(context, &((struct dfl_data *) (id->data))->d))
295         return KRB5_RC_IO;
296 #endif
297     return krb5_rc_dfl_close(context, id);
298 }
299
300 krb5_error_code KRB5_CALLCONV
301 krb5_rc_dfl_resolve(krb5_context context, krb5_rcache id, char *name)
302 {
303     struct dfl_data *t = 0;
304     krb5_error_code retval;
305
306     /* allocate id? no */
307     if (!(t = (struct dfl_data *) calloc(1, sizeof(struct dfl_data))))
308         return KRB5_RC_MALLOC;
309     id->data = (krb5_pointer) t;
310     if (name) {
311         t->name = strdup(name);
312         if (!t->name) {
313             retval = KRB5_RC_MALLOC;
314             goto cleanup;
315         }
316     } else
317         t->name = 0;
318     t->numhits = t->nummisses = 0;
319     t->hsize = HASHSIZE; /* no need to store---it's memory-only */
320     t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *));
321     if (!t->h) {
322         retval = KRB5_RC_MALLOC;
323         goto cleanup;
324     }
325     memset(t->h, 0, t->hsize*sizeof(struct authlist *));
326     t->a = (struct authlist *) 0;
327 #ifndef NOIOSTUFF
328     t->d.fd = -1;
329 #endif
330     t->recovering = 0;
331     return 0;
332
333 cleanup:
334     if (t) {
335         if (t->name)
336             free(t->name);
337         if (t->h)
338             free(t->h);
339         free(t);
340     }
341     return retval;
342 }
343
344 void
345 krb5_rc_free_entry(krb5_context context, krb5_donot_replay **rep)
346 {
347     krb5_donot_replay *rp = *rep;
348
349     *rep = NULL;
350     if (rp)
351     {
352         if (rp->client)
353             free(rp->client);
354         if (rp->server)
355             free(rp->server);
356         if (rp->msghash)
357             free(rp->msghash);
358         rp->client = NULL;
359         rp->server = NULL;
360         rp->msghash = NULL;
361         free(rp);
362     }
363 }
364
365 /*
366  * Parse a string in the format <len>:<data>, with the length
367  * represented in ASCII decimal.  On parse failure, return 0 but set
368  * *result to NULL.
369  */
370 static krb5_error_code
371 parse_counted_string(char **strptr, char **result)
372 {
373     char *str = *strptr, *end;
374     unsigned long len;
375
376     *result = NULL;
377
378     /* Parse the length, expecting a ':' afterwards. */
379     errno = 0;
380     len = strtoul(str, &end, 10);
381     if (errno != 0 || *end != ':' || len > strlen(end + 1))
382         return 0;
383
384     /* Allocate space for *result and copy the data. */
385     *result = malloc(len + 1);
386     if (!*result)
387         return KRB5_RC_MALLOC;
388     memcpy(*result, end + 1, len);
389     (*result)[len] = '\0';
390     *strptr = end + 1 + len;
391     return 0;
392 }
393
394 /*
395  * Hash extension records have the format:
396  *  client = <empty string>
397  *  server = HASH:<msghash> <clientlen>:<client> <serverlen>:<server>
398  * Spaces in the client and server string are represented with
399  * with backslashes.  Client and server lengths are represented in
400  * ASCII decimal (which is different from the 32-bit binary we use
401  * elsewhere in the replay cache).
402  *
403  * On parse failure, we leave the record unmodified.
404  */
405 static krb5_error_code
406 check_hash_extension(krb5_donot_replay *rep)
407 {
408     char *msghash = NULL, *client = NULL, *server = NULL, *str, *end;
409     krb5_error_code retval = 0;
410
411     /* Check if this appears to match the hash extension format. */
412     if (*rep->client)
413         return 0;
414     if (strncmp(rep->server, "HASH:", 5) != 0)
415         return 0;
416
417     /* Parse out the message hash. */
418     str = rep->server + 5;
419     end = strchr(str, ' ');
420     if (!end)
421         return 0;
422     msghash = malloc(end - str + 1);
423     if (!msghash)
424         return KRB5_RC_MALLOC;
425     memcpy(msghash, str, end - str);
426     msghash[end - str] = '\0';
427     str = end + 1;
428
429     /* Parse out the client and server. */
430     retval = parse_counted_string(&str, &client);
431     if (retval != 0 || client == NULL)
432         goto error;
433     if (*str != ' ')
434         goto error;
435     str++;
436     retval = parse_counted_string(&str, &server);
437     if (retval != 0 || server == NULL)
438         goto error;
439     if (*str)
440         goto error;
441
442     free(rep->client);
443     free(rep->server);
444     rep->client = client;
445     rep->server = server;
446     rep->msghash = msghash;
447     return 0;
448
449 error:
450     if (msghash)
451         free(msghash);
452     if (client)
453         free(client);
454     if (server)
455         free(server);
456     return retval;
457 }
458
459 static krb5_error_code
460 krb5_rc_io_fetch(krb5_context context, struct dfl_data *t,
461                  krb5_donot_replay *rep, int maxlen)
462 {
463     int len2;
464     unsigned int len;
465     krb5_error_code retval;
466
467     rep->client = rep->server = rep->msghash = NULL;
468
469     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2,
470                              sizeof(len2));
471     if (retval)
472         return retval;
473
474     if ((len2 <= 0) || (len2 >= maxlen))
475         return KRB5_RC_IO_EOF;
476
477     len = len2;
478     rep->client = malloc (len);
479     if (!rep->client)
480         return KRB5_RC_MALLOC;
481
482     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->client, len);
483     if (retval)
484         goto errout;
485
486     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2,
487                              sizeof(len2));
488     if (retval)
489         goto errout;
490
491     if ((len2 <= 0) || (len2 >= maxlen)) {
492         retval = KRB5_RC_IO_EOF;
493         goto errout;
494     }
495     len = len2;
496
497     rep->server = malloc (len);
498     if (!rep->server) {
499         retval = KRB5_RC_MALLOC;
500         goto errout;
501     }
502
503     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->server, len);
504     if (retval)
505         goto errout;
506
507     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->cusec,
508                              sizeof(rep->cusec));
509     if (retval)
510         goto errout;
511
512     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->ctime,
513                              sizeof(rep->ctime));
514     if (retval)
515         goto errout;
516
517     retval = check_hash_extension(rep);
518     if (retval)
519         goto errout;
520
521     return 0;
522
523 errout:
524     if (rep->client)
525         free(rep->client);
526     if (rep->server)
527         free(rep->server);
528     if (rep->msghash)
529         free(rep->msghash);
530     rep->client = rep->server = 0;
531     return retval;
532 }
533
534
535 static krb5_error_code
536 krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id);
537
538 static krb5_error_code
539 krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
540 {
541 #ifdef NOIOSTUFF
542     return KRB5_RC_NOIO;
543 #else
544
545     struct dfl_data *t = (struct dfl_data *)id->data;
546     krb5_donot_replay *rep = 0;
547     krb5_error_code retval;
548     long max_size;
549     int expired_entries = 0;
550     krb5_int32 now;
551
552     if ((retval = krb5_rc_io_open(context, &t->d, t->name))) {
553         return retval;
554     }
555
556     t->recovering = 1;
557
558     max_size = krb5_rc_io_size(context, &t->d);
559
560     rep = NULL;
561     if (krb5_rc_io_read(context, &t->d, (krb5_pointer) &t->lifespan,
562                         sizeof(t->lifespan))) {
563         retval = KRB5_RC_IO;
564         goto io_fail;
565     }
566
567     if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) {
568         retval = KRB5_RC_MALLOC;
569         goto io_fail;
570     }
571     rep->client = rep->server = rep->msghash = NULL;
572
573     if (krb5_timeofday(context, &now))
574         now = 0;
575
576     /* now read in each auth_replay and insert into table */
577     for (;;) {
578         if (krb5_rc_io_mark(context, &t->d)) {
579             retval = KRB5_RC_IO;
580             goto io_fail;
581         }
582
583         retval = krb5_rc_io_fetch(context, t, rep, (int) max_size);
584
585         if (retval == KRB5_RC_IO_EOF)
586             break;
587         else if (retval != 0)
588             goto io_fail;
589
590         if (alive(now, rep, t->lifespan) != CMP_EXPIRED) {
591             if (rc_store(context, id, rep, now, TRUE) == CMP_MALLOC) {
592                 retval = KRB5_RC_MALLOC; goto io_fail;
593             }
594         } else {
595             expired_entries++;
596         }
597
598         /*
599          *  free fields allocated by rc_io_fetch
600          */
601         free(rep->server);
602         free(rep->client);
603         if (rep->msghash)
604             free(rep->msghash);
605         rep->client = rep->server = rep->msghash = NULL;
606     }
607     retval = 0;
608     krb5_rc_io_unmark(context, &t->d);
609     /*
610      *  An automatic expunge here could remove the need for
611      *  mark/unmark but that would be inefficient.
612      */
613 io_fail:
614     krb5_rc_free_entry(context, &rep);
615     if (retval)
616         krb5_rc_io_close(context, &t->d);
617     else if (expired_entries > EXCESSREPS)
618         retval = krb5_rc_dfl_expunge_locked(context, id);
619     t->recovering = 0;
620     return retval;
621
622 #endif
623 }
624
625 krb5_error_code KRB5_CALLCONV
626 krb5_rc_dfl_recover(krb5_context context, krb5_rcache id)
627 {
628     krb5_error_code ret;
629     ret = k5_mutex_lock(&id->lock);
630     if (ret)
631         return ret;
632     ret = krb5_rc_dfl_recover_locked(context, id);
633     k5_mutex_unlock(&id->lock);
634     return ret;
635 }
636
637 krb5_error_code KRB5_CALLCONV
638 krb5_rc_dfl_recover_or_init(krb5_context context, krb5_rcache id,
639                             krb5_deltat lifespan)
640 {
641     krb5_error_code retval;
642
643     retval = k5_mutex_lock(&id->lock);
644     if (retval)
645         return retval;
646     retval = krb5_rc_dfl_recover_locked(context, id);
647     if (retval)
648         retval = krb5_rc_dfl_init_locked(context, id, lifespan);
649     k5_mutex_unlock(&id->lock);
650     return retval;
651 }
652
653 static krb5_error_code
654 krb5_rc_io_store(krb5_context context, struct dfl_data *t,
655                  krb5_donot_replay *rep)
656 {
657     size_t clientlen, serverlen;
658     ssize_t buflen;
659     unsigned int len;
660     krb5_error_code ret;
661     struct k5buf buf, extbuf;
662     char *bufptr, *extstr;
663
664     clientlen = strlen(rep->client);
665     serverlen = strlen(rep->server);
666
667     if (rep->msghash) {
668         /*
669          * Write a hash extension record, to be followed by a record
670          * in regular format (without the message hash) for the
671          * benefit of old implementations.
672          */
673
674         /* Format the extension value so we know its length. */
675         krb5int_buf_init_dynamic(&extbuf);
676         krb5int_buf_add_fmt(&extbuf, "HASH:%s %lu:%s %lu:%s", rep->msghash,
677                             (unsigned long) clientlen, rep->client,
678                             (unsigned long) serverlen, rep->server);
679         extstr = krb5int_buf_data(&extbuf);
680         if (!extstr)
681             return KRB5_RC_MALLOC;
682
683         /*
684          * Put the extension value into the server field of a
685          * regular-format record, with an empty client field.
686          */
687         krb5int_buf_init_dynamic(&buf);
688         len = 1;
689         krb5int_buf_add_len(&buf, (char *) &len, sizeof(len));
690         krb5int_buf_add_len(&buf, "", 1);
691         len = strlen(extstr) + 1;
692         krb5int_buf_add_len(&buf, (char *) &len, sizeof(len));
693         krb5int_buf_add_len(&buf, extstr, len);
694         krb5int_buf_add_len(&buf, (char *) &rep->cusec, sizeof(rep->cusec));
695         krb5int_buf_add_len(&buf, (char *) &rep->ctime, sizeof(rep->ctime));
696         free(extstr);
697     } else  /* No extension record needed. */
698         krb5int_buf_init_dynamic(&buf);
699
700     len = clientlen + 1;
701     krb5int_buf_add_len(&buf, (char *) &len, sizeof(len));
702     krb5int_buf_add_len(&buf, rep->client, len);
703     len = serverlen + 1;
704     krb5int_buf_add_len(&buf, (char *) &len, sizeof(len));
705     krb5int_buf_add_len(&buf, rep->server, len);
706     krb5int_buf_add_len(&buf, (char *) &rep->cusec, sizeof(rep->cusec));
707     krb5int_buf_add_len(&buf, (char *) &rep->ctime, sizeof(rep->ctime));
708
709     bufptr = krb5int_buf_data(&buf);
710     buflen = krb5int_buf_len(&buf);
711     if (bufptr == NULL || buflen < 0)
712         return KRB5_RC_MALLOC;
713
714     ret = krb5_rc_io_write(context, &t->d, bufptr, buflen);
715     krb5int_free_buf(&buf);
716     return ret;
717 }
718
719 static krb5_error_code krb5_rc_dfl_expunge_locked(krb5_context, krb5_rcache);
720
721 krb5_error_code KRB5_CALLCONV
722 krb5_rc_dfl_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
723 {
724     krb5_error_code ret;
725     struct dfl_data *t;
726     krb5_int32 now;
727
728     ret = krb5_timeofday(context, &now);
729     if (ret)
730         return ret;
731
732     ret = k5_mutex_lock(&id->lock);
733     if (ret)
734         return ret;
735
736     switch(rc_store(context, id, rep, now, FALSE)) {
737     case CMP_MALLOC:
738         k5_mutex_unlock(&id->lock);
739         return KRB5_RC_MALLOC;
740     case CMP_REPLAY:
741         k5_mutex_unlock(&id->lock);
742         return KRB5KRB_AP_ERR_REPEAT;
743     case 0: break;
744     default: /* wtf? */ ;
745     }
746     t = (struct dfl_data *)id->data;
747 #ifndef NOIOSTUFF
748     ret = krb5_rc_io_store(context, t, rep);
749     if (ret) {
750         k5_mutex_unlock(&id->lock);
751         return ret;
752     }
753 #endif
754     /* Shall we automatically expunge? */
755     if (t->nummisses > t->numhits + EXCESSREPS)
756     {
757         ret = krb5_rc_dfl_expunge_locked(context, id);
758         k5_mutex_unlock(&id->lock);
759         return ret;
760     }
761 #ifndef NOIOSTUFF
762     else
763     {
764         if (krb5_rc_io_sync(context, &t->d)) {
765             k5_mutex_unlock(&id->lock);
766             return KRB5_RC_IO;
767         }
768     }
769 #endif
770     k5_mutex_unlock(&id->lock);
771     return 0;
772 }
773
774 static krb5_error_code
775 krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id)
776 {
777     struct dfl_data *t = (struct dfl_data *)id->data;
778 #ifdef NOIOSTUFF
779     unsigned int i;
780     struct authlist **q;
781     struct authlist **qt;
782     struct authlist *r;
783     struct authlist *rt;
784     krb5_int32 now;
785
786     if (krb5_timestamp(context, &now))
787         now = 0;
788
789     for (q = &t->a; *q; q = qt) {
790         qt = &(*q)->na;
791         if (alive(now, &(*q)->rep, t->lifespan) == CMP_EXPIRED) {
792             free((*q)->rep.client);
793             free((*q)->rep.server);
794             if ((*q)->rep.msghash)
795                 free((*q)->rep.msghash);
796             free(*q);
797             *q = *qt; /* why doesn't this feel right? */
798         }
799     }
800     for (i = 0; i < t->hsize; i++)
801         t->h[i] = (struct authlist *) 0;
802     for (r = t->a; r; r = r->na) {
803         i = hash(&r->rep, t->hsize);
804         rt = t->h[i];
805         t->h[i] = r;
806         r->nh = rt;
807     }
808     return 0;
809 #else
810     struct authlist *q;
811     char *name;
812     krb5_error_code retval = 0;
813     krb5_rcache tmp;
814     krb5_deltat lifespan = t->lifespan;  /* save original lifespan */
815
816     if (! t->recovering) {
817         name = t->name;
818         t->name = 0;            /* Clear name so it isn't freed */
819         (void) krb5_rc_dfl_close_no_free(context, id);
820         retval = krb5_rc_dfl_resolve(context, id, name);
821         free(name);
822         if (retval)
823             return retval;
824         retval = krb5_rc_dfl_recover_locked(context, id);
825         if (retval)
826             return retval;
827         t = (struct dfl_data *)id->data; /* point to recovered cache */
828     }
829
830     retval = krb5_rc_resolve_type(context, &tmp, "dfl");
831     if (retval)
832         return retval;
833     retval = krb5_rc_resolve(context, tmp, 0);
834     if (retval)
835         goto cleanup;
836     retval = krb5_rc_initialize(context, tmp, lifespan);
837     if (retval)
838         goto cleanup;
839     for (q = t->a; q; q = q->na) {
840         if (krb5_rc_io_store(context, (struct dfl_data *)tmp->data, &q->rep)) {
841             retval = KRB5_RC_IO;
842             goto cleanup;
843         }
844     }
845     /* NOTE: We set retval in case we have an error */
846     retval = KRB5_RC_IO;
847     if (krb5_rc_io_sync(context, &((struct dfl_data *)tmp->data)->d))
848         goto cleanup;
849     if (krb5_rc_io_sync(context, &t->d))
850         goto cleanup;
851     if (krb5_rc_io_move(context, &t->d, &((struct dfl_data *)tmp->data)->d))
852         goto cleanup;
853     retval = 0;
854 cleanup:
855     (void) krb5_rc_dfl_close(context, tmp);
856     return retval;
857 #endif
858 }
859
860 krb5_error_code KRB5_CALLCONV
861 krb5_rc_dfl_expunge(krb5_context context, krb5_rcache id)
862 {
863     krb5_error_code ret;
864     ret = k5_mutex_lock(&id->lock);
865     if (ret)
866         return ret;
867     ret = krb5_rc_dfl_expunge_locked(context, id);
868     k5_mutex_unlock(&id->lock);
869     return ret;
870 }