/* * lib/krb5/rcache/rc_io.c * * This file of the Kerberos V5 software is derived from public-domain code * contributed by Daniel J. Bernstein, . * */ /* * I/O functions for the replay cache default implementation. */ #if defined(_MSDOS) || defined(_WIN32) # define PATH_SEPARATOR "\\" #else # define PATH_SEPARATOR "/" #endif #define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */ #define NEED_SOCKETS #define NEED_LOWLEVEL_IO #include #include /* for P_tmpdir */ #include "rc_base.h" #include "rc_dfl.h" #include "rc_io.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifdef HAVE_NETINET_IN_H #if !defined(_WINSOCKAPI_) && !defined(HAVE_MACSOCK_H) #include #endif #else #error find some way to use net-byte-order file version numbers. #endif #ifndef HAVE_ERRNO extern int errno; /* this should be in errno.h, but isn't on some systems */ #endif #define FREE(x) ((void) free((char *) (x))) #define UNIQUE getpid() /* hopefully unique number */ static int dirlen = 0; static char *dir; /* The do ... while(0) is required to insure that GETDIR looks like a single statement in all situations (just {}'s may cause troubles in certain situations, such as nested if/else clauses. */ #define GETDIR do { if (!dirlen) getdir(); } while(0) static void getdir() { if (!dirlen) { if (!(dir = getenv("KRB5RCACHEDIR"))) #if defined(_MSDOS) || defined(_WIN32) if (!(dir = getenv("TEMP"))) if (!(dir = getenv("TMP"))) dir = "C:\\"; #else if (!(dir = getenv("TMPDIR"))) #ifdef RCTMPDIR dir = RCTMPDIR; #else dir = "/tmp"; #endif #endif dirlen = strlen(dir) + 1; } } krb5_error_code krb5_rc_io_creat (context, d, fn) krb5_context context; krb5_rc_iostuff *d; char **fn; { char *c; krb5_int16 rc_vno = htons(KRB5_RC_VNO); krb5_error_code retval; GETDIR; if (fn && *fn) { if (!(d->fn = malloc(strlen(*fn) + dirlen + 1))) return KRB5_RC_IO_MALLOC; (void) strcpy(d->fn,dir); (void) strcat(d->fn,PATH_SEPARATOR); (void) strcat(d->fn,*fn); d->fd = THREEPARAMOPEN(d->fn,O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY,0600); } else { /* %d is max 11 digits (-, 10 digits of 32-bit number) * 11 + /krb5_RC + aaa = 24, +6 for slop */ if (!(d->fn = malloc(30 + dirlen))) return KRB5_RC_IO_MALLOC; if (fn) if (!(*fn = malloc(35))) { FREE(d->fn); return KRB5_RC_IO_MALLOC; } (void) sprintf(d->fn,"%s%skrb5_RC%d",dir,PATH_SEPARATOR,UNIQUE); c = d->fn + strlen(d->fn); (void) strcpy(c,"aaa"); while ((d->fd = THREEPARAMOPEN(d->fn,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY,0600)) == -1) { if ((c[2]++) == 'z') { c[2] = 'a'; if ((c[1]++) == 'z') { c[1] = 'a'; if ((c[0]++) == 'z') break; /* sigh */ } } } if (fn) (void) strcpy(*fn,d->fn + dirlen); } if (d->fd == -1) { switch(errno) { case EFBIG: #ifdef EDQUOT case EDQUOT: #endif case ENOSPC: retval = KRB5_RC_IO_SPACE; goto fail; case EIO: retval = KRB5_RC_IO_IO; goto fail; case EPERM: case EACCES: case EROFS: case EEXIST: retval = KRB5_RC_IO_PERM; goto no_unlink; default: retval = KRB5_RC_IO_UNKNOWN; goto fail; } } if ((retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno, sizeof(rc_vno))) || (retval = krb5_rc_io_sync(context, d))) { fail: (void) unlink(d->fn); no_unlink: FREE(d->fn); d->fn = NULL; (void) close(d->fd); return retval; } return 0; } krb5_error_code krb5_rc_io_open (context, d, fn) krb5_context context; krb5_rc_iostuff *d; char *fn; { krb5_int16 rc_vno; krb5_error_code retval; #ifndef NO_USERID struct stat statb; #endif GETDIR; if (!(d->fn = malloc(strlen(fn) + dirlen + 1))) return KRB5_RC_IO_MALLOC; (void) strcpy(d->fn,dir); (void) strcat(d->fn,PATH_SEPARATOR); (void) strcat(d->fn,fn); #ifdef NO_USERID d->fd = THREEPARAMOPEN(d->fn,O_RDWR | O_BINARY,0600); #else if ((d->fd = stat(d->fn, &statb)) != -1) { uid_t me; me = geteuid(); /* must be owned by this user, to prevent some security problems with * other users modifying replay cache stufff */ if ((statb.st_uid != me) || ((statb.st_mode & S_IFMT) != S_IFREG)) { FREE(d->fn); return KRB5_RC_IO_PERM; } d->fd = THREEPARAMOPEN(d->fn,O_RDWR | O_BINARY,0600); } #endif if (d->fd == -1) { switch(errno) { case EFBIG: #ifdef EDQUOT case EDQUOT: #endif case ENOSPC: retval = KRB5_RC_IO_SPACE; goto fail; case EIO: retval = KRB5_RC_IO_IO; goto fail; case EPERM: case EACCES: case EROFS: retval = KRB5_RC_IO_PERM; goto fail; default: retval = KRB5_RC_IO_UNKNOWN; goto fail; } } if ((retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno, sizeof(rc_vno)))) goto unlk; if (ntohs(rc_vno) != KRB5_RC_VNO) { retval = KRB5_RCACHE_BADVNO; unlk: unlink(d->fn); fail: (void) close(d->fd); FREE(d->fn); d->fn = NULL; return retval; } return 0; } krb5_error_code krb5_rc_io_move (context, new, old) krb5_context context; krb5_rc_iostuff *new; krb5_rc_iostuff *old; { #if defined(_MSDOS) || defined(_WIN32) /* * Work around provided by Tom Sanfilippo to work around poor * Windows emulation of POSIX functions. Rename and dup has * different semantics! */ char *fn = NULL; GETDIR; close(new->fd); unlink(new->fn); close(old->fd); if (rename(old->fn,new->fn) == -1) /* MUST be atomic! */ return KRB5_RC_IO_UNKNOWN; if (!(fn = malloc(strlen(new->fn) - dirlen + 1))) return KRB5_RC_IO_MALLOC; strcpy(fn, new->fn + dirlen); krb5_rc_io_close(context, new); krb5_rc_io_open(context, new, fn); free(fn); #else if (rename(old->fn,new->fn) == -1) /* MUST be atomic! */ return KRB5_RC_IO_UNKNOWN; (void) krb5_rc_io_close(context, new); new->fn = malloc(strlen(old->fn)+1); if (new->fn == 0) return ENOMEM; strcpy(new->fn, old->fn); #ifdef macintosh new->fd = fcntl(old->fd, F_DUPFD); #else new->fd = dup(old->fd); #endif #endif return 0; } krb5_error_code krb5_rc_io_write (context, d, buf, num) krb5_context context; krb5_rc_iostuff *d; krb5_pointer buf; int num; { if (write(d->fd,(char *) buf,num) == -1) switch(errno) { case EBADF: return KRB5_RC_IO_UNKNOWN; case EFBIG: return KRB5_RC_IO_SPACE; #ifdef EDQUOT case EDQUOT: return KRB5_RC_IO_SPACE; #endif case ENOSPC: return KRB5_RC_IO_SPACE; case EIO: return KRB5_RC_IO_IO; default: return KRB5_RC_IO_UNKNOWN; } return 0; } krb5_error_code krb5_rc_io_sync (context, d) krb5_context context; krb5_rc_iostuff *d; { #if !defined(MSDOS_FILESYSTEM) && !defined(macintosh) if (fsync(d->fd) == -1) { switch(errno) { case EBADF: return KRB5_RC_IO_UNKNOWN; case EIO: return KRB5_RC_IO_IO; default: return KRB5_RC_IO_UNKNOWN; } } #endif return 0; } krb5_error_code krb5_rc_io_read (context, d, buf, num) krb5_context context; krb5_rc_iostuff *d; krb5_pointer buf; int num; { int count; if ((count = read(d->fd,(char *) buf,num)) == -1) switch(errno) { case EBADF: return KRB5_RC_IO_UNKNOWN; case EIO: return KRB5_RC_IO_IO; default: return KRB5_RC_IO_UNKNOWN; } if (count == 0) return KRB5_RC_IO_EOF; return 0; } krb5_error_code krb5_rc_io_close (context, d) krb5_context context; krb5_rc_iostuff *d; { FREE(d->fn); d->fn = NULL; if (close(d->fd) == -1) /* can't happen */ return KRB5_RC_IO_UNKNOWN; return 0; } krb5_error_code krb5_rc_io_destroy (context, d) krb5_context context; krb5_rc_iostuff *d; { if (unlink(d->fn) == -1) switch(errno) { case EBADF: return KRB5_RC_IO_UNKNOWN; case EIO: return KRB5_RC_IO_IO; case EPERM: return KRB5_RC_IO_PERM; case EBUSY: return KRB5_RC_IO_PERM; case EROFS: return KRB5_RC_IO_PERM; default: return KRB5_RC_IO_UNKNOWN; } return 0; } krb5_error_code krb5_rc_io_mark (context, d) krb5_context context; krb5_rc_iostuff *d; { d->mark = lseek(d->fd,0,SEEK_CUR); /* can't fail */ return 0; } krb5_error_code krb5_rc_io_unmark (context, d) krb5_context context; krb5_rc_iostuff *d; { (void) lseek(d->fd,d->mark,SEEK_SET); /* if it fails, tough luck */ return 0; } long krb5_rc_io_size (context, d) krb5_context context; krb5_rc_iostuff *d; { struct stat statb; if (fstat (d->fd, &statb) == 0) return statb.st_size; else return 0; }