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