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