xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/replay.c (revision 55fea89d)
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 
33 #include "k5-int.h"
34 #include "kdc_util.h"
35 #include "extern.h"
36 
37 #ifndef NOCACHE
38 
39 typedef struct _krb5_kdc_replay_ent {
40     struct _krb5_kdc_replay_ent *next;
41     int num_hits;
42     krb5_int32 timein;
43     time_t db_age;
44     krb5_data *req_packet;
45     krb5_data *reply_packet;
46 } krb5_kdc_replay_ent;
47 
48 static krb5_kdc_replay_ent root_ptr = {0};
49 
50 static int hits = 0;
51 static int calls = 0;
52 static int max_hits_per_entry = 0;
53 static int num_entries = 0;
54 
55 #define STALE_TIME	2*60		/* two minutes */
56 #define STALE(ptr) ((abs((ptr)->timein - timenow) >= STALE_TIME) ||	\
57 		    ((ptr)->db_age != db_age))
58 
59 #define MATCH(ptr) (((ptr)->req_packet->length == inpkt->length) &&	\
60 		    !memcmp((ptr)->req_packet->data, inpkt->data,	\
61 			    inpkt->length) &&				\
62 		    ((ptr)->db_age == db_age))
63 /* XXX
64    Todo:  quench the size of the queue...
65  */
66 
67 /* return TRUE if outpkt is filled in with a packet to reply with,
68    FALSE if the caller should do the work */
69 
70 krb5_boolean
kdc_check_lookaside(krb5_data * inpkt,krb5_data ** outpkt)71 kdc_check_lookaside(krb5_data *inpkt, krb5_data **outpkt)
72 {
73     krb5_int32 timenow;
74     register krb5_kdc_replay_ent *eptr, *last, *hold;
75     time_t db_age;
76 
77     if (krb5_timeofday(kdc_context, &timenow) ||
78 	krb5_db_get_age(kdc_context, 0, &db_age))
79 	return FALSE;
80 
81     calls++;
82 
83     /* search for a replay entry in the queue, possibly removing
84        stale entries while we're here */
85 
86     if (root_ptr.next) {
87 	for (last = &root_ptr, eptr = root_ptr.next;
88 	     eptr;
89 	     eptr = eptr->next) {
90 	    if (MATCH(eptr)) {
91 		eptr->num_hits++;
92 		hits++;
93 
94 		if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt))
95 		    return FALSE;
96 		else
97 		    return TRUE;
98 		/* return here, don't bother flushing even if it is stale.
99 		   if we just matched, we may get another retransmit... */
100 	    }
101 	    if (STALE(eptr)) {
102 		/* flush it and collect stats */
103 		max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits);
104 		krb5_free_data(kdc_context, eptr->req_packet);
105 		krb5_free_data(kdc_context, eptr->reply_packet);
106 		hold = eptr;
107 		last->next = eptr->next;
108 		eptr = last;
109 		free(hold);
110 	    } else {
111 		/* this isn't it, just move along */
112 		last = eptr;
113 	    }
114 	}
115     }
116     return FALSE;
117 }
118 
119 /* insert a request & reply into the lookaside queue.  assumes it's not
120    already there, and can fail softly due to other weird errors. */
121 
122 void
kdc_insert_lookaside(krb5_data * inpkt,krb5_data * outpkt)123 kdc_insert_lookaside(krb5_data *inpkt, krb5_data *outpkt)
124 {
125     register krb5_kdc_replay_ent *eptr;
126     krb5_int32 timenow;
127     time_t db_age;
128 
129     if (krb5_timeofday(kdc_context, &timenow) ||
130 	krb5_db_get_age(kdc_context, 0, &db_age))
131 	return;
132 
133     /* this is a new entry */
134     eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr));
135     if (!eptr)
136 	return;
137     eptr->timein = timenow;
138     eptr->db_age = db_age;
139     /*
140      * This is going to hurt a lot malloc()-wise due to the need to
141      * allocate memory for the krb5_data and krb5_address elements.
142      * ARGH!
143      */
144     if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) {
145 	free(eptr);
146 	return;
147     }
148     if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) {
149 	krb5_free_data(kdc_context, eptr->req_packet);
150 	free(eptr);
151 	return;
152     }
153     eptr->next = root_ptr.next;
154     root_ptr.next = eptr;
155     num_entries++;
156     return;
157 }
158 
159 /* frees memory associated with the lookaside queue for memory profiling */
160 void
kdc_free_lookaside(krb5_context kcontext)161 kdc_free_lookaside(krb5_context kcontext)
162 {
163     register krb5_kdc_replay_ent *eptr, *last, *hold;
164     if (root_ptr.next) {
165         for (last = &root_ptr, eptr = root_ptr.next;
166 	     eptr; eptr = eptr->next) {
167 		krb5_free_data(kcontext, eptr->req_packet);
168 		krb5_free_data(kcontext, eptr->reply_packet);
169 		hold = eptr;
170 		last->next = eptr->next;
171 		eptr = last;
172 		free(hold);
173 	}
174     }
175 }
176 
177 #endif /* NOCACHE */
178