1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * mech_krb5/krb5/rcache/rc_mem.c
10  *
11  * This file of the Kerberos V5 software is derived from public-domain code
12  * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
13  */
14 
15 /*
16  * An implementation for the memory only (mem) replay cache type.
17  */
18 #include "rc_common.h"
19 #include "rc_mem.h"
20 
21 /*
22  * of course, list is backwards
23  * hash could be forwards since we have to search on match, but naaaah
24  */
25 static int
26 rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
27 {
28 	struct mem_data *t = (struct mem_data *)id->data;
29 	int rephash;
30 	struct authlist *ta, *pta = NULL, *head;
31 	krb5_int32 time;
32 
33 	rephash = hash(rep, t->hsize);
34 
35 	/* Solaris: calling krb_timeofday() here, once for better perf. */
36 	krb5_timeofday(context, &time);
37 
38 	/*
39 	 * Solaris: calling alive() on rep since it doesn't make sense to store
40 	 * an expired replay.
41 	 */
42 	if (alive(context, rep, t->lifespan, time) == CMP_EXPIRED)
43 		return (CMP_EXPIRED);
44 
45 	for (ta = t->h[rephash]; ta; ta = ta->nh) {
46 		switch (cmp(&ta->rep, rep)) {
47 			case CMP_REPLAY:
48 				return (CMP_REPLAY);
49 			case CMP_HOHUM:
50 				if (alive(context, &ta->rep, t->lifespan, time)
51 				    == CMP_EXPIRED) {
52 					free(ta->rep.client);
53 					free(ta->rep.server);
54 					if (pta) {
55 						pta->nh = ta->nh;
56 						free(ta);
57 						ta = pta;
58 					} else {
59 						head = t->h[rephash];
60 						t->h[rephash] = ta->nh;
61 						free(head);
62 					}
63 					continue;
64 				}
65 		}
66 		pta = ta;
67 	}
68 
69 	if (!(ta = (struct authlist *)malloc(sizeof (struct authlist))))
70 		return (CMP_MALLOC);
71 	ta->rep = *rep;
72 	if (!(ta->rep.client = strdup(rep->client))) {
73 		free(ta);
74 		return (CMP_MALLOC);
75 	}
76 	if (!(ta->rep.server = strdup(rep->server))) {
77 		free(ta->rep.client);
78 		free(ta);
79 		return (CMP_MALLOC);
80 	}
81 	ta->nh = t->h[rephash];
82 	t->h[rephash] = ta;
83 
84 	return (CMP_HOHUM);
85 }
86 
87 /*ARGSUSED*/
88 char *KRB5_CALLCONV
89 krb5_rc_mem_get_name(krb5_context context, krb5_rcache id)
90 {
91 	return (((struct mem_data *)(id->data))->name);
92 }
93 
94 /*ARGSUSED*/
95 krb5_error_code KRB5_CALLCONV
96 krb5_rc_mem_get_span(
97 	krb5_context context,
98 	krb5_rcache id,
99 	krb5_deltat *lifespan)
100 {
101     krb5_error_code err;
102     struct mem_data *t;
103 
104     err = k5_mutex_lock(&id->lock);
105     if (err)
106 	return err;
107     t = (struct mem_data *) id->data;
108     *lifespan = t->lifespan;
109     k5_mutex_unlock(&id->lock);
110     return 0;
111 }
112 
113 krb5_error_code KRB5_CALLCONV
114 krb5_rc_mem_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
115 {
116 	struct mem_data *t = (struct mem_data *)id->data;
117 	krb5_error_code retval;
118 
119 	t->lifespan = lifespan ? lifespan : context->clockskew;
120 	/* default to clockskew from the context */
121 	return (0);
122 }
123 
124 krb5_error_code KRB5_CALLCONV
125 krb5_rc_mem_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
126 {
127     krb5_error_code retval;
128 
129     retval = k5_mutex_lock(&id->lock);
130     if (retval)
131 	return retval;
132     retval = krb5_rc_mem_init_locked(context, id, lifespan);
133     k5_mutex_unlock(&id->lock);
134     return retval;
135 }
136 
137 
138 krb5_error_code KRB5_CALLCONV
139 krb5_rc_mem_close_no_free(krb5_context context, krb5_rcache id)
140 {
141 	struct mem_data *t = (struct mem_data *)id->data;
142 	struct authlist *q, *qt;
143 	int i;
144 
145 	if (t->name)
146 		free(t->name);
147 	for (i = 0; i < t->hsize; i++)
148 		for (q = t->h[i]; q; q = qt) {
149 			qt = q->nh;
150 			free(q->rep.server);
151 			free(q->rep.client);
152 			free(q);
153 		}
154 	if (t->h)
155 		free(t->h);
156 	free(t);
157 	id->data = NULL;
158 	return (0);
159 }
160 
161 krb5_error_code KRB5_CALLCONV
162 krb5_rc_mem_close(krb5_context context, krb5_rcache id)
163 {
164     krb5_error_code retval;
165     retval = k5_mutex_lock(&id->lock);
166     if (retval)
167 	return retval;
168     krb5_rc_mem_close_no_free(context, id);
169     k5_mutex_unlock(&id->lock);
170     k5_mutex_destroy(&id->lock);
171     free(id);
172     return 0;
173 }
174 
175 krb5_error_code KRB5_CALLCONV
176 krb5_rc_mem_destroy(krb5_context context, krb5_rcache id)
177 {
178 	return (krb5_rc_mem_close(context, id));
179 }
180 
181 /*ARGSUSED*/
182 krb5_error_code KRB5_CALLCONV
183 krb5_rc_mem_resolve(krb5_context context, krb5_rcache id, char *name)
184 {
185 	struct mem_data *t = 0;
186 	krb5_error_code retval;
187 
188 	/* allocate id? no */
189 	if (!(t = (struct mem_data *)malloc(sizeof (struct mem_data))))
190 		return (KRB5_RC_MALLOC);
191 	id->data = (krb5_pointer)t;
192 	memset(t, 0, sizeof (struct mem_data));
193 	if (name) {
194 		t->name = malloc(strlen(name)+1);
195 		if (!t->name) {
196 			retval = KRB5_RC_MALLOC;
197 			goto cleanup;
198 		}
199 		strcpy(t->name, name);
200 	} else
201 		t->name = 0;
202 	t->hsize = HASHSIZE; /* no need to store---it's memory-only */
203 	t->h = (struct authlist **)malloc(t->hsize*sizeof (struct authlist *));
204 	if (!t->h) {
205 		retval = KRB5_RC_MALLOC;
206 		goto cleanup;
207 	}
208 	memset(t->h, 0, t->hsize*sizeof (struct authlist *));
209 	return (0);
210 
211 cleanup:
212 	if (t) {
213 		if (t->name)
214 			krb5_xfree(t->name);
215 		if (t->h)
216 			krb5_xfree(t->h);
217 		krb5_xfree(t);
218 		id->data = NULL;
219 	}
220 	return (retval);
221 }
222 
223 krb5_error_code KRB5_CALLCONV
224 krb5_rc_mem_recover(krb5_context context, krb5_rcache id)
225 {
226 	/* SUNW14resync - No need for locking here, just returning RC_NOIO */
227 	return (KRB5_RC_NOIO);
228 }
229 
230 krb5_error_code KRB5_CALLCONV
231 krb5_rc_mem_recover_or_init(krb5_context context, krb5_rcache id,
232 			    krb5_deltat lifespan)
233 {
234     krb5_error_code retval;
235 
236     retval = k5_mutex_lock(&id->lock);
237     if (retval)
238 	return retval;
239     retval = krb5_rc_mem_recover(context, id);
240     if (retval)
241 	retval = krb5_rc_mem_init_locked(context, id, lifespan);
242     k5_mutex_unlock(&id->lock);
243     return retval;
244 }
245 
246 krb5_error_code KRB5_CALLCONV
247 krb5_rc_mem_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
248 {
249 	krb5_error_code ret;
250 
251 	ret = k5_mutex_lock(&id->lock);
252 	if (ret)
253 		return (ret);
254 
255 	switch (rc_store(context, id, rep)) {
256 		case CMP_MALLOC:
257 			k5_mutex_unlock(&id->lock);
258 			return (KRB5_RC_MALLOC);
259 		case CMP_REPLAY:
260 			k5_mutex_unlock(&id->lock);
261 			return (KRB5KRB_AP_ERR_REPEAT);
262 		case CMP_EXPIRED:
263 			k5_mutex_unlock(&id->lock);
264 			return (KRB5KRB_AP_ERR_SKEW);
265 		case CMP_HOHUM:
266 			break;
267 	}
268 
269 	k5_mutex_unlock(&id->lock);
270 	return (0);
271 }
272