/* * kdc/replay.c * * Copyright 1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * Replay lookaside cache for the KDC, to avoid extra work. * */ #include "k5-int.h" #include "kdc_util.h" #include "extern.h" #ifndef NOCACHE typedef struct _krb5_kdc_replay_ent { struct _krb5_kdc_replay_ent *next; int num_hits; krb5_int32 timein; time_t db_age; krb5_data *req_packet; krb5_data *reply_packet; } krb5_kdc_replay_ent; static krb5_kdc_replay_ent root_ptr = {0}; static int hits = 0; static int calls = 0; static int max_hits_per_entry = 0; static int num_entries = 0; #define STALE_TIME 2*60 /* two minutes */ #define STALE(ptr) ((abs((ptr)->timein - timenow) >= STALE_TIME) || \ ((ptr)->db_age != db_age)) #define MATCH(ptr) (((ptr)->req_packet->length == inpkt->length) && \ !memcmp((ptr)->req_packet->data, inpkt->data, \ inpkt->length) && \ ((ptr)->db_age == db_age)) /* XXX Todo: quench the size of the queue... */ /* return TRUE if outpkt is filled in with a packet to reply with, FALSE if the caller should do the work */ krb5_boolean kdc_check_lookaside(krb5_data *inpkt, krb5_data **outpkt) { krb5_int32 timenow; register krb5_kdc_replay_ent *eptr, *last, *hold; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return FALSE; calls++; /* search for a replay entry in the queue, possibly removing stale entries while we're here */ if (root_ptr.next) { for (last = &root_ptr, eptr = root_ptr.next; eptr; eptr = eptr->next) { if (MATCH(eptr)) { eptr->num_hits++; hits++; if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt)) return FALSE; else return TRUE; /* return here, don't bother flushing even if it is stale. if we just matched, we may get another retransmit... */ } if (STALE(eptr)) { /* flush it and collect stats */ max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits); krb5_free_data(kdc_context, eptr->req_packet); krb5_free_data(kdc_context, eptr->reply_packet); hold = eptr; last->next = eptr->next; eptr = last; free(hold); } else { /* this isn't it, just move along */ last = eptr; } } } return FALSE; } /* insert a request & reply into the lookaside queue. assumes it's not already there, and can fail softly due to other weird errors. */ void kdc_insert_lookaside(krb5_data *inpkt, krb5_data *outpkt) { register krb5_kdc_replay_ent *eptr; krb5_int32 timenow; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return; /* this is a new entry */ eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr)); if (!eptr) return; eptr->timein = timenow; eptr->db_age = db_age; /* * This is going to hurt a lot malloc()-wise due to the need to * allocate memory for the krb5_data and krb5_address elements. * ARGH! */ if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) { free(eptr); return; } if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) { krb5_free_data(kdc_context, eptr->req_packet); free(eptr); return; } eptr->next = root_ptr.next; root_ptr.next = eptr; num_entries++; return; } /* frees memory associated with the lookaside queue for memory profiling */ void kdc_free_lookaside(krb5_context kcontext) { register krb5_kdc_replay_ent *eptr, *last, *hold; if (root_ptr.next) { for (last = &root_ptr, eptr = root_ptr.next; eptr; eptr = eptr->next) { krb5_free_data(kcontext, eptr->req_packet); krb5_free_data(kcontext, eptr->reply_packet); hold = eptr; last->next = eptr->next; eptr = last; free(hold); } } } #endif /* NOCACHE */