1 /* 2 * Copyright 2005 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 * lib/krb5/rcache/rc_file.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 /* 17 * An implementation for the default replay cache type. 18 */ 19 #include "rc_common.h" 20 #include "rc_file.h" 21 22 /* 23 * Solaris: The NOIOSTUFF macro has been taken out for the Solaris version 24 * of this module, because this has been split into a separate mem rcache. 25 */ 26 27 /* of course, list is backwards from file */ 28 /* hash could be forwards since we have to search on match, but naaaah */ 29 30 static int rc_store(context, id, rep) 31 krb5_context context; 32 krb5_rcache id; 33 krb5_donot_replay *rep; 34 { 35 struct file_data *t = (struct file_data *)id->data; 36 int rephash; 37 struct authlist *ta; 38 krb5_int32 time; 39 40 rephash = hash(rep,t->hsize); 41 42 /* Solaris: calling krb_timeofday() here, once for better perf. */ 43 krb5_timeofday(context, &time); 44 45 /* Solaris: calling alive() on rep since it doesn't make sense to store an 46 * expired replay. 47 */ 48 if (alive(context, rep, t->lifespan, time) == CMP_EXPIRED){ 49 return CMP_EXPIRED; 50 } 51 52 for (ta = t->h[rephash]; ta; ta = ta->nh) 53 switch(cmp(&ta->rep, rep)) 54 { 55 case CMP_REPLAY: return CMP_REPLAY; 56 case CMP_HOHUM: if (alive(context, &ta->rep, t->lifespan, time) 57 == CMP_EXPIRED) 58 t->nummisses++; 59 else 60 t->numhits++; 61 break; 62 default: ; /* wtf? */ 63 } 64 65 if (!(ta = (struct authlist *) malloc(sizeof(struct authlist)))) 66 return CMP_MALLOC; 67 ta->rep = *rep; 68 if (!(ta->rep.client = strdup(rep->client))) { 69 free(ta); 70 return CMP_MALLOC; 71 } 72 if (!(ta->rep.server = strdup(rep->server))) { 73 free(ta->rep.client); 74 free(ta); 75 return CMP_MALLOC; 76 } 77 ta->na = t->a; t->a = ta; 78 ta->nh = t->h[rephash]; t->h[rephash] = ta; 79 80 return CMP_HOHUM; 81 } 82 83 /*ARGSUSED*/ 84 char * KRB5_CALLCONV 85 krb5_rc_file_get_name(context, id) 86 krb5_context context; 87 krb5_rcache id; 88 { 89 return ((struct file_data *) (id->data))->name; 90 } 91 92 /*ARGSUSED*/ 93 krb5_error_code KRB5_CALLCONV 94 krb5_rc_file_get_span(context, id, lifespan) 95 krb5_context context; 96 krb5_rcache id; 97 krb5_deltat *lifespan; 98 { 99 krb5_error_code err; 100 struct file_data *t; 101 102 err = k5_mutex_lock(&id->lock); 103 if (err) 104 return err; 105 t = (struct file_data *) id->data; 106 *lifespan = t->lifespan; 107 k5_mutex_unlock(&id->lock); 108 return 0; 109 } 110 111 krb5_error_code KRB5_CALLCONV 112 krb5_rc_file_init_locked(context, id, lifespan) 113 krb5_context context; 114 krb5_rcache id; 115 krb5_deltat lifespan; 116 { 117 struct file_data *t = (struct file_data *)id->data; 118 krb5_error_code retval; 119 120 t->lifespan = lifespan ? lifespan : context->clockskew; 121 /* default to clockskew from the context */ 122 if ((retval = krb5_rc_io_creat(context, &t->d, &t->name))) 123 return retval; 124 if ((krb5_rc_io_write(context, &t->d, 125 (krb5_pointer) &t->lifespan, sizeof(t->lifespan)) 126 || krb5_rc_io_sync(context, &t->d))) 127 return KRB5_RC_IO; 128 return 0; 129 } 130 131 krb5_error_code KRB5_CALLCONV 132 krb5_rc_file_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan) 133 { 134 krb5_error_code retval; 135 136 retval = k5_mutex_lock(&id->lock); 137 if (retval) 138 return retval; 139 retval = krb5_rc_file_init_locked(context, id, lifespan); 140 k5_mutex_unlock(&id->lock); 141 return retval; 142 } 143 144 krb5_error_code krb5_rc_file_close_no_free(context, id) 145 krb5_context context; 146 krb5_rcache id; 147 { 148 struct file_data *t = (struct file_data *)id->data; 149 struct authlist *q; 150 151 if (t->h) 152 free(t->h); 153 if (t->name) 154 free(t->name); 155 156 while ((q = t->a) != NULL) 157 { 158 t->a = q->na; 159 free(q->rep.client); 160 free(q->rep.server); 161 free(q); 162 } 163 if (t->d.fd >= 0) 164 (void) krb5_rc_io_close(context, &t->d); 165 free(t); 166 id->data = NULL; 167 return 0; 168 } 169 170 krb5_error_code KRB5_CALLCONV 171 krb5_rc_file_close(context, id) 172 krb5_context context; 173 krb5_rcache id; 174 { 175 krb5_error_code retval; 176 retval = k5_mutex_lock(&id->lock); 177 if (retval) 178 return retval; 179 krb5_rc_file_close_no_free(context, id); 180 k5_mutex_unlock(&id->lock); 181 k5_mutex_destroy(&id->lock); 182 free(id); 183 return 0; 184 } 185 186 krb5_error_code KRB5_CALLCONV 187 krb5_rc_file_destroy(context, id) 188 krb5_context context; 189 krb5_rcache id; 190 { 191 if (krb5_rc_io_destroy(context, &((struct file_data *) (id->data))->d)) 192 return KRB5_RC_IO; 193 return krb5_rc_file_close(context, id); 194 } 195 196 /*ARGSUSED*/ 197 krb5_error_code KRB5_CALLCONV 198 krb5_rc_file_resolve(context, id, name) 199 krb5_context context; 200 krb5_rcache id; 201 char *name; 202 { 203 struct file_data *t = 0; 204 krb5_error_code retval; 205 206 /* allocate id? no */ 207 if (!(t = (struct file_data *) malloc(sizeof(struct file_data)))) 208 return KRB5_RC_MALLOC; 209 id->data = (krb5_pointer) t; 210 memset(t, 0, sizeof(struct file_data)); 211 if (name) { 212 t->name = malloc(strlen(name)+1); 213 if (!t->name) { 214 retval = KRB5_RC_MALLOC; 215 goto cleanup; 216 } 217 strcpy(t->name, name); 218 } else 219 t->name = 0; 220 t->numhits = t->nummisses = 0; 221 t->hsize = HASHSIZE; /* no need to store---it's memory-only */ 222 t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *)); 223 if (!t->h) { 224 retval = KRB5_RC_MALLOC; 225 goto cleanup; 226 } 227 memset(t->h, 0, t->hsize*sizeof(struct authlist *)); 228 t->a = (struct authlist *) 0; 229 t->d.fd = -1; 230 t->recovering = 0; 231 return 0; 232 233 cleanup: 234 if (t) { 235 if (t->name) 236 krb5_xfree(t->name); 237 if (t->h) 238 krb5_xfree(t->h); 239 krb5_xfree(t); 240 id->data = NULL; 241 } 242 return retval; 243 } 244 245 /*ARGSUSED*/ 246 void krb5_rc_free_entry (context, rep) 247 krb5_context context; 248 krb5_donot_replay **rep; 249 { 250 krb5_donot_replay *rp = *rep; 251 252 *rep = NULL; 253 if (rp) 254 { 255 if (rp->client) 256 free(rp->client); 257 258 if (rp->server) 259 free(rp->server); 260 rp->client = NULL; 261 rp->server = NULL; 262 free(rp); 263 } 264 } 265 266 static krb5_error_code krb5_rc_io_fetch(context, t, rep, maxlen) 267 krb5_context context; 268 struct file_data *t; 269 krb5_donot_replay *rep; 270 int maxlen; 271 { 272 int len; 273 krb5_error_code retval; 274 275 rep->client = rep->server = 0; 276 277 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &len, sizeof(len)); 278 if (retval) 279 return retval; 280 281 if ((len <= 0) || (len >= maxlen)) 282 return KRB5_RC_IO_EOF; 283 284 rep->client = malloc (len); 285 if (!rep->client) 286 return KRB5_RC_MALLOC; 287 288 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) rep->client, len); 289 if (retval) 290 goto errout; 291 292 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &len, sizeof(len)); 293 if (retval) 294 goto errout; 295 296 if ((len <= 0) || (len >= maxlen)) { 297 retval = KRB5_RC_IO_EOF; 298 goto errout; 299 } 300 301 rep->server = malloc (len); 302 if (!rep->server) { 303 retval = KRB5_RC_MALLOC; 304 goto errout; 305 } 306 307 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) rep->server, len); 308 if (retval) 309 goto errout; 310 311 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &rep->cusec, sizeof(rep->cusec)); 312 if (retval) 313 goto errout; 314 315 retval = krb5_rc_io_read (context, &t->d, (krb5_pointer) &rep->ctime, sizeof(rep->ctime)); 316 if (retval) 317 goto errout; 318 319 return 0; 320 321 errout: 322 if (rep->client) 323 krb5_xfree(rep->client); 324 if (rep->server) 325 krb5_xfree(rep->server); 326 rep->client = rep->server = 0; 327 return retval; 328 } 329 330 static krb5_error_code 331 krb5_rc_file_expunge_locked(krb5_context context, krb5_rcache id); 332 333 static krb5_error_code 334 krb5_rc_file_recover_locked(context, id) 335 krb5_context context; 336 krb5_rcache id; 337 { 338 struct file_data *t = (struct file_data *)id->data; 339 krb5_donot_replay *rep = 0; 340 krb5_error_code retval; 341 long max_size; 342 int expired_entries = 0; 343 344 if ((retval = krb5_rc_io_open(context, &t->d, t->name))) 345 return retval; 346 347 t->recovering = 1; 348 349 max_size = krb5_rc_io_size(context, &t->d); 350 351 rep = NULL; 352 if (krb5_rc_io_read(context, &t->d,(krb5_pointer) &t->lifespan,sizeof(t->lifespan))) { 353 retval = KRB5_RC_IO; 354 goto io_fail; 355 } 356 357 if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) { 358 retval = KRB5_RC_MALLOC; 359 goto io_fail; 360 } 361 rep->client = NULL; 362 rep->server = NULL; 363 364 /* now read in each auth_replay and insert into table */ 365 for (;;) { 366 if (krb5_rc_io_mark(context, &t->d)) { 367 retval = KRB5_RC_IO; 368 goto io_fail; 369 } 370 371 retval = krb5_rc_io_fetch (context, t, rep, (int) max_size); 372 373 if (retval == KRB5_RC_IO_EOF) 374 break; 375 else if (retval != 0) 376 goto io_fail; 377 378 /* Solaris: made the change below for better perf. */ 379 switch (rc_store(context, id, rep)) { 380 case CMP_EXPIRED: 381 expired_entries++; 382 break; 383 case CMP_MALLOC: 384 retval = KRB5_RC_MALLOC; 385 goto io_fail; 386 break; 387 } 388 /* 389 * free fields allocated by rc_io_fetch 390 */ 391 free(rep->server); 392 free(rep->client); 393 rep->server = 0; 394 rep->client = 0; 395 } 396 retval = 0; 397 krb5_rc_io_unmark(context, &t->d); 398 /* 399 * An automatic expunge here could remove the need for 400 * mark/unmark but that would be inefficient. 401 */ 402 io_fail: 403 krb5_rc_free_entry(context, &rep); 404 if (retval) 405 krb5_rc_io_close(context, &t->d); 406 else if (expired_entries > EXCESSREPS) 407 retval = krb5_rc_file_expunge_locked(context, id); 408 t->recovering = 0; 409 return retval; 410 } 411 412 413 krb5_error_code KRB5_CALLCONV 414 krb5_rc_file_recover(krb5_context context, krb5_rcache id) 415 { 416 krb5_error_code ret; 417 ret = k5_mutex_lock(&id->lock); 418 if (ret) 419 return ret; 420 ret = krb5_rc_file_recover_locked(context, id); 421 k5_mutex_unlock(&id->lock); 422 return ret; 423 } 424 425 krb5_error_code KRB5_CALLCONV 426 krb5_rc_file_recover_or_init(krb5_context context, krb5_rcache id, 427 krb5_deltat lifespan) 428 { 429 krb5_error_code retval; 430 431 retval = k5_mutex_lock(&id->lock); 432 if (retval) 433 return retval; 434 retval = krb5_rc_file_recover_locked(context, id); 435 if (retval) 436 retval = krb5_rc_file_init_locked(context, id, lifespan); 437 k5_mutex_unlock(&id->lock); 438 return retval; 439 } 440 441 442 static krb5_error_code 443 krb5_rc_io_store (context, t, rep) 444 krb5_context context; 445 struct file_data *t; 446 krb5_donot_replay *rep; 447 { 448 int clientlen, serverlen, len; 449 char *buf, *ptr; 450 krb5_error_code ret; 451 452 clientlen = strlen (rep->client) + 1; 453 serverlen = strlen (rep->server) + 1; 454 len = sizeof(clientlen) + clientlen + sizeof(serverlen) + serverlen + 455 sizeof(rep->cusec) + sizeof(rep->ctime); 456 buf = malloc (len); 457 if (buf == 0) 458 return KRB5_RC_MALLOC; 459 ptr = buf; 460 memcpy(ptr, &clientlen, sizeof(clientlen)); ptr += sizeof(clientlen); 461 memcpy(ptr, rep->client, clientlen); ptr += clientlen; 462 memcpy(ptr, &serverlen, sizeof(serverlen)); ptr += sizeof(serverlen); 463 memcpy(ptr, rep->server, serverlen); ptr += serverlen; 464 memcpy(ptr, &rep->cusec, sizeof(rep->cusec)); ptr += sizeof(rep->cusec); 465 memcpy(ptr, &rep->ctime, sizeof(rep->ctime)); ptr += sizeof(rep->ctime); 466 467 ret = krb5_rc_io_write(context, &t->d, buf, len); 468 free(buf); 469 return ret; 470 } 471 472 static krb5_error_code krb5_rc_file_expunge_locked(krb5_context, krb5_rcache); 473 474 krb5_error_code KRB5_CALLCONV 475 krb5_rc_file_store(context, id, rep) 476 krb5_context context; 477 krb5_rcache id; 478 krb5_donot_replay *rep; 479 { 480 krb5_error_code ret; 481 struct file_data *t; 482 483 ret = k5_mutex_lock(&id->lock); 484 if (ret) 485 return ret; 486 487 t = (struct file_data *)id->data; 488 489 switch(rc_store(context, id,rep)) { 490 case CMP_MALLOC: 491 k5_mutex_unlock(&id->lock); 492 return KRB5_RC_MALLOC; 493 case CMP_REPLAY: 494 k5_mutex_unlock(&id->lock); 495 return KRB5KRB_AP_ERR_REPEAT; 496 case CMP_EXPIRED: 497 k5_mutex_unlock(&id->lock); 498 return KRB5KRB_AP_ERR_SKEW; 499 case CMP_HOHUM: break; 500 default: /* wtf? */ ; 501 } 502 ret = krb5_rc_io_store (context, t, rep); 503 if (ret) { 504 k5_mutex_unlock(&id->lock); 505 return ret; 506 } 507 /* Shall we automatically expunge? */ 508 if (t->nummisses > t->numhits + EXCESSREPS) 509 { 510 ret = krb5_rc_file_expunge_locked(context, id); 511 k5_mutex_unlock(&id->lock); 512 return ret; 513 } 514 else 515 { 516 if (krb5_rc_io_sync(context, &t->d)) { 517 k5_mutex_unlock(&id->lock); 518 return KRB5_RC_IO; 519 } 520 } 521 k5_mutex_unlock(&id->lock); 522 return 0; 523 } 524 525 static krb5_error_code 526 krb5_rc_file_expunge_locked(context, id) 527 krb5_context context; 528 krb5_rcache id; 529 { 530 struct file_data *t = (struct file_data *)id->data; 531 struct authlist *q; 532 char *name; 533 krb5_error_code retval = 0; 534 krb5_rcache tmp; 535 krb5_deltat lifespan = t->lifespan; /* save original lifespan */ 536 537 if (! t->recovering) { 538 name = t->name; 539 t->name = 0; /* Clear name so it isn't freed */ 540 (void) krb5_rc_file_close_no_free(context, id); 541 retval = krb5_rc_file_resolve(context, id, name); 542 free(name); 543 if (retval) 544 return retval; 545 retval = krb5_rc_file_recover_locked(context, id); 546 if (retval) 547 return retval; 548 t = (struct file_data *)id->data; /* point to recovered cache */ 549 } 550 551 tmp = (krb5_rcache) malloc(sizeof(*tmp)); 552 if (!tmp) 553 return ENOMEM; 554 555 retval = k5_mutex_init(&tmp->lock); 556 if (retval) { 557 free (tmp); 558 return retval; 559 } 560 561 tmp->ops = &krb5_rc_file_ops; 562 if ((retval = krb5_rc_file_resolve(context, tmp, 0)) != 0) 563 goto out; 564 if ((retval = krb5_rc_initialize(context, tmp, lifespan)) != 0) 565 goto out; 566 for (q = t->a;q;q = q->na) { 567 if (krb5_rc_io_store (context, (struct file_data *)tmp->data, &q->rep)) { 568 retval = KRB5_RC_IO; 569 goto out; 570 } 571 } 572 if (krb5_rc_io_sync(context, &t->d)) { 573 retval = KRB5_RC_IO; 574 goto out; 575 } 576 if (krb5_rc_io_move(context, &t->d, &((struct file_data *)tmp->data)->d)) 577 retval = KRB5_RC_IO; 578 579 out: 580 /* 581 * krb5_rc_file_close() will free the tmp struct and it's members that the 582 * previous functions had allocated. 583 */ 584 (void) krb5_rc_file_close(context, tmp); 585 586 return (retval); 587 } 588 589 krb5_error_code KRB5_CALLCONV 590 krb5_rc_file_expunge(krb5_context context, krb5_rcache id) 591 { 592 krb5_error_code ret; 593 ret = k5_mutex_lock(&id->lock); 594 if (ret) 595 return ret; 596 ret = krb5_rc_file_expunge_locked(context, id); 597 k5_mutex_unlock(&id->lock); 598 return ret; 599 } 600