1 /* 2 * kdc/replay.c 3 * 4 * Copyright 1991 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 * 26 * 27 * Replay lookaside cache for the KDC, to avoid extra work. 28 * 29 */ 30 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include "k5-int.h" 35 #include "kdc_util.h" 36 #include "extern.h" 37 38 #ifndef NOCACHE 39 40 typedef struct _krb5_kdc_replay_ent { 41 struct _krb5_kdc_replay_ent *next; 42 int num_hits; 43 krb5_int32 timein; 44 time_t db_age; 45 krb5_data *req_packet; 46 krb5_data *reply_packet; 47 krb5_address *addr; /* XXX should these not be pointers? */ 48 } krb5_kdc_replay_ent; 49 50 static krb5_kdc_replay_ent root_ptr = {0}; 51 52 static int hits = 0; 53 static int calls = 0; 54 static int max_hits_per_entry = 0; 55 static int num_entries = 0; 56 57 #define STALE_TIME 2*60 /* two minutes */ 58 #define STALE(ptr) ((abs((ptr)->timein - timenow) >= STALE_TIME) || \ 59 ((ptr)->db_age != db_age)) 60 61 #define MATCH(ptr) (((ptr)->req_packet->length == inpkt->length) && \ 62 !memcmp((ptr)->req_packet->data, inpkt->data, \ 63 inpkt->length) && \ 64 ((ptr)->addr->length == from->address->length) && \ 65 !memcmp((ptr)->addr->contents, \ 66 from->address->contents, \ 67 from->address->length)&& \ 68 ((ptr)->db_age == db_age)) 69 /* XXX 70 Todo: quench the size of the queue... 71 */ 72 73 /* return TRUE if outpkt is filled in with a packet to reply with, 74 FALSE if the caller should do the work */ 75 76 krb5_boolean 77 kdc_check_lookaside(krb5_data *inpkt, const krb5_fulladdr *from, 78 krb5_data **outpkt) 79 { 80 krb5_int32 timenow; 81 register krb5_kdc_replay_ent *eptr, *last, *hold; 82 time_t db_age; 83 84 if (krb5_timeofday(kdc_context, &timenow) || 85 krb5_db_get_age(kdc_context, 0, &db_age)) 86 return FALSE; 87 88 calls++; 89 90 /* search for a replay entry in the queue, possibly removing 91 stale entries while we're here */ 92 93 if (root_ptr.next) { 94 for (last = &root_ptr, eptr = root_ptr.next; 95 eptr; 96 eptr = eptr->next) { 97 if (MATCH(eptr)) { 98 eptr->num_hits++; 99 hits++; 100 101 if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt)) 102 return FALSE; 103 else 104 return TRUE; 105 /* return here, don't bother flushing even if it is stale. 106 if we just matched, we may get another retransmit... */ 107 } 108 if (STALE(eptr)) { 109 /* flush it and collect stats */ 110 max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits); 111 krb5_free_data(kdc_context, eptr->req_packet); 112 krb5_free_data(kdc_context, eptr->reply_packet); 113 krb5_free_address(kdc_context, eptr->addr); 114 hold = eptr; 115 last->next = eptr->next; 116 eptr = last; 117 free(hold); 118 } else { 119 /* this isn't it, just move along */ 120 last = eptr; 121 } 122 } 123 } 124 return FALSE; 125 } 126 127 /* insert a request & reply into the lookaside queue. assumes it's not 128 already there, and can fail softly due to other weird errors. */ 129 130 void 131 kdc_insert_lookaside(krb5_data *inpkt, const krb5_fulladdr *from, 132 krb5_data *outpkt) 133 { 134 register krb5_kdc_replay_ent *eptr; 135 krb5_int32 timenow; 136 time_t db_age; 137 138 if (krb5_timeofday(kdc_context, &timenow) || 139 krb5_db_get_age(kdc_context, 0, &db_age)) 140 return; 141 142 /* this is a new entry */ 143 eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr)); 144 if (!eptr) 145 return; 146 eptr->timein = timenow; 147 eptr->db_age = db_age; 148 /* 149 * This is going to hurt a lot malloc()-wise due to the need to 150 * allocate memory for the krb5_data and krb5_address elements. 151 * ARGH! 152 */ 153 if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) { 154 free(eptr); 155 return; 156 } 157 if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) { 158 krb5_free_data(kdc_context, eptr->req_packet); 159 free(eptr); 160 return; 161 } 162 if (krb5_copy_addr(kdc_context, from->address, &eptr->addr)) { 163 krb5_free_data(kdc_context, eptr->req_packet); 164 krb5_free_data(kdc_context, eptr->reply_packet); 165 free(eptr); 166 return; 167 } 168 eptr->next = root_ptr.next; 169 root_ptr.next = eptr; 170 num_entries++; 171 return; 172 } 173 174 /* frees memory associated with the lookaside queue for memory profiling */ 175 void 176 kdc_free_lookaside(krb5_context kcontext) 177 { 178 register krb5_kdc_replay_ent *eptr, *last, *hold; 179 if (root_ptr.next) { 180 for (last = &root_ptr, eptr = root_ptr.next; 181 eptr; eptr = eptr->next) { 182 krb5_free_data(kcontext, eptr->req_packet); 183 krb5_free_data(kcontext, eptr->reply_packet); 184 krb5_free_address(kcontext, eptr->addr); 185 hold = eptr; 186 last->next = eptr->next; 187 eptr = last; 188 free(hold); 189 } 190 } 191 } 192 193 #endif /* NOCACHE */ 194