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 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 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 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 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 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 164 krb5_rc_mem_close(krb5_context context, krb5_rcache id) 165 { 166 return (0); 167 } 168 169 krb5_error_code KRB5_CALLCONV 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 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 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 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 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