2 * lib/krb5/rcache/rc_dfl.c
4 * This file of the Kerberos V5 software is derived from public-domain code
5 * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
11 * An implementation for the default replay cache type.
13 #define FREE(x) ((void) free((char *) (x)))
20 If NOIOSTUFF is defined at compile time, dfl rcaches will be per-process.
26 static int hash(krb5_donot_replay *rep,int hsize)
27 returns hash value of *rep, between 0 and hsize - 1
29 size of hash table (constant), can be preset
30 static int cmp(krb5_donot_replay *old,krb5_donot_replay *new,krb5_deltat t)
31 compare old and new; return CMP_REPLAY or CMP_HOHUM
32 static int alive (krb5_context, krb5_donot_replay *new,krb5_deltat t)
33 see if new is still alive; return CMP_EXPIRED or CMP_HOHUM
34 CMP_MALLOC, CMP_EXPIRED, CMP_REPLAY, CMP_HOHUM
35 return codes from cmp(), alive(), and store()
37 data stored in this cache type, namely "dfl"
39 multilinked list of reps
40 static int store(context, krb5_rcache id,krb5_donot_replay *rep)
41 store rep in cache id; return CMP_REPLAY if replay, else CMP_MALLOC/CMP_HOHUM
46 #define HASHSIZE 997 /* a convenient prime */
52 /* The rcache will be automatically expunged when the number of expired
53 krb5_donot_replays encountered incidentally in searching exceeds the number
54 of live krb5_donot_replays by EXCESSREPS. With the defaults here, a typical
55 cache might build up some 10K of expired krb5_donot_replays before an automatic
56 expunge, with the waste basically independent of the number of stores per
59 static int hash(rep, hsize)
60 krb5_donot_replay *rep;
63 return (int) ((((rep->cusec + rep->ctime + *rep->server + *rep->client)
64 % hsize) + hsize) % hsize);
65 /* We take this opportunity to once again complain about C's idiotic %. */
69 #define CMP_EXPIRED -2
74 static int cmp(old, new, t)
75 krb5_donot_replay *old;
76 krb5_donot_replay *new;
79 if ((old->cusec == new->cusec) && /* most likely to distinguish */
80 (old->ctime == new->ctime) &&
81 (strcmp(old->client,new->client) == 0) &&
82 (strcmp(old->server,new->server) == 0)) /* always true */
87 static int alive(context, new, t)
89 krb5_donot_replay *new;
94 if (krb5_timeofday(context, &time))
95 return CMP_HOHUM; /* who cares? */
96 if (new->ctime + t < time) /* I hope we don't have to worry about overflow */
104 krb5_deltat lifespan;
118 krb5_donot_replay rep;
124 /* of course, list is backwards from file */
125 /* hash could be forwards since we have to search on match, but naaaah */
127 static int store(context, id, rep)
128 krb5_context context;
130 krb5_donot_replay *rep;
132 struct dfl_data *t = (struct dfl_data *)id->data;
136 rephash = hash(rep,t->hsize);
138 for (ta = t->h[rephash];ta;ta = ta->nh)
139 switch(cmp(&ta->rep,rep,t->lifespan))
141 case CMP_REPLAY: return CMP_REPLAY;
142 case CMP_HOHUM: if (alive(context, &ta->rep,t->lifespan) == CMP_EXPIRED)
147 default: ; /* wtf? */
150 if (!(ta = (struct authlist *) malloc(sizeof(struct authlist))))
152 ta->na = t->a; t->a = ta;
153 ta->nh = t->h[rephash]; t->h[rephash] = ta;
155 if (!(ta->rep.client = strdup(rep->client))) {
159 if (!(ta->rep.server = strdup(rep->server))) {
160 FREE(ta->rep.client);
168 char * INTERFACE krb5_rc_dfl_get_name(context, id)
169 krb5_context context;
172 return ((struct dfl_data *) (id->data))->name;
175 krb5_error_code INTERFACE krb5_rc_dfl_get_span(context, id, lifespan)
176 krb5_context context;
178 krb5_deltat *lifespan;
180 *lifespan = ((struct dfl_data *) (id->data))->lifespan;
184 krb5_error_code INTERFACE krb5_rc_dfl_init(context, id, lifespan)
185 krb5_context context;
187 krb5_deltat lifespan;
189 struct dfl_data *t = (struct dfl_data *)id->data;
190 krb5_error_code retval;
192 t->lifespan = lifespan;
194 if (retval = krb5_rc_io_creat(context, &t->d,&t->name))
196 if (krb5_rc_io_write(context, &t->d,(krb5_pointer) &t->lifespan,sizeof(t->lifespan))
197 || krb5_rc_io_sync(context, &t->d))
203 krb5_error_code INTERFACE krb5_rc_dfl_close_no_free(context, id)
204 krb5_context context;
207 struct dfl_data *t = (struct dfl_data *)id->data;
222 (void) krb5_rc_io_close(context, &t->d);
228 krb5_error_code INTERFACE krb5_rc_dfl_close(context, id)
229 krb5_context context;
232 krb5_rc_dfl_close_no_free(context, id);
237 krb5_error_code INTERFACE krb5_rc_dfl_destroy(context, id)
238 krb5_context context;
242 if (krb5_rc_io_destroy(context, &((struct dfl_data *) (id->data))->d))
245 return krb5_rc_dfl_close(context, id);
248 krb5_error_code INTERFACE krb5_rc_dfl_resolve(context, id, name)
249 krb5_context context;
253 struct dfl_data *t = 0;
254 krb5_error_code retval;
256 /* allocate id? no */
257 if (!(t = (struct dfl_data *) malloc(sizeof(struct dfl_data))))
258 return KRB5_RC_MALLOC;
259 id->data = (krb5_pointer) t;
260 memset(t, 0, sizeof(struct dfl_data));
262 t->name = malloc(strlen(name)+1);
264 retval = KRB5_RC_MALLOC;
267 strcpy(t->name, name);
270 t->numhits = t->nummisses = 0;
271 t->hsize = HASHSIZE; /* no need to store---it's memory-only */
272 t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *));
274 retval = KRB5_RC_MALLOC;
277 memset(t->h, 0, t->hsize*sizeof(struct authlist *));
278 t->a = (struct authlist *) 0;
295 void INTERFACE krb5_rc_free_entry (context, rep)
296 krb5_context context;
297 krb5_donot_replay **rep;
299 krb5_donot_replay *rp = *rep;
315 static krb5_error_code krb5_rc_io_fetch(context, t, rep, maxlen)
316 krb5_context context;
318 krb5_donot_replay *rep;
322 krb5_error_code retval;
324 rep->client = rep->server = 0;
326 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &len, sizeof(len));
330 if ((len <= 0) || (len >= maxlen))
331 return KRB5_RC_IO_EOF;
333 rep->client = malloc (len);
335 return KRB5_RC_MALLOC;
337 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) rep->client, len);
341 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &len, sizeof(len));
345 if ((len <= 0) || (len >= maxlen)) {
346 retval = KRB5_RC_IO_EOF;
350 rep->server = malloc (len);
352 retval = KRB5_RC_MALLOC;
356 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) rep->server, len);
360 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &rep->cusec, sizeof(rep->cusec));
364 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &rep->ctime, sizeof(rep->ctime));
372 krb5_xfree(rep->client);
374 krb5_xfree(rep->server);
380 krb5_error_code INTERFACE krb5_rc_dfl_recover(context, id)
381 krb5_context context;
388 struct dfl_data *t = (struct dfl_data *)id->data;
389 krb5_donot_replay *rep;
390 krb5_error_code retval;
393 if (retval = krb5_rc_io_open(context, &t->d,t->name))
396 max_size = krb5_rc_io_size(context, &t->d);
399 if (krb5_rc_io_read(context, &t->d,(krb5_pointer) &t->lifespan,sizeof(t->lifespan))) {
404 /* now read in each auth_replay and insert into table */
407 if (krb5_rc_io_mark(context, &t->d)) {
412 if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) {
413 retval = KRB5_RC_MALLOC;
419 retval = krb5_rc_io_fetch (context, t, rep, (int) max_size);
421 if (retval == KRB5_RC_IO_EOF)
423 else if (retval != 0)
427 if (alive(context, rep,t->lifespan) == CMP_EXPIRED) {
428 krb5_rc_free_entry(context, &rep);
432 if (store(context, id,rep) == CMP_MALLOC) {/* can't be a replay */
433 retval = KRB5_RC_MALLOC; goto io_fail;
436 * store() copies the server & client fields to make sure
437 * they don't get stomped on by other callers, so we need to
445 krb5_rc_io_unmark(context, &t->d);
447 * An automatic expunge here could remove the need for
448 * mark/unmark but that would be inefficient.
451 krb5_rc_free_entry(context, &rep);
453 krb5_rc_io_close(context, &t->d);
459 static krb5_error_code
460 krb5_rc_io_store (context, t, rep)
461 krb5_context context;
463 krb5_donot_replay *rep;
465 int clientlen, serverlen, len;
469 clientlen = strlen (rep->client) + 1;
470 serverlen = strlen (rep->server) + 1;
471 len = sizeof(clientlen) + clientlen + sizeof(serverlen) + serverlen +
472 sizeof(rep->cusec) + sizeof(rep->ctime);
475 return KRB5_RC_MALLOC;
477 memcpy(ptr, &clientlen, sizeof(clientlen)); ptr += sizeof(clientlen);
478 memcpy(ptr, rep->client, clientlen); ptr += clientlen;
479 memcpy(ptr, &serverlen, sizeof(serverlen)); ptr += sizeof(serverlen);
480 memcpy(ptr, rep->server, serverlen); ptr += serverlen;
481 memcpy(ptr, &rep->cusec, sizeof(rep->cusec)); ptr += sizeof(rep->cusec);
482 memcpy(ptr, &rep->ctime, sizeof(rep->ctime)); ptr += sizeof(rep->ctime);
484 ret = krb5_rc_io_write(context, &t->d, buf, len);
489 krb5_error_code INTERFACE krb5_rc_dfl_store(context, id, rep)
490 krb5_context context;
492 krb5_donot_replay *rep;
495 struct dfl_data *t = (struct dfl_data *)id->data;
497 switch(store(context, id,rep)) {
499 return KRB5_RC_MALLOC;
501 return KRB5KRB_AP_ERR_REPEAT;
503 default: /* wtf? */ ;
506 ret = krb5_rc_io_store (context, t, rep);
510 /* Shall we automatically expunge? */
511 if (t->nummisses > t->numhits + EXCESSREPS)
513 return krb5_rc_dfl_expunge(context, id);
518 if (krb5_rc_io_sync(context, &t->d))
525 krb5_error_code INTERFACE krb5_rc_dfl_expunge(context, id)
526 krb5_context context;
529 struct dfl_data *t = (struct dfl_data *)id->data;
533 struct authlist **qt;
537 for (q = &t->a;*q;q = qt) {
539 if (alive(context, &(*q)->rep,t->lifespan) == CMP_EXPIRED) {
540 FREE((*q)->rep.client);
541 FREE((*q)->rep.server);
543 *q = *qt; /* why doesn't this feel right? */
546 for (i = 0;i < t->hsize;i++)
547 t->h[i] = (struct authlist *) 0;
548 for (r = t->a;r;r = r->na) {
549 i = hash(&r->rep,t->hsize);
558 krb5_error_code retval;
560 krb5_deltat lifespan = t->lifespan; /* save original lifespan */
563 t->name = 0; /* Clear name so it isn't freed */
564 (void) krb5_rc_dfl_close_no_free(context, id);
565 retval = krb5_rc_dfl_resolve(context, id, name);
569 retval = krb5_rc_dfl_recover(context, id);
572 t = (struct dfl_data *)id->data; /* point to recovered cache */
573 tmp = (krb5_rcache) malloc(sizeof(*tmp));
576 retval = krb5_rc_resolve_type(context, &tmp, "dfl");
579 retval = krb5_rc_resolve(context, tmp, 0);
582 retval = krb5_rc_initialize(context, tmp, lifespan);
585 for (q = t->a;q;q = q->na) {
586 if (krb5_rc_io_store (context, (struct dfl_data *)tmp->data, &q->rep))
589 if (krb5_rc_io_sync(context, &t->d))
591 if (krb5_rc_io_move(context, &t->d, &((struct dfl_data *)tmp->data)->d))
593 (void) krb5_rc_dfl_close(context, tmp);