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 */
27struct 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 */
33static int
34rc_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*/
96char *KRB5_CALLCONV
97krb5_rc_mem_get_name(krb5_context context, krb5_rcache id)
98{
99	return (((struct mem_data *)(id->data))->name);
100}
101
102/*ARGSUSED*/
103krb5_error_code KRB5_CALLCONV
104krb5_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
128krb5_error_code KRB5_CALLCONV
129krb5_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
139krb5_error_code KRB5_CALLCONV
140krb5_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 */
165krb5_error_code KRB5_CALLCONV
166krb5_rc_mem_close(krb5_context context, krb5_rcache id)
167{
168	return (0);
169}
170
171krb5_error_code KRB5_CALLCONV
172krb5_rc_mem_destroy(krb5_context context, krb5_rcache id)
173{
174	return (krb5_rc_mem_close(context, id));
175}
176
177/*ARGSUSED*/
178krb5_error_code KRB5_CALLCONV
179krb5_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
224cleanup:
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 */
242krb5_error_code KRB5_CALLCONV
243krb5_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
249krb5_error_code KRB5_CALLCONV
250krb5_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
262krb5_error_code KRB5_CALLCONV
263krb5_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