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