1 /* 2 * Copyright 2004 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(_MSDOS) || 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_) && !defined(HAVE_MACSOCK_H) 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 #ifndef HAVE_ERRNO 51 extern int errno; /* this should be in errno.h, but isn't on some systems */ 52 #endif 53 54 #define free(x) ((void) free((char *) (x))) 55 #define UNIQUE getpid() /* hopefully unique number */ 56 57 static int dirlen = 0; 58 static char *dir; 59 60 /* The do ... while(0) is required to insure that GETDIR looks like a 61 single statement in all situations (just {}'s may cause troubles in 62 certain situations, such as nested if/else clauses. */ 63 64 static int false = 0; 65 #define GETDIR do { if (!dirlen) getdir(); } while(false) 66 67 static void 68 getdir(void) 69 { 70 #if defined(_MSDOS) || defined(_WIN32) 71 if (!(dir = getenv("TEMP"))) 72 if (!(dir = getenv("TMP"))) 73 dir = "C:\\"; 74 #else 75 if (geteuid() == 0) 76 dir = "/var/krb5/rcache/root"; 77 else 78 dir = "/var/krb5/rcache"; 79 #endif 80 dirlen = strlen(dir) + sizeof(PATH_SEPARATOR); 81 } 82 83 krb5_error_code krb5_rc_io_creat (context, d, fn) 84 krb5_context context; 85 krb5_rc_iostuff *d; 86 char **fn; 87 { 88 char *c; 89 krb5_int16 rc_vno = htons(KRB5_RC_VNO); 90 krb5_error_code retval; 91 92 GETDIR; 93 if (fn && *fn) 94 { 95 if (*fn[0] == '/') { 96 d->fn = strdup(*fn); 97 if (d->fn == NULL) 98 return (KRB5_RC_IO_MALLOC); 99 } else { 100 if (!(d->fn = malloc(strlen(*fn) + dirlen + 1))) 101 return KRB5_RC_IO_MALLOC; 102 (void) strcpy(d->fn, dir); 103 (void) strcat(d->fn, PATH_SEPARATOR); 104 (void) strcat(d->fn,*fn); 105 } 106 d->fd = THREEPARAMOPEN(d->fn,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0600); 107 } 108 else 109 { 110 /* %d is max 11 digits (-, 10 digits of 32-bit number) 111 * 11 + /krb5_RC + aaa = 24, +6 for slop */ 112 if (!(d->fn = malloc(30 + dirlen))) 113 return KRB5_RC_IO_MALLOC; 114 if (fn) 115 if (!(*fn = malloc(35))) 116 { free(d->fn); return KRB5_RC_IO_MALLOC; } 117 (void) sprintf(d->fn,"%s%skrb5_RC%d",dir,PATH_SEPARATOR,UNIQUE); 118 c = d->fn + strlen(d->fn); 119 (void) strcpy(c,"aaa"); 120 while ((d->fd = THREEPARAMOPEN(d->fn,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY,0600)) == -1) 121 { 122 if ((c[2]++) == 'z') 123 { 124 c[2] = 'a'; 125 if ((c[1]++) == 'z') 126 { 127 c[1] = 'a'; 128 if ((c[0]++) == 'z') 129 break; /* sigh */ 130 } 131 } 132 } 133 if (fn) 134 (void) strcpy(*fn,d->fn + dirlen); 135 } 136 if (d->fd == -1) 137 { 138 switch(errno) 139 { 140 case EFBIG: 141 #ifdef EDQUOT 142 case EDQUOT: 143 #endif 144 case ENOSPC: 145 retval = KRB5_RC_IO_SPACE; 146 goto fail; 147 case EIO: 148 retval = KRB5_RC_IO_IO; goto fail; 149 150 case EPERM: 151 case EACCES: 152 case EROFS: 153 case EEXIST: 154 retval = KRB5_RC_IO_PERM; goto no_unlink; 155 156 default: 157 retval = KRB5_RC_IO_UNKNOWN; goto fail; 158 } 159 } 160 if (((retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno, sizeof(rc_vno))) != 0) || 161 (retval = krb5_rc_io_sync(context, d) != 0)) 162 { 163 fail: 164 (void) unlink(d->fn); 165 no_unlink: 166 syslog(LOG_ERR, "Could not create replay cache %s\n", d->fn); /* SUNW */ 167 free(d->fn); 168 d->fn = NULL; 169 (void) close(d->fd); 170 return retval; 171 } 172 return 0; 173 } 174 175 krb5_error_code krb5_rc_io_open (context, d, fn) 176 krb5_context context; 177 krb5_rc_iostuff *d; 178 char *fn; 179 { 180 krb5_int16 rc_vno; 181 krb5_error_code retval = 0; 182 int do_not_unlink = 1; 183 struct stat lstatb, fstatb; 184 int use_errno = 0; 185 186 GETDIR; 187 if (fn[0] == '/') { 188 d->fn = strdup(fn); 189 if (d->fn == NULL) 190 return (KRB5_RC_IO_MALLOC); 191 } else { 192 if (!(d->fn = malloc(strlen(fn) + dirlen + 1))) 193 return KRB5_RC_IO_MALLOC; 194 (void) strcpy(d->fn,dir); 195 (void) strcat(d->fn,PATH_SEPARATOR); 196 (void) strcat(d->fn,fn); 197 } 198 199 /* Solaris: BEGIN made changes to be safer and better code structure */ 200 if ((d->fd = THREEPARAMOPEN(d->fn, O_RDWR|O_BINARY, 0600)) == -1) { 201 use_errno = 1; 202 goto cleanup; 203 } 204 205 do_not_unlink = 0; 206 if (fstat(d->fd, &fstatb) == 0) { 207 #ifndef NO_USERID 208 uid_t me; 209 210 me = geteuid(); 211 /* must be owned by this user, to prevent some security problems with 212 * other users modifying replay cache stuff and must be a regular file 213 */ 214 if ((fstatb.st_uid != me) || ((fstatb.st_mode & S_IFMT) != S_IFREG)) { 215 retval = KRB5_RC_IO_PERM; 216 goto cleanup; 217 } 218 #else 219 /* make sure the rcache is a regular file */ 220 if (((fstatb.st_mode & S_IFMT) != S_IFREG)) { 221 retval = KRB5_RC_IO_PERM; 222 goto cleanup; 223 } 224 #endif 225 if (lstat(d->fn, &lstatb) == 0) { 226 /* Make sure fstat() and lstat() have accessed the same file */ 227 if ((lstatb.st_ino != fstatb.st_ino) || 228 (lstatb.st_dev != fstatb.st_dev)) { 229 retval = KRB5_RC_IO_PERM; 230 goto cleanup; 231 } 232 233 if ((lstatb.st_mode & S_IFMT) == S_IFLNK) { 234 /* if we accessed the rcache via a symlink, bail out */ 235 syslog(LOG_ERR, "Error, krb replay cache %s is a symlink " 236 "and should be removed.\n", d->fn); 237 retval = KRB5_RC_IO_PERM; 238 goto cleanup; 239 } 240 } 241 else { 242 use_errno = 1; 243 goto cleanup; 244 } 245 } 246 else { 247 use_errno = 1; 248 goto cleanup; 249 } 250 251 retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno, 252 sizeof(rc_vno)); 253 if (retval) 254 goto cleanup; 255 256 if (ntohs(rc_vno) != KRB5_RC_VNO) 257 retval = KRB5_RCACHE_BADVNO; 258 259 cleanup: 260 if (use_errno) { 261 switch(errno) 262 { 263 case EFBIG: 264 #ifdef EDQUOT 265 case EDQUOT: 266 #endif 267 case ENOSPC: 268 retval = KRB5_RC_IO_SPACE; 269 break; 270 271 case EIO: 272 retval = KRB5_RC_IO_IO; 273 break; 274 275 case EPERM: 276 case EACCES: 277 case EROFS: 278 retval = KRB5_RC_IO_PERM; 279 break; 280 281 default: 282 retval = KRB5_RC_IO_UNKNOWN; 283 } 284 } 285 /* Solaris: END made changes to be safer and better code structure */ 286 if (retval) { 287 if (d->fn) { 288 if (!do_not_unlink) { 289 /* unlink in case there is a bogus RC. */ 290 (void) unlink(d->fn); 291 } 292 free(d->fn); 293 d->fn = NULL; 294 } 295 (void) close(d->fd); 296 } 297 return retval; 298 } 299 300 krb5_error_code 301 krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1, 302 krb5_rc_iostuff *old) 303 { 304 char *fn = NULL; 305 306 #if defined(_MSDOS) || defined(_WIN32) 307 /* 308 * Work around provided by Tom Sanfilippo to work around poor 309 * Windows emulation of POSIX functions. Rename and dup has 310 * different semantics! 311 */ 312 char *fn = NULL; 313 GETDIR; 314 close(new->fd); 315 unlink(new->fn); 316 close(old->fd); 317 if (rename(old->fn,new->fn) == -1) /* MUST be atomic! */ 318 return KRB5_RC_IO_UNKNOWN; 319 if (!(fn = malloc(strlen(new->fn) - dirlen + 1))) 320 return KRB5_RC_IO_MALLOC; 321 strcpy(fn, new->fn + dirlen); 322 krb5_rc_io_close(context, new); 323 krb5_rc_io_open(context, new, fn); 324 free(fn); 325 #else 326 if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */ 327 return KRB5_RC_IO_UNKNOWN; 328 fn = new1->fn; 329 new1->fn = NULL; /* avoid clobbering */ 330 (void) krb5_rc_io_close(context, new1); 331 new1->fn = fn; 332 #ifdef macintosh 333 new1->fd = fcntl(old->fd, F_DUPFD); 334 #else 335 new1->fd = dup(old->fd); 336 #endif 337 #endif 338 return 0; 339 } 340 341 /*ARGSUSED*/ 342 krb5_error_code krb5_rc_io_write (context, d, buf, num) 343 krb5_context context; 344 krb5_rc_iostuff *d; 345 krb5_pointer buf; 346 int num; 347 { 348 if (write(d->fd,(char *) buf,num) == -1) 349 switch(errno) 350 { 351 case EBADF: return KRB5_RC_IO_UNKNOWN; 352 case EFBIG: return KRB5_RC_IO_SPACE; 353 #ifdef EDQUOT 354 case EDQUOT: return KRB5_RC_IO_SPACE; 355 #endif 356 case ENOSPC: return KRB5_RC_IO_SPACE; 357 case EIO: return KRB5_RC_IO_IO; 358 default: return KRB5_RC_IO_UNKNOWN; 359 } 360 return 0; 361 } 362 363 /*ARGSUSED*/ 364 krb5_error_code krb5_rc_io_sync (context, d) 365 krb5_context context; 366 krb5_rc_iostuff *d; 367 { 368 #if !defined(MSDOS_FILESYSTEM) && !defined(macintosh) 369 if (fsync(d->fd) == -1) { 370 switch(errno) 371 { 372 case EBADF: return KRB5_RC_IO_UNKNOWN; 373 case EIO: return KRB5_RC_IO_IO; 374 default: return KRB5_RC_IO_UNKNOWN; 375 } 376 } 377 #endif 378 return 0; 379 } 380 381 /*ARGSUSED*/ 382 krb5_error_code krb5_rc_io_read (context, d, buf, num) 383 krb5_context context; 384 krb5_rc_iostuff *d; 385 krb5_pointer buf; 386 int num; 387 { 388 int count; 389 if ((count = read(d->fd,(char *) buf,num)) == -1) 390 switch(errno) 391 { 392 case EBADF: return KRB5_RC_IO_UNKNOWN; 393 case EIO: return KRB5_RC_IO_IO; 394 default: return KRB5_RC_IO_UNKNOWN; 395 } 396 if (count == 0) 397 return KRB5_RC_IO_EOF; 398 return 0; 399 } 400 401 /*ARGSUSED*/ 402 krb5_error_code krb5_rc_io_close (context, d) 403 krb5_context context; 404 krb5_rc_iostuff *d; 405 { 406 free(d->fn); 407 d->fn = NULL; 408 if (close(d->fd) == -1) /* can't happen */ 409 return KRB5_RC_IO_UNKNOWN; 410 return 0; 411 } 412 413 /*ARGSUSED*/ 414 krb5_error_code krb5_rc_io_destroy (context, d) 415 krb5_context context; 416 krb5_rc_iostuff *d; 417 { 418 if (unlink(d->fn) == -1) 419 switch(errno) 420 { 421 case EBADF: return KRB5_RC_IO_UNKNOWN; 422 case EIO: return KRB5_RC_IO_IO; 423 case EPERM: return KRB5_RC_IO_PERM; 424 case EBUSY: return KRB5_RC_IO_PERM; 425 case EROFS: return KRB5_RC_IO_PERM; 426 default: return KRB5_RC_IO_UNKNOWN; 427 } 428 return 0; 429 } 430 431 /*ARGSUSED*/ 432 krb5_error_code krb5_rc_io_mark (context, d) 433 krb5_context context; 434 krb5_rc_iostuff *d; 435 { 436 d->mark = lseek(d->fd,0,SEEK_CUR); /* can't fail */ 437 return 0; 438 } 439 440 /*ARGSUSED*/ 441 krb5_error_code krb5_rc_io_unmark (context, d) 442 krb5_context context; 443 krb5_rc_iostuff *d; 444 { 445 (void) lseek(d->fd,d->mark,SEEK_SET); /* if it fails, tough luck */ 446 return 0; 447 } 448 449 /*ARGSUSED*/ 450 long 451 krb5_rc_io_size (context, d) 452 krb5_context context; 453 krb5_rc_iostuff *d; 454 { 455 struct stat statb; 456 457 if (fstat (d->fd, &statb) == 0) 458 return statb.st_size; 459 else 460 return 0; 461 } 462