17c478bd9Sstevel@tonic-gate /*
23125ebfcSsemery  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * mech_krb5/krb5/rcache/rc_mem.c
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * This file of the Kerberos V5 software is derived from public-domain code
107c478bd9Sstevel@tonic-gate  * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
117c478bd9Sstevel@tonic-gate  */
127c478bd9Sstevel@tonic-gate 
137c478bd9Sstevel@tonic-gate /*
1465d0d3dcSsemery  * Solaris Kerberos:
157c478bd9Sstevel@tonic-gate  * An implementation for the memory only (mem) replay cache type.
167c478bd9Sstevel@tonic-gate  */
177c478bd9Sstevel@tonic-gate #include "rc_common.h"
187c478bd9Sstevel@tonic-gate #include "rc_mem.h"
197c478bd9Sstevel@tonic-gate 
2065d0d3dcSsemery /*
2165d0d3dcSsemery  * We want the replay cache to hang around for the entire life span of the
2265d0d3dcSsemery  * process, regardless if the auth_context or acceptor_cred handles are
2365d0d3dcSsemery  * destroyed.
2465d0d3dcSsemery  */
2565d0d3dcSsemery struct global_rcache grcache = {K5_MUTEX_PARTIAL_INITIALIZER, NULL};
2665d0d3dcSsemery 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * of course, list is backwards
297c478bd9Sstevel@tonic-gate  * hash could be forwards since we have to search on match, but naaaah
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate static int
rc_store(krb5_context context,krb5_rcache id,krb5_donot_replay * rep)327c478bd9Sstevel@tonic-gate rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
337c478bd9Sstevel@tonic-gate {
347c478bd9Sstevel@tonic-gate 	struct mem_data *t = (struct mem_data *)id->data;
357c478bd9Sstevel@tonic-gate 	int rephash;
367c478bd9Sstevel@tonic-gate 	struct authlist *ta, *pta = NULL, *head;
377c478bd9Sstevel@tonic-gate 	krb5_int32 time;
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate 	rephash = hash(rep, t->hsize);
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate 	/* Solaris: calling krb_timeofday() here, once for better perf. */
427c478bd9Sstevel@tonic-gate 	krb5_timeofday(context, &time);
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate 	/*
457c478bd9Sstevel@tonic-gate 	 * Solaris: calling alive() on rep since it doesn't make sense to store
467c478bd9Sstevel@tonic-gate 	 * an expired replay.
477c478bd9Sstevel@tonic-gate 	 */
487c478bd9Sstevel@tonic-gate 	if (alive(context, rep, t->lifespan, time) == CMP_EXPIRED)
497c478bd9Sstevel@tonic-gate 		return (CMP_EXPIRED);
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 	for (ta = t->h[rephash]; ta; ta = ta->nh) {
527c478bd9Sstevel@tonic-gate 		switch (cmp(&ta->rep, rep)) {
537c478bd9Sstevel@tonic-gate 			case CMP_REPLAY:
547c478bd9Sstevel@tonic-gate 				return (CMP_REPLAY);
557c478bd9Sstevel@tonic-gate 			case CMP_HOHUM:
567c478bd9Sstevel@tonic-gate 				if (alive(context, &ta->rep, t->lifespan, time)
577c478bd9Sstevel@tonic-gate 				    == CMP_EXPIRED) {
587c478bd9Sstevel@tonic-gate 					free(ta->rep.client);
597c478bd9Sstevel@tonic-gate 					free(ta->rep.server);
607c478bd9Sstevel@tonic-gate 					if (pta) {
617c478bd9Sstevel@tonic-gate 						pta->nh = ta->nh;
627c478bd9Sstevel@tonic-gate 						free(ta);
637c478bd9Sstevel@tonic-gate 						ta = pta;
647c478bd9Sstevel@tonic-gate 					} else {
657c478bd9Sstevel@tonic-gate 						head = t->h[rephash];
667c478bd9Sstevel@tonic-gate 						t->h[rephash] = ta->nh;
677c478bd9Sstevel@tonic-gate 						free(head);
687c478bd9Sstevel@tonic-gate 					}
697c478bd9Sstevel@tonic-gate 					continue;
707c478bd9Sstevel@tonic-gate 				}
717c478bd9Sstevel@tonic-gate 		}
727c478bd9Sstevel@tonic-gate 		pta = ta;
737c478bd9Sstevel@tonic-gate 	}
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	if (!(ta = (struct authlist *)malloc(sizeof (struct authlist))))
767c478bd9Sstevel@tonic-gate 		return (CMP_MALLOC);
777c478bd9Sstevel@tonic-gate 	ta->rep = *rep;
787c478bd9Sstevel@tonic-gate 	if (!(ta->rep.client = strdup(rep->client))) {
797c478bd9Sstevel@tonic-gate 		free(ta);
807c478bd9Sstevel@tonic-gate 		return (CMP_MALLOC);
817c478bd9Sstevel@tonic-gate 	}
827c478bd9Sstevel@tonic-gate 	if (!(ta->rep.server = strdup(rep->server))) {
837c478bd9Sstevel@tonic-gate 		free(ta->rep.client);
847c478bd9Sstevel@tonic-gate 		free(ta);
857c478bd9Sstevel@tonic-gate 		return (CMP_MALLOC);
867c478bd9Sstevel@tonic-gate 	}
877c478bd9Sstevel@tonic-gate 	ta->nh = t->h[rephash];
887c478bd9Sstevel@tonic-gate 	t->h[rephash] = ta;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	return (CMP_HOHUM);
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*ARGSUSED*/
947c478bd9Sstevel@tonic-gate char *KRB5_CALLCONV
krb5_rc_mem_get_name(krb5_context context,krb5_rcache id)957c478bd9Sstevel@tonic-gate krb5_rc_mem_get_name(krb5_context context, krb5_rcache id)
967c478bd9Sstevel@tonic-gate {
977c478bd9Sstevel@tonic-gate 	return (((struct mem_data *)(id->data))->name);
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1017c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_get_span(krb5_context context,krb5_rcache id,krb5_deltat * lifespan)1027c478bd9Sstevel@tonic-gate krb5_rc_mem_get_span(
1037c478bd9Sstevel@tonic-gate 	krb5_context context,
1047c478bd9Sstevel@tonic-gate 	krb5_rcache id,
1057c478bd9Sstevel@tonic-gate 	krb5_deltat *lifespan)
1067c478bd9Sstevel@tonic-gate {
107505d05c7Sgtb     krb5_error_code err;
108505d05c7Sgtb     struct mem_data *t;
109505d05c7Sgtb 
110505d05c7Sgtb     err = k5_mutex_lock(&id->lock);
111505d05c7Sgtb     if (err)
112505d05c7Sgtb 	return err;
11365d0d3dcSsemery 
11465d0d3dcSsemery     if (err = k5_mutex_lock(&grcache.lock)) {
11565d0d3dcSsemery 	k5_mutex_unlock(&id->lock);
11665d0d3dcSsemery 	return (err);
11765d0d3dcSsemery     }
118505d05c7Sgtb     t = (struct mem_data *) id->data;
119505d05c7Sgtb     *lifespan = t->lifespan;
12065d0d3dcSsemery     k5_mutex_unlock(&grcache.lock);
12165d0d3dcSsemery 
122505d05c7Sgtb     k5_mutex_unlock(&id->lock);
123505d05c7Sgtb     return 0;
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_init_locked(krb5_context context,krb5_rcache id,krb5_deltat lifespan)127505d05c7Sgtb krb5_rc_mem_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate 	struct mem_data *t = (struct mem_data *)id->data;
1307c478bd9Sstevel@tonic-gate 	krb5_error_code retval;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	t->lifespan = lifespan ? lifespan : context->clockskew;
1337c478bd9Sstevel@tonic-gate 	/* default to clockskew from the context */
1347c478bd9Sstevel@tonic-gate 	return (0);
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate 
137505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_rc_mem_init(krb5_context context,krb5_rcache id,krb5_deltat lifespan)138505d05c7Sgtb krb5_rc_mem_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
139505d05c7Sgtb {
140505d05c7Sgtb     krb5_error_code retval;
141505d05c7Sgtb 
142505d05c7Sgtb     retval = k5_mutex_lock(&id->lock);
143505d05c7Sgtb     if (retval)
144505d05c7Sgtb 	return retval;
14565d0d3dcSsemery     retval = k5_mutex_lock(&grcache.lock);
14665d0d3dcSsemery     if (retval) {
14765d0d3dcSsemery 	k5_mutex_unlock(&id->lock);
14865d0d3dcSsemery 	return (retval);
14965d0d3dcSsemery     }
15065d0d3dcSsemery 
151505d05c7Sgtb     retval = krb5_rc_mem_init_locked(context, id, lifespan);
15265d0d3dcSsemery 
15365d0d3dcSsemery     k5_mutex_unlock(&grcache.lock);
154505d05c7Sgtb     k5_mutex_unlock(&id->lock);
155505d05c7Sgtb     return retval;
156505d05c7Sgtb }
157505d05c7Sgtb 
15865d0d3dcSsemery /*
15965d0d3dcSsemery  * We want the replay cache to be persistent since we can't
16065d0d3dcSsemery  * read from a file to retrieve the rcache, so we must not free
16165d0d3dcSsemery  * here.  Just return success.
16265d0d3dcSsemery  */
1637c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_close(krb5_context context,krb5_rcache id)1647c478bd9Sstevel@tonic-gate krb5_rc_mem_close(krb5_context context, krb5_rcache id)
1657c478bd9Sstevel@tonic-gate {
16665d0d3dcSsemery 	return (0);
1677c478bd9Sstevel@tonic-gate }
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_destroy(krb5_context context,krb5_rcache id)1707c478bd9Sstevel@tonic-gate krb5_rc_mem_destroy(krb5_context context, krb5_rcache id)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	return (krb5_rc_mem_close(context, id));
1737c478bd9Sstevel@tonic-gate }
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1767c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_resolve(krb5_context context,krb5_rcache id,char * name)1777c478bd9Sstevel@tonic-gate krb5_rc_mem_resolve(krb5_context context, krb5_rcache id, char *name)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	struct mem_data *t = 0;
1807c478bd9Sstevel@tonic-gate 	krb5_error_code retval;
1817c478bd9Sstevel@tonic-gate 
18265d0d3dcSsemery 	retval = k5_mutex_lock(&grcache.lock);
18365d0d3dcSsemery 	if (retval)
18465d0d3dcSsemery 		return (retval);
18565d0d3dcSsemery 
18665d0d3dcSsemery 	/*
18765d0d3dcSsemery 	 * If the global rcache has already been initialized through a prior
18865d0d3dcSsemery 	 * call to this function then just set the rcache to point to it for
18965d0d3dcSsemery 	 * any subsequent operations.
19065d0d3dcSsemery 	 */
19165d0d3dcSsemery 	if (grcache.data != NULL) {
19265d0d3dcSsemery 		id->data = (krb5_pointer)grcache.data;
19365d0d3dcSsemery 		k5_mutex_unlock(&grcache.lock);
19465d0d3dcSsemery 		return (0);
19565d0d3dcSsemery 	}
1967c478bd9Sstevel@tonic-gate 	/* allocate id? no */
19765d0d3dcSsemery 	if (!(t = (struct mem_data *)malloc(sizeof (struct mem_data)))) {
19865d0d3dcSsemery 		k5_mutex_unlock(&grcache.lock);
1997c478bd9Sstevel@tonic-gate 		return (KRB5_RC_MALLOC);
20065d0d3dcSsemery 	}
20165d0d3dcSsemery 	grcache.data = id->data = (krb5_pointer)t;
2027c478bd9Sstevel@tonic-gate 	memset(t, 0, sizeof (struct mem_data));
2037c478bd9Sstevel@tonic-gate 	if (name) {
2047c478bd9Sstevel@tonic-gate 		t->name = malloc(strlen(name)+1);
2057c478bd9Sstevel@tonic-gate 		if (!t->name) {
2067c478bd9Sstevel@tonic-gate 			retval = KRB5_RC_MALLOC;
2077c478bd9Sstevel@tonic-gate 			goto cleanup;
2087c478bd9Sstevel@tonic-gate 		}
2097c478bd9Sstevel@tonic-gate 		strcpy(t->name, name);
2107c478bd9Sstevel@tonic-gate 	} else
2117c478bd9Sstevel@tonic-gate 		t->name = 0;
2127c478bd9Sstevel@tonic-gate 	t->hsize = HASHSIZE; /* no need to store---it's memory-only */
2137c478bd9Sstevel@tonic-gate 	t->h = (struct authlist **)malloc(t->hsize*sizeof (struct authlist *));
2147c478bd9Sstevel@tonic-gate 	if (!t->h) {
2157c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_MALLOC;
2167c478bd9Sstevel@tonic-gate 		goto cleanup;
2177c478bd9Sstevel@tonic-gate 	}
2187c478bd9Sstevel@tonic-gate 	memset(t->h, 0, t->hsize*sizeof (struct authlist *));
21965d0d3dcSsemery 	k5_mutex_unlock(&grcache.lock);
2207c478bd9Sstevel@tonic-gate 	return (0);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate cleanup:
2237c478bd9Sstevel@tonic-gate 	if (t) {
2247c478bd9Sstevel@tonic-gate 		if (t->name)
2257c478bd9Sstevel@tonic-gate 			krb5_xfree(t->name);
2267c478bd9Sstevel@tonic-gate 		if (t->h)
2277c478bd9Sstevel@tonic-gate 			krb5_xfree(t->h);
2287c478bd9Sstevel@tonic-gate 		krb5_xfree(t);
229*1da57d55SToomas Soome 		grcache.data = NULL;
2307c478bd9Sstevel@tonic-gate 		id->data = NULL;
2317c478bd9Sstevel@tonic-gate 	}
23265d0d3dcSsemery 	k5_mutex_unlock(&grcache.lock);
2337c478bd9Sstevel@tonic-gate 	return (retval);
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate 
23665d0d3dcSsemery /*
23765d0d3dcSsemery  * Recovery (retrieval) of the replay cache occurred during
23865d0d3dcSsemery  * krb5_rc_resolve().  So we just return error here.
23965d0d3dcSsemery  */
2407c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_recover(krb5_context context,krb5_rcache id)2417c478bd9Sstevel@tonic-gate krb5_rc_mem_recover(krb5_context context, krb5_rcache id)
2427c478bd9Sstevel@tonic-gate {
243505d05c7Sgtb 	/* SUNW14resync - No need for locking here, just returning RC_NOIO */
2447c478bd9Sstevel@tonic-gate 	return (KRB5_RC_NOIO);
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate 
247505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_rc_mem_recover_or_init(krb5_context context,krb5_rcache id,krb5_deltat lifespan)248505d05c7Sgtb krb5_rc_mem_recover_or_init(krb5_context context, krb5_rcache id,
249505d05c7Sgtb 			    krb5_deltat lifespan)
250505d05c7Sgtb {
251505d05c7Sgtb     krb5_error_code retval;
252505d05c7Sgtb 
253*1da57d55SToomas Soome     retval = krb5_rc_mem_recover(context, id);
254505d05c7Sgtb     if (retval)
2553125ebfcSsemery 	retval = krb5_rc_mem_init(context, id, lifespan);
2563125ebfcSsemery 
257505d05c7Sgtb     return retval;
258505d05c7Sgtb }
259505d05c7Sgtb 
2607c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_rc_mem_store(krb5_context context,krb5_rcache id,krb5_donot_replay * rep)2617c478bd9Sstevel@tonic-gate krb5_rc_mem_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate 	krb5_error_code ret;
2647c478bd9Sstevel@tonic-gate 
2654ef135ebSsemery 	ret = k5_mutex_lock(&id->lock);
2664ef135ebSsemery 	if (ret)
2674ef135ebSsemery 		return (ret);
26865d0d3dcSsemery 	ret = k5_mutex_lock(&grcache.lock);
26965d0d3dcSsemery 	if (ret) {
27065d0d3dcSsemery 		k5_mutex_unlock(&id->lock);
27165d0d3dcSsemery 		return (ret);
27265d0d3dcSsemery 	}
2734ef135ebSsemery 
2747c478bd9Sstevel@tonic-gate 	switch (rc_store(context, id, rep)) {
2757c478bd9Sstevel@tonic-gate 		case CMP_MALLOC:
27665d0d3dcSsemery 			k5_mutex_unlock(&grcache.lock);
2774ef135ebSsemery 			k5_mutex_unlock(&id->lock);
2787c478bd9Sstevel@tonic-gate 			return (KRB5_RC_MALLOC);
2797c478bd9Sstevel@tonic-gate 		case CMP_REPLAY:
28065d0d3dcSsemery 			k5_mutex_unlock(&grcache.lock);
2814ef135ebSsemery 			k5_mutex_unlock(&id->lock);
2827c478bd9Sstevel@tonic-gate 			return (KRB5KRB_AP_ERR_REPEAT);
2837c478bd9Sstevel@tonic-gate 		case CMP_EXPIRED:
28465d0d3dcSsemery 			k5_mutex_unlock(&grcache.lock);
2854ef135ebSsemery 			k5_mutex_unlock(&id->lock);
2867c478bd9Sstevel@tonic-gate 			return (KRB5KRB_AP_ERR_SKEW);
2877c478bd9Sstevel@tonic-gate 		case CMP_HOHUM:
2887c478bd9Sstevel@tonic-gate 			break;
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 
29165d0d3dcSsemery 	k5_mutex_unlock(&grcache.lock);
2924ef135ebSsemery 	k5_mutex_unlock(&id->lock);
2937c478bd9Sstevel@tonic-gate 	return (0);
2947c478bd9Sstevel@tonic-gate }
295