1 /*
2  * Copyright 2007 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  * Solaris Kerberos:
17  * An implementation for the memory only (mem) replay cache type.
18  */
19 #include "rc_common.h"
20 #include "rc_mem.h"
21 
22 /*
23  * We want the replay cache to hang around for the entire life span of the
24  * process, regardless if the auth_context or acceptor_cred handles are
25  * destroyed.
26  */
27 struct global_rcache grcache = {K5_MUTEX_PARTIAL_INITIALIZER, NULL};
28 
29 /*
30  * of course, list is backwards
31  * hash could be forwards since we have to search on match, but naaaah
32  */
33 static int
34 rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
35 {
36 	struct mem_data *t = (struct mem_data *)id->data;
37 	int rephash;
38 	struct authlist *ta, *pta = NULL, *head;
39 	krb5_int32 time;
40 
41 	rephash = hash(rep, t->hsize);
42 
43 	/* Solaris: calling krb_timeofday() here, once for better perf. */
44 	krb5_timeofday(context, &time);
45 
46 	/*
47 	 * Solaris: calling alive() on rep since it doesn't make sense to store
48 	 * an expired replay.
49 	 */
50 	if (alive(context, rep, t->lifespan, time) == CMP_EXPIRED)
51 		return (CMP_EXPIRED);
52 
53 	for (ta = t->h[rephash]; ta; ta = ta->nh) {
54 		switch (cmp(&ta->rep, rep)) {
55 			case CMP_REPLAY:
56 				return (CMP_REPLAY);
57 			case CMP_HOHUM:
58 				if (alive(context, &ta->rep, t->lifespan, time)
59 				    == CMP_EXPIRED) {
60 					free(ta->rep.client);
61 					free(ta->rep.server);
62 					if (pta) {
63 						pta->nh = ta->nh;
64 						free(ta);
65 						ta = pta;
66 					} else {
67 						head = t->h[rephash];
68 						t->h[rephash] = ta->nh;
69 						free(head);
70 					}
71 					continue;
72 				}
73 		}
74 		pta = ta;
75 	}
76 
77 	if (!(ta = (struct authlist *)malloc(sizeof (struct authlist))))
78 		return (CMP_MALLOC);
79 	ta->rep = *rep;
80 	if (!(ta->rep.client = strdup(rep->client))) {
81 		free(ta);
82 		return (CMP_MALLOC);
83 	}
84 	if (!(ta->rep.server = strdup(rep->server))) {
85 		free(ta->rep.client);
86 		free(ta);
87 		return (CMP_MALLOC);
88 	}
89 	ta->nh = t->h[rephash];
90 	t->h[rephash] = ta;
91 
92 	return (CMP_HOHUM);
93 }
94 
95 /*ARGSUSED*/
96 char *KRB5_CALLCONV
97 krb5_rc_mem_get_name(krb5_context context, krb5_rcache id)
98 {
99 	return (((struct mem_data *)(id->data))->name);
100 }
101 
102 /*ARGSUSED*/
103 krb5_error_code KRB5_CALLCONV
104 krb5_rc_mem_get_span(
105 	krb5_context context,
106 	krb5_rcache id,
107 	krb5_deltat *lifespan)
108 {
109     krb5_error_code err;
110     struct mem_data *t;
111 
112     err = k5_mutex_lock(&id->lock);
113     if (err)
114 	return err;
115 
116     if (err = k5_mutex_lock(&grcache.lock)) {
117 	k5_mutex_unlock(&id->lock);
118 	return (err);
119     }
120     t = (struct mem_data *) id->data;
121     *lifespan = t->lifespan;
122     k5_mutex_unlock(&grcache.lock);
123 
124     k5_mutex_unlock(&id->lock);
125     return 0;
126 }
127 
128 krb5_error_code KRB5_CALLCONV
129 krb5_rc_mem_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
130 {
131 	struct mem_data *t = (struct mem_data *)id->data;
132 	krb5_error_code retval;
133 
134 	t->lifespan = lifespan ? lifespan : context->clockskew;
135 	/* default to clockskew from the context */
136 	return (0);
137 }
138 
139 krb5_error_code KRB5_CALLCONV
140 krb5_rc_mem_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
141 {
142     krb5_error_code retval;
143 
144     retval = k5_mutex_lock(&id->lock);
145     if (retval)
146 	return retval;
147     retval = k5_mutex_lock(&grcache.lock);
148     if (retval) {
149 	k5_mutex_unlock(&id->lock);
150 	return (retval);
151     }
152 
153     retval = krb5_rc_mem_init_locked(context, id, lifespan);
154 
155     k5_mutex_unlock(&grcache.lock);
156     k5_mutex_unlock(&id->lock);
157     return retval;
158 }
159 
160 /*
161  * We want the replay cache to be persistent since we can't
162  * read from a file to retrieve the rcache, so we must not free
163  * here.  Just return success.
164  */
165 krb5_error_code KRB5_CALLCONV
166 krb5_rc_mem_close(krb5_context context, krb5_rcache id)
167 {
168 	return (0);
169 }
170 
171 krb5_error_code KRB5_CALLCONV
172 krb5_rc_mem_destroy(krb5_context context, krb5_rcache id)
173 {
174 	return (krb5_rc_mem_close(context, id));
175 }
176 
177 /*ARGSUSED*/
178 krb5_error_code KRB5_CALLCONV
179 krb5_rc_mem_resolve(krb5_context context, krb5_rcache id, char *name)
180 {
181 	struct mem_data *t = 0;
182 	krb5_error_code retval;
183 
184 	retval = k5_mutex_lock(&grcache.lock);
185 	if (retval)
186 		return (retval);
187 
188 	/*
189 	 * If the global rcache has already been initialized through a prior
190 	 * call to this function then just set the rcache to point to it for
191 	 * any subsequent operations.
192 	 */
193 	if (grcache.data != NULL) {
194 		id->data = (krb5_pointer)grcache.data;
195 		k5_mutex_unlock(&grcache.lock);
196 		return (0);
197 	}
198 	/* allocate id? no */
199 	if (!(t = (struct mem_data *)malloc(sizeof (struct mem_data)))) {
200 		k5_mutex_unlock(&grcache.lock);
201 		return (KRB5_RC_MALLOC);
202 	}
203 	grcache.data = id->data = (krb5_pointer)t;
204 	memset(t, 0, sizeof (struct mem_data));
205 	if (name) {
206 		t->name = malloc(strlen(name)+1);
207 		if (!t->name) {
208 			retval = KRB5_RC_MALLOC;
209 			goto cleanup;
210 		}
211 		strcpy(t->name, name);
212 	} else
213 		t->name = 0;
214 	t->hsize = HASHSIZE; /* no need to store---it's memory-only */
215 	t->h = (struct authlist **)malloc(t->hsize*sizeof (struct authlist *));
216 	if (!t->h) {
217 		retval = KRB5_RC_MALLOC;
218 		goto cleanup;
219 	}
220 	memset(t->h, 0, t->hsize*sizeof (struct authlist *));
221 	k5_mutex_unlock(&grcache.lock);
222 	return (0);
223 
224 cleanup:
225 	if (t) {
226 		if (t->name)
227 			krb5_xfree(t->name);
228 		if (t->h)
229 			krb5_xfree(t->h);
230 		krb5_xfree(t);
231 		grcache.data = NULL;
232 		id->data = NULL;
233 	}
234 	k5_mutex_unlock(&grcache.lock);
235 	return (retval);
236 }
237 
238 /*
239  * Recovery (retrieval) of the replay cache occurred during
240  * krb5_rc_resolve().  So we just return error here.
241  */
242 krb5_error_code KRB5_CALLCONV
243 krb5_rc_mem_recover(krb5_context context, krb5_rcache id)
244 {
245 	/* SUNW14resync - No need for locking here, just returning RC_NOIO */
246 	return (KRB5_RC_NOIO);
247 }
248 
249 krb5_error_code KRB5_CALLCONV
250 krb5_rc_mem_recover_or_init(krb5_context context, krb5_rcache id,
251 			    krb5_deltat lifespan)
252 {
253     krb5_error_code retval;
254 
255     retval = krb5_rc_mem_recover(context, id);
256     if (retval)
257 	retval = krb5_rc_mem_init(context, id, lifespan);
258 
259     return retval;
260 }
261 
262 krb5_error_code KRB5_CALLCONV
263 krb5_rc_mem_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
264 {
265 	krb5_error_code ret;
266 
267 	ret = k5_mutex_lock(&id->lock);
268 	if (ret)
269 		return (ret);
270 	ret = k5_mutex_lock(&grcache.lock);
271 	if (ret) {
272 		k5_mutex_unlock(&id->lock);
273 		return (ret);
274 	}
275 
276 	switch (rc_store(context, id, rep)) {
277 		case CMP_MALLOC:
278 			k5_mutex_unlock(&grcache.lock);
279 			k5_mutex_unlock(&id->lock);
280 			return (KRB5_RC_MALLOC);
281 		case CMP_REPLAY:
282 			k5_mutex_unlock(&grcache.lock);
283 			k5_mutex_unlock(&id->lock);
284 			return (KRB5KRB_AP_ERR_REPEAT);
285 		case CMP_EXPIRED:
286 			k5_mutex_unlock(&grcache.lock);
287 			k5_mutex_unlock(&id->lock);
288 			return (KRB5KRB_AP_ERR_SKEW);
289 		case CMP_HOHUM:
290 			break;
291 	}
292 
293 	k5_mutex_unlock(&grcache.lock);
294 	k5_mutex_unlock(&id->lock);
295 	return (0);
296 }
297