1 /* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 6 /* 7 * lib/krb5/rcache/rc_io.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 15 /* 16 * I/O functions for the replay cache default implementation. 17 */ 18 19 #if defined(_WIN32) 20 # define PATH_SEPARATOR "\\" 21 #else 22 # define PATH_SEPARATOR "/" 23 #endif 24 25 #define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */ 26 27 #include "k5-int.h" 28 #include <stdio.h> /* for P_tmpdir */ 29 #include <sys/types.h> 30 #include <unistd.h> 31 #include <syslog.h> /* SUNW */ 32 #include <locale.h> /* Solaris Kerberos */ 33 #include "rc_base.h" 34 #include "rc_file.h" 35 #include "rc_io.h" 36 37 #ifndef O_BINARY 38 #define O_BINARY 0 39 #endif 40 41 #ifdef HAVE_NETINET_IN_H 42 #if !defined(_WINSOCKAPI_) 43 #include <netinet/in.h> 44 #endif 45 #else 46 #error find some way to use net-byte-order file version numbers. 47 #endif 48 49 /* Solaris Kerberos */ 50 #define FREE_RC(x) ((void) free((char *) (x))) 51 #define UNIQUE getpid() /* hopefully unique number */ 52 53 #define GETDIR (dir = getdir(), dirlen = strlen(dir) + sizeof(PATH_SEPARATOR) - 1) 54 55 static char * 56 getdir(void) 57 { 58 char *dir; 59 60 #if defined(_WIN32) 61 if (!(dir = getenv("TEMP"))) 62 if (!(dir = getenv("TMP"))) 63 dir = "C:"; 64 #else 65 /* Solaris Kerberos */ 66 if (geteuid() == 0) 67 dir = "/var/krb5/rcache/root"; 68 else 69 dir = "/var/krb5/rcache"; 70 #endif 71 return dir; 72 } 73 74 krb5_error_code 75 krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn) 76 { 77 char *c; 78 krb5_int16 rc_vno = htons(KRB5_RC_VNO); 79 krb5_error_code retval = 0; 80 int do_not_unlink = 0; 81 char *dir; 82 size_t dirlen; 83 84 GETDIR; 85 if (fn && *fn) 86 { 87 /* Solaris Kerberos */ 88 if (*fn[0] == '/') { 89 d->fn = strdup(*fn); 90 if (d->fn == NULL) 91 return (KRB5_RC_IO_MALLOC); 92 } else { 93 if (!(d->fn = malloc(strlen(*fn) + dirlen + 1))) 94 return KRB5_RC_IO_MALLOC; 95 (void) strcpy(d->fn, dir); 96 (void) strcat(d->fn, PATH_SEPARATOR); 97 (void) strcat(d->fn, *fn); 98 } 99 d->fd = THREEPARAMOPEN(d->fn, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | 100 O_BINARY, 0600); 101 } 102 else 103 { 104 /* %d is max 11 digits (-, 10 digits of 32-bit number) 105 * 11 + /krb5_RC + aaa = 24, +6 for slop */ 106 if (!(d->fn = malloc(30 + dirlen))) 107 return KRB5_RC_IO_MALLOC; 108 if (fn) 109 if (!(*fn = malloc(35))) { 110 FREE_RC(d->fn); 111 return KRB5_RC_IO_MALLOC; 112 } 113 (void) sprintf(d->fn, "%s%skrb5_RC%d", dir, PATH_SEPARATOR, 114 (int) UNIQUE); 115 c = d->fn + strlen(d->fn); 116 (void) strcpy(c, "aaa"); 117 while ((d->fd = THREEPARAMOPEN(d->fn, O_WRONLY | O_CREAT | O_TRUNC | 118 O_EXCL | O_BINARY, 0600)) == -1) 119 { 120 if ((c[2]++) == 'z') 121 { 122 c[2] = 'a'; 123 if ((c[1]++) == 'z') 124 { 125 c[1] = 'a'; 126 if ((c[0]++) == 'z') 127 break; /* sigh */ 128 } 129 } 130 } 131 if (fn) 132 (void) strcpy(*fn, d->fn + dirlen); 133 } 134 if (d->fd == -1) 135 { 136 switch(errno) 137 { 138 case EFBIG: 139 #ifdef EDQUOT 140 case EDQUOT: 141 #endif 142 case ENOSPC: 143 retval = KRB5_RC_IO_SPACE; 144 goto cleanup; 145 146 case EIO: 147 retval = KRB5_RC_IO_IO; 148 goto cleanup; 149 150 case EPERM: 151 case EACCES: 152 case EROFS: 153 case EEXIST: 154 retval = KRB5_RC_IO_PERM; 155 krb5_set_error_message(context, retval, 156 dgettext(TEXT_DOMAIN, 157 "Cannot create replay cache %s: %s"), 158 d->fn ? d->fn : "<null>", 159 strerror(errno)); 160 do_not_unlink = 1; 161 goto cleanup; 162 163 default: 164 retval = KRB5_RC_IO_UNKNOWN; 165 krb5_set_error_message(context, retval, 166 dgettext(TEXT_DOMAIN, 167 "Cannot create replay cache %s: %s"), 168 d->fn ? d->fn : "<null>", 169 strerror(errno)); 170 goto cleanup; 171 } 172 } 173 retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno, 174 sizeof(rc_vno)); 175 if (retval) 176 goto cleanup; 177 178 retval = krb5_rc_io_sync(context, d); 179 180 cleanup: 181 if (retval) { 182 if (d->fn) { 183 if (!do_not_unlink) 184 (void) unlink(d->fn); 185 FREE_RC(d->fn); 186 d->fn = NULL; 187 } 188 if (d->fd != -1) { 189 (void) close(d->fd); 190 } 191 } 192 return retval; 193 } 194 195 static krb5_error_code 196 krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn, 197 char* full_pathname) 198 { 199 krb5_int16 rc_vno; 200 krb5_error_code retval = 0; 201 int do_not_unlink = 1; 202 struct stat lstatb, fstatb; 203 int use_errno = 0; 204 char *dir; 205 size_t dirlen; 206 207 GETDIR; 208 if (fn[0] == '/') { 209 d->fn = strdup(fn); 210 if (d->fn == NULL) 211 return (KRB5_RC_IO_MALLOC); 212 } else { 213 if (!(d->fn = malloc(strlen(fn) + dirlen + 1))) 214 return KRB5_RC_IO_MALLOC; 215 (void) strcpy(d->fn, dir); 216 (void) strcat(d->fn, PATH_SEPARATOR); 217 (void) strcat(d->fn, fn); 218 } 219 220 /* Solaris: BEGIN made changes to be safer and better code structure */ 221 if ((d->fd = THREEPARAMOPEN(d->fn, O_RDWR|O_BINARY, 0600)) == -1) { 222 use_errno = 1; 223 goto cleanup; 224 } 225 226 do_not_unlink = 0; 227 if (fstat(d->fd, &fstatb) == 0) { 228 #ifndef NO_USERID 229 uid_t me; 230 231 me = geteuid(); 232 /* must be owned by this user, to prevent some security problems with 233 * other users modifying replay cache stuff and must be a regular file 234 */ 235 if ((fstatb.st_uid != me) || ((fstatb.st_mode & S_IFMT) != S_IFREG)) { 236 retval = KRB5_RC_IO_PERM; 237 goto cleanup; 238 } 239 #else 240 /* make sure the rcache is a regular file */ 241 if (((fstatb.st_mode & S_IFMT) != S_IFREG)) { 242 retval = KRB5_RC_IO_PERM; 243 244 goto cleanup; 245 } 246 #endif 247 if (lstat(d->fn, &lstatb) == 0) { 248 /* Make sure fstat() and lstat() have accessed the same file */ 249 if ((lstatb.st_ino != fstatb.st_ino) || 250 (lstatb.st_dev != fstatb.st_dev)) { 251 retval = KRB5_RC_IO_PERM; 252 goto cleanup; 253 } 254 255 if ((lstatb.st_mode & S_IFMT) == S_IFLNK) { 256 /* if we accessed the rcache via a symlink, bail out */ 257 syslog(LOG_ERR, "Error, krb replay cache %s is a symlink " 258 "and should be removed.\n", d->fn); 259 retval = KRB5_RC_IO_PERM; 260 goto cleanup; 261 } 262 } 263 else { 264 use_errno = 1; 265 goto cleanup; 266 } 267 } 268 else { 269 use_errno = 1; 270 goto cleanup; 271 } 272 273 do_not_unlink = 0; 274 retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno, 275 sizeof(rc_vno)); 276 if (retval) 277 goto cleanup; 278 279 if (ntohs(rc_vno) != KRB5_RC_VNO) 280 retval = KRB5_RCACHE_BADVNO; 281 282 cleanup: 283 if (use_errno) { 284 switch(errno) 285 { 286 case EFBIG: 287 #ifdef EDQUOT 288 case EDQUOT: 289 #endif 290 case ENOSPC: 291 retval = KRB5_RC_IO_SPACE; 292 break; 293 294 case EIO: 295 retval = KRB5_RC_IO_IO; 296 break; 297 298 case EPERM: 299 case EACCES: 300 case EROFS: 301 retval = KRB5_RC_IO_PERM; 302 krb5_set_error_message (context, retval, 303 dgettext(TEXT_DOMAIN, 304 "Cannot open replay cache %s: %s"), 305 d->fn, strerror(errno)); 306 break; 307 308 default: 309 retval = KRB5_RC_IO_UNKNOWN; 310 krb5_set_error_message (context, retval, 311 dgettext(TEXT_DOMAIN, 312 "Cannot open replay cache %s: %s"), 313 d->fn, strerror(errno)); 314 } 315 } 316 /* Solaris: END made changes to be safer and better code structure */ 317 if (retval) { 318 if (d->fn) { 319 if (!do_not_unlink) 320 (void) unlink(d->fn); 321 FREE_RC(d->fn); 322 d->fn = NULL; 323 } 324 if (d->fd >= 0) 325 (void) close(d->fd); 326 } 327 return retval; 328 } 329 330 krb5_error_code 331 krb5_rc_io_open(krb5_context context, krb5_rc_iostuff *d, char *fn) 332 { 333 return krb5_rc_io_open_internal(context, d, fn, NULL); 334 } 335 336 krb5_error_code 337 krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1, 338 krb5_rc_iostuff *old) 339 { 340 #if defined(_WIN32) || defined(__CYGWIN__) 341 char *new_fn = NULL; 342 char *old_fn = NULL; 343 off_t offset = 0; 344 krb5_error_code retval = 0; 345 /* 346 * Initial work around provided by Tom Sanfilippo to work around 347 * poor Windows emulation of POSIX functions. Rename and dup has 348 * different semantics! 349 * 350 * Additional fixes and explanation provided by dalmeida@mit.edu: 351 * 352 * First, we save the offset of "old". Then, we close and remove 353 * the "new" file so we can do the rename. We also close "old" to 354 * make sure the rename succeeds (though that might not be 355 * necessary on some systems). 356 * 357 * Next, we do the rename. If all goes well, we seek the "new" 358 * file to the position "old" was at. 359 * 360 * --- WARNING!!! --- 361 * 362 * Since "old" is now gone, we mourn its disappearance, but we 363 * cannot emulate that Unix behavior... THIS BEHAVIOR IS 364 * DIFFERENT FROM UNIX. However, it is ok because this function 365 * gets called such that "old" gets closed right afterwards. 366 */ 367 offset = lseek(old->fd, 0, SEEK_CUR); 368 369 new_fn = new1->fn; 370 new1->fn = NULL; 371 close(new1->fd); 372 new1->fd = -1; 373 374 unlink(new_fn); 375 376 old_fn = old->fn; 377 old->fn = NULL; 378 close(old->fd); 379 old->fd = -1; 380 381 if (rename(old_fn, new_fn) == -1) { /* MUST be atomic! */ 382 retval = KRB5_RC_IO_UNKNOWN; 383 goto cleanup; 384 } 385 386 retval = krb5_rc_io_open_internal(context, new1, 0, new_fn); 387 if (retval) 388 goto cleanup; 389 390 if (lseek(new1->fd, offset, SEEK_SET) == -1) { 391 retval = KRB5_RC_IO_UNKNOWN; 392 goto cleanup; 393 } 394 395 cleanup: 396 free(new_fn); 397 free(old_fn); 398 return retval; 399 #else 400 char *fn = NULL; 401 if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */ 402 return KRB5_RC_IO_UNKNOWN; 403 fn = new1->fn; 404 new1->fn = NULL; /* avoid clobbering */ 405 (void) krb5_rc_io_close(context, new1); 406 new1->fn = fn; 407 new1->fd = dup(old->fd); 408 return 0; 409 #endif 410 } 411 412 krb5_error_code 413 krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf, 414 unsigned int num) 415 { 416 if (write(d->fd, (char *) buf, num) == -1) 417 switch(errno) 418 { 419 #ifdef EDQUOT 420 case EDQUOT: 421 #endif 422 case EFBIG: 423 case ENOSPC: 424 krb5_set_error_message (context, KRB5_RC_IO_SPACE, 425 dgettext(TEXT_DOMAIN, 426 "Can't write to replay cache %s: %s"), 427 d->fn, strerror(errno)); 428 return KRB5_RC_IO_SPACE; 429 case EIO: 430 krb5_set_error_message (context, KRB5_RC_IO_IO, 431 dgettext(TEXT_DOMAIN, 432 "Can't write to replay cache %s: %s"), 433 d->fn, strerror(errno)); 434 return KRB5_RC_IO_IO; 435 case EBADF: 436 default: 437 krb5_set_error_message (context, KRB5_RC_IO_UNKNOWN, 438 dgettext(TEXT_DOMAIN, 439 "Can't write to replay cache %s: %s"), 440 d->fn, strerror(errno)); 441 return KRB5_RC_IO_UNKNOWN; 442 } 443 return 0; 444 } 445 446 krb5_error_code 447 krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d) 448 { 449 #if defined(_WIN32) 450 #ifndef fsync 451 #define fsync _commit 452 #endif 453 #endif 454 if (fsync(d->fd) == -1) { 455 switch(errno) 456 { 457 case EBADF: return KRB5_RC_IO_UNKNOWN; 458 case EIO: return KRB5_RC_IO_IO; 459 default: 460 krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN, 461 dgettext(TEXT_DOMAIN, 462 "Cannot sync replay cache file %s: %s"), 463 d->fn, strerror(errno)); 464 return KRB5_RC_IO_UNKNOWN; 465 } 466 } 467 return 0; 468 } 469 470 /*ARGSUSED*/ 471 krb5_error_code 472 krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf, 473 unsigned int num) 474 { 475 int count; 476 if ((count = read(d->fd, (char *) buf, num)) == -1) 477 switch(errno) 478 { 479 case EIO: return KRB5_RC_IO_IO; 480 case EBADF: 481 default: 482 krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN, 483 dgettext(TEXT_DOMAIN, 484 "Can't read from replay cache %s: %s"), 485 d->fn, strerror(errno)); 486 return KRB5_RC_IO_UNKNOWN; 487 } 488 if (count == 0) 489 return KRB5_RC_IO_EOF; 490 return 0; 491 } 492 493 /*ARGSUSED*/ 494 krb5_error_code 495 krb5_rc_io_close(krb5_context context, krb5_rc_iostuff *d) 496 { 497 if (d->fn != NULL) { 498 FREE_RC(d->fn); 499 d->fn = NULL; 500 } 501 if (d->fd != -1) { 502 if (close(d->fd) == -1) /* can't happen */ 503 return KRB5_RC_IO_UNKNOWN; 504 d->fd = -1; 505 } 506 return 0; 507 } 508 509 /*ARGSUSED*/ 510 krb5_error_code 511 krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d) 512 { 513 if (unlink(d->fn) == -1) 514 switch(errno) 515 { 516 case EIO: 517 krb5_set_error_message(context, KRB5_RC_IO_IO, 518 dgettext(TEXT_DOMAIN, 519 "Can't destroy replay cache %s: %s"), 520 d->fn, strerror(errno)); 521 return KRB5_RC_IO_IO; 522 case EPERM: 523 case EBUSY: 524 case EROFS: 525 krb5_set_error_message(context, KRB5_RC_IO_PERM, 526 dgettext(TEXT_DOMAIN, 527 "Can't destroy replay cache %s: %s"), 528 d->fn, strerror(errno)); 529 return KRB5_RC_IO_PERM; 530 case EBADF: 531 default: 532 krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN, 533 dgettext(TEXT_DOMAIN, 534 "Can't destroy replay cache %s: %s"), 535 d->fn, strerror(errno)); 536 return KRB5_RC_IO_UNKNOWN; 537 } 538 return 0; 539 } 540 541 /*ARGSUSED*/ 542 krb5_error_code 543 krb5_rc_io_mark(krb5_context context, krb5_rc_iostuff *d) 544 { 545 d->mark = lseek(d->fd, (off_t) 0, SEEK_CUR); /* can't fail */ 546 return 0; 547 } 548 549 /*ARGSUSED*/ 550 krb5_error_code 551 krb5_rc_io_unmark(krb5_context context, krb5_rc_iostuff *d) 552 { 553 (void) lseek(d->fd, d->mark, SEEK_SET); /* if it fails, tough luck */ 554 return 0; 555 } 556 557 /*ARGSUSED*/ 558 long 559 krb5_rc_io_size(krb5_context context, krb5_rc_iostuff *d) 560 { 561 struct stat statb; 562 563 if (fstat(d->fd, &statb) == 0) 564 return statb.st_size; 565 else 566 return 0; 567 } 568