17c478bd9Sstevel@tonic-gate /*
25e01956fSGlenn Barry  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate  */
47c478bd9Sstevel@tonic-gate 
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * lib/krb5/rcache/rc_io.c
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * This file of the Kerberos V5 software is derived from public-domain code
107c478bd9Sstevel@tonic-gate  * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  */
137c478bd9Sstevel@tonic-gate 
14159d09a2SMark Phalan 
157c478bd9Sstevel@tonic-gate /*
167c478bd9Sstevel@tonic-gate  * I/O functions for the replay cache default implementation.
177c478bd9Sstevel@tonic-gate  */
187c478bd9Sstevel@tonic-gate 
19505d05c7Sgtb #if defined(_WIN32)
207c478bd9Sstevel@tonic-gate #  define PATH_SEPARATOR "\\"
217c478bd9Sstevel@tonic-gate #else
227c478bd9Sstevel@tonic-gate #  define PATH_SEPARATOR "/"
237c478bd9Sstevel@tonic-gate #endif
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #define KRB5_RC_VNO	0x0501		/* krb5, rcache v 1 */
267c478bd9Sstevel@tonic-gate 
27159d09a2SMark Phalan #include "k5-int.h"
28159d09a2SMark Phalan #include <stdio.h> /* for P_tmpdir */
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <unistd.h>
317c478bd9Sstevel@tonic-gate #include <syslog.h> /* SUNW */
325e01956fSGlenn Barry #include <locale.h> /* Solaris Kerberos */
337c478bd9Sstevel@tonic-gate #include "rc_base.h"
347c478bd9Sstevel@tonic-gate #include "rc_file.h"
357c478bd9Sstevel@tonic-gate #include "rc_io.h"
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #ifndef O_BINARY
387c478bd9Sstevel@tonic-gate #define O_BINARY    0
397c478bd9Sstevel@tonic-gate #endif
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H
42505d05c7Sgtb #if !defined(_WINSOCKAPI_)
437c478bd9Sstevel@tonic-gate #include <netinet/in.h>
447c478bd9Sstevel@tonic-gate #endif
457c478bd9Sstevel@tonic-gate #else
46505d05c7Sgtb #error find some way to use net-byte-order file version numbers.
477c478bd9Sstevel@tonic-gate #endif
487c478bd9Sstevel@tonic-gate 
49159d09a2SMark Phalan /* Solaris Kerberos */
50159d09a2SMark Phalan #define FREE_RC(x) ((void) free((char *) (x)))
517c478bd9Sstevel@tonic-gate #define UNIQUE getpid() /* hopefully unique number */
527c478bd9Sstevel@tonic-gate 
53505d05c7Sgtb #define GETDIR (dir = getdir(), dirlen = strlen(dir) + sizeof(PATH_SEPARATOR) - 1)
547c478bd9Sstevel@tonic-gate 
55505d05c7Sgtb static char *
getdir(void)567c478bd9Sstevel@tonic-gate getdir(void)
577c478bd9Sstevel@tonic-gate {
58159d09a2SMark Phalan     char *dir;
59505d05c7Sgtb 
60505d05c7Sgtb #if defined(_WIN32)
61159d09a2SMark Phalan 	if (!(dir = getenv("TEMP")))
62159d09a2SMark Phalan 	    if (!(dir = getenv("TMP")))
63159d09a2SMark Phalan 		dir = "C:";
647c478bd9Sstevel@tonic-gate #else
65159d09a2SMark Phalan      /* Solaris Kerberos */
667c478bd9Sstevel@tonic-gate      if (geteuid() == 0)
677c478bd9Sstevel@tonic-gate 	 dir = "/var/krb5/rcache/root";
687c478bd9Sstevel@tonic-gate      else
697c478bd9Sstevel@tonic-gate 	 dir = "/var/krb5/rcache";
707c478bd9Sstevel@tonic-gate #endif
71505d05c7Sgtb      return dir;
727c478bd9Sstevel@tonic-gate }
737c478bd9Sstevel@tonic-gate 
74505d05c7Sgtb krb5_error_code
krb5_rc_io_creat(krb5_context context,krb5_rc_iostuff * d,char ** fn)75505d05c7Sgtb krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn)
767c478bd9Sstevel@tonic-gate {
77159d09a2SMark Phalan     char *c;
78159d09a2SMark Phalan     krb5_int16 rc_vno = htons(KRB5_RC_VNO);
79159d09a2SMark Phalan     krb5_error_code retval = 0;
80159d09a2SMark Phalan     int do_not_unlink = 0;
81159d09a2SMark Phalan     char *dir;
82159d09a2SMark Phalan     size_t dirlen;
83159d09a2SMark Phalan 
84159d09a2SMark Phalan     GETDIR;
85159d09a2SMark Phalan     if (fn && *fn)
86159d09a2SMark Phalan     {
87159d09a2SMark Phalan     /* Solaris Kerberos */
887c478bd9Sstevel@tonic-gate    if (*fn[0] == '/') {
897c478bd9Sstevel@tonic-gate 	d->fn = strdup(*fn);
907c478bd9Sstevel@tonic-gate 	if (d->fn == NULL)
917c478bd9Sstevel@tonic-gate 		return (KRB5_RC_IO_MALLOC);
927c478bd9Sstevel@tonic-gate    } else {
937c478bd9Sstevel@tonic-gate 	if (!(d->fn = malloc(strlen(*fn) + dirlen + 1)))
94159d09a2SMark Phalan 	    return KRB5_RC_IO_MALLOC;
957c478bd9Sstevel@tonic-gate 	(void) strcpy(d->fn, dir);
967c478bd9Sstevel@tonic-gate 	(void) strcat(d->fn, PATH_SEPARATOR);
97505d05c7Sgtb 	(void) strcat(d->fn, *fn);
987c478bd9Sstevel@tonic-gate    }
99159d09a2SMark Phalan     d->fd = THREEPARAMOPEN(d->fn, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL |
100159d09a2SMark Phalan 		       O_BINARY, 0600);
101159d09a2SMark Phalan     }
102159d09a2SMark Phalan     else
1037c478bd9Sstevel@tonic-gate     {
104159d09a2SMark Phalan 	/* %d is max 11 digits (-, 10 digits of 32-bit number)
105159d09a2SMark Phalan 	 * 11 + /krb5_RC + aaa = 24, +6 for slop */
106159d09a2SMark Phalan 	if (!(d->fn = malloc(30 + dirlen)))
107159d09a2SMark Phalan 	    return KRB5_RC_IO_MALLOC;
108159d09a2SMark Phalan 	if (fn)
109159d09a2SMark Phalan 	    if (!(*fn = malloc(35))) {
110159d09a2SMark Phalan 		FREE_RC(d->fn);
111159d09a2SMark Phalan 		return KRB5_RC_IO_MALLOC;
112159d09a2SMark Phalan 	    }
113159d09a2SMark Phalan 	(void) sprintf(d->fn, "%s%skrb5_RC%d", dir, PATH_SEPARATOR,
114159d09a2SMark Phalan 		       (int) UNIQUE);
115159d09a2SMark Phalan 	c = d->fn + strlen(d->fn);
116159d09a2SMark Phalan 	(void) strcpy(c, "aaa");
117159d09a2SMark Phalan 	while ((d->fd = THREEPARAMOPEN(d->fn, O_WRONLY | O_CREAT | O_TRUNC |
118159d09a2SMark Phalan 				       O_EXCL | O_BINARY, 0600)) == -1)
1197c478bd9Sstevel@tonic-gate 	{
120159d09a2SMark Phalan 	    if ((c[2]++) == 'z')
121159d09a2SMark Phalan 	    {
122159d09a2SMark Phalan 		c[2] = 'a';
123159d09a2SMark Phalan 		if ((c[1]++) == 'z')
124159d09a2SMark Phalan 		{
125159d09a2SMark Phalan 		    c[1] = 'a';
126159d09a2SMark Phalan 		    if ((c[0]++) == 'z')
127159d09a2SMark Phalan 			break; /* sigh */
128159d09a2SMark Phalan 		}
129159d09a2SMark Phalan 	    }
130159d09a2SMark Phalan 	}
131159d09a2SMark Phalan 	if (fn)
132159d09a2SMark Phalan 	    (void) strcpy(*fn, d->fn + dirlen);
1337c478bd9Sstevel@tonic-gate     }
134159d09a2SMark Phalan     if (d->fd == -1)
1357c478bd9Sstevel@tonic-gate     {
136159d09a2SMark Phalan 	switch(errno)
137159d09a2SMark Phalan 	{
1387c478bd9Sstevel@tonic-gate 	case EFBIG:
1397c478bd9Sstevel@tonic-gate #ifdef EDQUOT
1407c478bd9Sstevel@tonic-gate 	case EDQUOT:
1417c478bd9Sstevel@tonic-gate #endif
1427c478bd9Sstevel@tonic-gate 	case ENOSPC:
1437c478bd9Sstevel@tonic-gate 	    retval = KRB5_RC_IO_SPACE;
144505d05c7Sgtb 	    goto cleanup;
145159d09a2SMark Phalan 
1467c478bd9Sstevel@tonic-gate 	case EIO:
147505d05c7Sgtb 	    retval = KRB5_RC_IO_IO;
148505d05c7Sgtb 	    goto cleanup;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	case EPERM:
1517c478bd9Sstevel@tonic-gate 	case EACCES:
1527c478bd9Sstevel@tonic-gate 	case EROFS:
1537c478bd9Sstevel@tonic-gate 	case EEXIST:
154505d05c7Sgtb 	    retval = KRB5_RC_IO_PERM;
155159d09a2SMark Phalan 	    krb5_set_error_message(context, retval,
1565e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
1575e01956fSGlenn Barry 					"Cannot create replay cache %s: %s"),
1585e01956fSGlenn Barry 				d->fn ? d->fn : "<null>",
1595e01956fSGlenn Barry 				strerror(errno));
160505d05c7Sgtb 	    do_not_unlink = 1;
161505d05c7Sgtb 	    goto cleanup;
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	default:
164505d05c7Sgtb 	    retval = KRB5_RC_IO_UNKNOWN;
165159d09a2SMark Phalan 	    krb5_set_error_message(context, retval,
1665e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
1675e01956fSGlenn Barry 					"Cannot create replay cache %s: %s"),
1685e01956fSGlenn Barry 				d->fn ? d->fn : "<null>",
1695e01956fSGlenn Barry 				strerror(errno));
170505d05c7Sgtb 	    goto cleanup;
171159d09a2SMark Phalan 	}
1727c478bd9Sstevel@tonic-gate     }
173505d05c7Sgtb     retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno,
174505d05c7Sgtb 			      sizeof(rc_vno));
175505d05c7Sgtb     if (retval)
176505d05c7Sgtb 	goto cleanup;
177505d05c7Sgtb 
178505d05c7Sgtb     retval = krb5_rc_io_sync(context, d);
179505d05c7Sgtb 
180505d05c7Sgtb  cleanup:
181505d05c7Sgtb     if (retval) {
182505d05c7Sgtb 	if (d->fn) {
183505d05c7Sgtb 	    if (!do_not_unlink)
184505d05c7Sgtb 		(void) unlink(d->fn);
185159d09a2SMark Phalan 	    FREE_RC(d->fn);
186505d05c7Sgtb 	    d->fn = NULL;
187505d05c7Sgtb 	}
188159d09a2SMark Phalan 	if (d->fd != -1) {
189159d09a2SMark Phalan 	  (void) close(d->fd);
190159d09a2SMark Phalan 	}
1917c478bd9Sstevel@tonic-gate     }
192505d05c7Sgtb     return retval;
1937c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate 
195505d05c7Sgtb static krb5_error_code
krb5_rc_io_open_internal(krb5_context context,krb5_rc_iostuff * d,char * fn,char * full_pathname)196505d05c7Sgtb krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn,
197159d09a2SMark Phalan 			 char* full_pathname)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate     krb5_int16 rc_vno;
2007c478bd9Sstevel@tonic-gate     krb5_error_code retval = 0;
2017c478bd9Sstevel@tonic-gate     int do_not_unlink = 1;
2027c478bd9Sstevel@tonic-gate     struct stat lstatb, fstatb;
2037c478bd9Sstevel@tonic-gate     int use_errno = 0;
204505d05c7Sgtb     char *dir;
205505d05c7Sgtb     size_t dirlen;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate     GETDIR;
2087c478bd9Sstevel@tonic-gate     if (fn[0] == '/') {
2097c478bd9Sstevel@tonic-gate 	d->fn = strdup(fn);
2107c478bd9Sstevel@tonic-gate 	if (d->fn == NULL)
2117c478bd9Sstevel@tonic-gate 		return (KRB5_RC_IO_MALLOC);
2127c478bd9Sstevel@tonic-gate     } else {
2137c478bd9Sstevel@tonic-gate 	if (!(d->fn = malloc(strlen(fn) + dirlen + 1)))
214159d09a2SMark Phalan 	    return KRB5_RC_IO_MALLOC;
215505d05c7Sgtb 	(void) strcpy(d->fn, dir);
216505d05c7Sgtb 	(void) strcat(d->fn, PATH_SEPARATOR);
217505d05c7Sgtb 	(void) strcat(d->fn, fn);
2187c478bd9Sstevel@tonic-gate     }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate     /* Solaris: BEGIN made changes to be safer and better code structure */
2217c478bd9Sstevel@tonic-gate     if ((d->fd = THREEPARAMOPEN(d->fn, O_RDWR|O_BINARY, 0600)) == -1) {
2227c478bd9Sstevel@tonic-gate 	use_errno = 1;
2237c478bd9Sstevel@tonic-gate 	goto cleanup;
2247c478bd9Sstevel@tonic-gate     }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate     do_not_unlink = 0;
2277c478bd9Sstevel@tonic-gate     if (fstat(d->fd, &fstatb) == 0) {
2287c478bd9Sstevel@tonic-gate #ifndef NO_USERID
2297c478bd9Sstevel@tonic-gate 	uid_t me;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	me = geteuid();
2327c478bd9Sstevel@tonic-gate 	/* must be owned by this user, to prevent some security problems with
2337c478bd9Sstevel@tonic-gate 	 * other users modifying replay cache stuff and must be a regular file
2347c478bd9Sstevel@tonic-gate 	 */
2357c478bd9Sstevel@tonic-gate 	if ((fstatb.st_uid != me) || ((fstatb.st_mode & S_IFMT) != S_IFREG)) {
2367c478bd9Sstevel@tonic-gate 	    retval = KRB5_RC_IO_PERM;
2377c478bd9Sstevel@tonic-gate 	    goto cleanup;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate #else
2407c478bd9Sstevel@tonic-gate 	/* make sure the rcache is a regular file */
2417c478bd9Sstevel@tonic-gate 	if (((fstatb.st_mode & S_IFMT) != S_IFREG)) {
2427c478bd9Sstevel@tonic-gate 	    retval = KRB5_RC_IO_PERM;
243159d09a2SMark Phalan 
2447c478bd9Sstevel@tonic-gate 	    goto cleanup;
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate #endif
2477c478bd9Sstevel@tonic-gate 	if (lstat(d->fn, &lstatb) == 0) {
2487c478bd9Sstevel@tonic-gate 	    /* Make sure fstat() and lstat() have accessed the same file */
249*55fea89dSDan Cross 	    if ((lstatb.st_ino != fstatb.st_ino) ||
2507c478bd9Sstevel@tonic-gate 		    (lstatb.st_dev != fstatb.st_dev)) {
2517c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_PERM;
2527c478bd9Sstevel@tonic-gate 		goto cleanup;
2537c478bd9Sstevel@tonic-gate 	    }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	    if ((lstatb.st_mode & S_IFMT) == S_IFLNK) {
2567c478bd9Sstevel@tonic-gate 		/* if we accessed the rcache via a symlink, bail out */
2577c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Error, krb replay cache %s is a symlink "
2587c478bd9Sstevel@tonic-gate 			   "and should be removed.\n", d->fn);
2597c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_PERM;
2607c478bd9Sstevel@tonic-gate 		goto cleanup;
2617c478bd9Sstevel@tonic-gate 	    }
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 	else {
2647c478bd9Sstevel@tonic-gate 	    use_errno = 1;
2657c478bd9Sstevel@tonic-gate 	    goto cleanup;
2667c478bd9Sstevel@tonic-gate 	}
2677c478bd9Sstevel@tonic-gate     }
2687c478bd9Sstevel@tonic-gate     else {
2697c478bd9Sstevel@tonic-gate 	use_errno = 1;
2707c478bd9Sstevel@tonic-gate 	goto cleanup;
2717c478bd9Sstevel@tonic-gate     }
2727c478bd9Sstevel@tonic-gate 
273505d05c7Sgtb     do_not_unlink = 0;
2747c478bd9Sstevel@tonic-gate     retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno,
275159d09a2SMark Phalan 			     sizeof(rc_vno));
2767c478bd9Sstevel@tonic-gate     if (retval)
2777c478bd9Sstevel@tonic-gate 	goto cleanup;
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate     if (ntohs(rc_vno) != KRB5_RC_VNO)
2807c478bd9Sstevel@tonic-gate 	retval = KRB5_RCACHE_BADVNO;
2817c478bd9Sstevel@tonic-gate 
282159d09a2SMark Phalan  cleanup:
2837c478bd9Sstevel@tonic-gate     if (use_errno) {
2847c478bd9Sstevel@tonic-gate 	switch(errno)
2857c478bd9Sstevel@tonic-gate 	{
2867c478bd9Sstevel@tonic-gate 	    case EFBIG:
2877c478bd9Sstevel@tonic-gate #ifdef EDQUOT
2887c478bd9Sstevel@tonic-gate 	    case EDQUOT:
2897c478bd9Sstevel@tonic-gate #endif
2907c478bd9Sstevel@tonic-gate 	    case ENOSPC:
2917c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_SPACE;
2927c478bd9Sstevel@tonic-gate 		break;
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	    case EIO:
2957c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_IO;
2967c478bd9Sstevel@tonic-gate 		break;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	    case EPERM:
2997c478bd9Sstevel@tonic-gate 	    case EACCES:
3007c478bd9Sstevel@tonic-gate 	    case EROFS:
3017c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_PERM;
302159d09a2SMark Phalan 	    	krb5_set_error_message (context, retval,
3035e01956fSGlenn Barry 			    dgettext(TEXT_DOMAIN,
3045e01956fSGlenn Barry 				"Cannot open replay cache %s: %s"),
305159d09a2SMark Phalan 			    d->fn, strerror(errno));
3067c478bd9Sstevel@tonic-gate 		break;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	    default:
3097c478bd9Sstevel@tonic-gate 		retval = KRB5_RC_IO_UNKNOWN;
310159d09a2SMark Phalan 		krb5_set_error_message (context, retval,
3115e01956fSGlenn Barry 			    dgettext(TEXT_DOMAIN,
3125e01956fSGlenn Barry 				"Cannot open replay cache %s: %s"),
313159d09a2SMark Phalan 			    d->fn, strerror(errno));
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate     }
3167c478bd9Sstevel@tonic-gate     /* Solaris: END made changes to be safer and better code structure */
3177c478bd9Sstevel@tonic-gate     if (retval) {
3187c478bd9Sstevel@tonic-gate 	if (d->fn) {
319159d09a2SMark Phalan 	    if (!do_not_unlink)
3207c478bd9Sstevel@tonic-gate 		(void) unlink(d->fn);
321159d09a2SMark Phalan 	    FREE_RC(d->fn);
3227c478bd9Sstevel@tonic-gate 	    d->fn = NULL;
3237c478bd9Sstevel@tonic-gate 	}
324*55fea89dSDan Cross 	if (d->fd >= 0)
325159d09a2SMark Phalan 	     (void) close(d->fd);
3267c478bd9Sstevel@tonic-gate     }
3277c478bd9Sstevel@tonic-gate     return retval;
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate 
330505d05c7Sgtb krb5_error_code
krb5_rc_io_open(krb5_context context,krb5_rc_iostuff * d,char * fn)331505d05c7Sgtb krb5_rc_io_open(krb5_context context, krb5_rc_iostuff *d, char *fn)
332505d05c7Sgtb {
333505d05c7Sgtb     return krb5_rc_io_open_internal(context, d, fn, NULL);
334505d05c7Sgtb }
335505d05c7Sgtb 
3367c478bd9Sstevel@tonic-gate krb5_error_code
krb5_rc_io_move(krb5_context context,krb5_rc_iostuff * new1,krb5_rc_iostuff * old)3377c478bd9Sstevel@tonic-gate krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1,
3387c478bd9Sstevel@tonic-gate 		krb5_rc_iostuff *old)
3397c478bd9Sstevel@tonic-gate {
340159d09a2SMark Phalan #if defined(_WIN32) || defined(__CYGWIN__)
341505d05c7Sgtb     char *new_fn = NULL;
342505d05c7Sgtb     char *old_fn = NULL;
343505d05c7Sgtb     off_t offset = 0;
344505d05c7Sgtb     krb5_error_code retval = 0;
3457c478bd9Sstevel@tonic-gate     /*
346505d05c7Sgtb      * Initial work around provided by Tom Sanfilippo to work around
347505d05c7Sgtb      * poor Windows emulation of POSIX functions.  Rename and dup has
3487c478bd9Sstevel@tonic-gate      * different semantics!
349505d05c7Sgtb      *
350505d05c7Sgtb      * Additional fixes and explanation provided by dalmeida@mit.edu:
351505d05c7Sgtb      *
352505d05c7Sgtb      * First, we save the offset of "old".  Then, we close and remove
353505d05c7Sgtb      * the "new" file so we can do the rename.  We also close "old" to
354505d05c7Sgtb      * make sure the rename succeeds (though that might not be
355505d05c7Sgtb      * necessary on some systems).
356505d05c7Sgtb      *
357505d05c7Sgtb      * Next, we do the rename.  If all goes well, we seek the "new"
358505d05c7Sgtb      * file to the position "old" was at.
359505d05c7Sgtb      *
360505d05c7Sgtb      * --- WARNING!!! ---
361505d05c7Sgtb      *
362505d05c7Sgtb      * Since "old" is now gone, we mourn its disappearance, but we
363505d05c7Sgtb      * cannot emulate that Unix behavior...  THIS BEHAVIOR IS
364505d05c7Sgtb      * DIFFERENT FROM UNIX.  However, it is ok because this function
365505d05c7Sgtb      * gets called such that "old" gets closed right afterwards.
3667c478bd9Sstevel@tonic-gate      */
367505d05c7Sgtb     offset = lseek(old->fd, 0, SEEK_CUR);
368505d05c7Sgtb 
369505d05c7Sgtb     new_fn = new1->fn;
370505d05c7Sgtb     new1->fn = NULL;
371505d05c7Sgtb     close(new1->fd);
372505d05c7Sgtb     new1->fd = -1;
373505d05c7Sgtb 
374505d05c7Sgtb     unlink(new_fn);
375505d05c7Sgtb 
376505d05c7Sgtb     old_fn = old->fn;
377505d05c7Sgtb     old->fn = NULL;
3787c478bd9Sstevel@tonic-gate     close(old->fd);
379505d05c7Sgtb     old->fd = -1;
380505d05c7Sgtb 
381505d05c7Sgtb     if (rename(old_fn, new_fn) == -1) { /* MUST be atomic! */
382505d05c7Sgtb 	retval = KRB5_RC_IO_UNKNOWN;
383505d05c7Sgtb 	goto cleanup;
384505d05c7Sgtb     }
385505d05c7Sgtb 
386505d05c7Sgtb     retval = krb5_rc_io_open_internal(context, new1, 0, new_fn);
387505d05c7Sgtb     if (retval)
388505d05c7Sgtb 	goto cleanup;
389505d05c7Sgtb 
390505d05c7Sgtb     if (lseek(new1->fd, offset, SEEK_SET) == -1) {
391505d05c7Sgtb 	retval = KRB5_RC_IO_UNKNOWN;
392505d05c7Sgtb 	goto cleanup;
393505d05c7Sgtb     }
394505d05c7Sgtb 
395505d05c7Sgtb  cleanup:
396505d05c7Sgtb     free(new_fn);
397505d05c7Sgtb     free(old_fn);
398505d05c7Sgtb     return retval;
3997c478bd9Sstevel@tonic-gate #else
400505d05c7Sgtb     char *fn = NULL;
4017c478bd9Sstevel@tonic-gate     if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */
4027c478bd9Sstevel@tonic-gate 	return KRB5_RC_IO_UNKNOWN;
4037c478bd9Sstevel@tonic-gate     fn = new1->fn;
4047c478bd9Sstevel@tonic-gate     new1->fn = NULL;		/* avoid clobbering */
4057c478bd9Sstevel@tonic-gate     (void) krb5_rc_io_close(context, new1);
4067c478bd9Sstevel@tonic-gate     new1->fn = fn;
4077c478bd9Sstevel@tonic-gate     new1->fd = dup(old->fd);
4087c478bd9Sstevel@tonic-gate     return 0;
409505d05c7Sgtb #endif
4107c478bd9Sstevel@tonic-gate }
4117c478bd9Sstevel@tonic-gate 
412505d05c7Sgtb krb5_error_code
krb5_rc_io_write(krb5_context context,krb5_rc_iostuff * d,krb5_pointer buf,unsigned int num)413505d05c7Sgtb krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
414505d05c7Sgtb 		 unsigned int num)
4157c478bd9Sstevel@tonic-gate {
416505d05c7Sgtb     if (write(d->fd, (char *) buf, num) == -1)
417505d05c7Sgtb 	switch(errno)
418505d05c7Sgtb 	{
4197c478bd9Sstevel@tonic-gate #ifdef EDQUOT
420159d09a2SMark Phalan 	case EDQUOT:
4217c478bd9Sstevel@tonic-gate #endif
422159d09a2SMark Phalan 	case EFBIG:
423159d09a2SMark Phalan 	case ENOSPC:
424159d09a2SMark Phalan 	    krb5_set_error_message (context, KRB5_RC_IO_SPACE,
4255e01956fSGlenn Barry 				    dgettext(TEXT_DOMAIN,
4265e01956fSGlenn Barry 					    "Can't write to replay cache %s: %s"),
4275e01956fSGlenn Barry 				    d->fn, strerror(errno));
428159d09a2SMark Phalan 	    return KRB5_RC_IO_SPACE;
429159d09a2SMark Phalan 	case EIO:
430159d09a2SMark Phalan 	    krb5_set_error_message (context, KRB5_RC_IO_IO,
4315e01956fSGlenn Barry 				    dgettext(TEXT_DOMAIN,
4325e01956fSGlenn Barry 					    "Can't write to replay cache %s: %s"),
4335e01956fSGlenn Barry 				    d->fn, strerror(errno));
434159d09a2SMark Phalan 	    return KRB5_RC_IO_IO;
435159d09a2SMark Phalan 	case EBADF:
436159d09a2SMark Phalan 	default:
437159d09a2SMark Phalan 	    krb5_set_error_message (context, KRB5_RC_IO_UNKNOWN,
4385e01956fSGlenn Barry 				    dgettext(TEXT_DOMAIN,
4395e01956fSGlenn Barry 					    "Can't write to replay cache %s: %s"),
4405e01956fSGlenn Barry 				    d->fn, strerror(errno));
441159d09a2SMark Phalan 	    return KRB5_RC_IO_UNKNOWN;
442505d05c7Sgtb 	}
443505d05c7Sgtb     return 0;
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate 
446505d05c7Sgtb krb5_error_code
krb5_rc_io_sync(krb5_context context,krb5_rc_iostuff * d)447505d05c7Sgtb krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d)
4487c478bd9Sstevel@tonic-gate {
449505d05c7Sgtb #if defined(_WIN32)
450505d05c7Sgtb #ifndef fsync
451505d05c7Sgtb #define fsync _commit
452505d05c7Sgtb #endif
453505d05c7Sgtb #endif
4547c478bd9Sstevel@tonic-gate     if (fsync(d->fd) == -1) {
455505d05c7Sgtb 	switch(errno)
456505d05c7Sgtb 	{
457505d05c7Sgtb 	case EBADF: return KRB5_RC_IO_UNKNOWN;
458505d05c7Sgtb 	case EIO: return KRB5_RC_IO_IO;
459159d09a2SMark Phalan 	default:
460159d09a2SMark Phalan 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
4615e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
4625e01956fSGlenn Barry 					"Cannot sync replay cache file %s: %s"),
4635e01956fSGlenn Barry 				d->fn, strerror(errno));
464159d09a2SMark Phalan 	    return KRB5_RC_IO_UNKNOWN;
465505d05c7Sgtb 	}
4667c478bd9Sstevel@tonic-gate     }
4677c478bd9Sstevel@tonic-gate     return 0;
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate /*ARGSUSED*/
471505d05c7Sgtb krb5_error_code
krb5_rc_io_read(krb5_context context,krb5_rc_iostuff * d,krb5_pointer buf,unsigned int num)472505d05c7Sgtb krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
473505d05c7Sgtb 		unsigned int num)
4747c478bd9Sstevel@tonic-gate {
475505d05c7Sgtb     int count;
476505d05c7Sgtb     if ((count = read(d->fd, (char *) buf, num)) == -1)
477505d05c7Sgtb 	switch(errno)
478505d05c7Sgtb 	{
479505d05c7Sgtb 	case EIO: return KRB5_RC_IO_IO;
480159d09a2SMark Phalan 	case EBADF:
481159d09a2SMark Phalan 	default:
482159d09a2SMark Phalan 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
4835e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
4845e01956fSGlenn Barry 					"Can't read from replay cache %s: %s"),
4855e01956fSGlenn Barry 				d->fn, strerror(errno));
486159d09a2SMark Phalan 	    return KRB5_RC_IO_UNKNOWN;
487505d05c7Sgtb 	}
488505d05c7Sgtb     if (count == 0)
489505d05c7Sgtb 	return KRB5_RC_IO_EOF;
490505d05c7Sgtb     return 0;
4917c478bd9Sstevel@tonic-gate }
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate /*ARGSUSED*/
494505d05c7Sgtb krb5_error_code
krb5_rc_io_close(krb5_context context,krb5_rc_iostuff * d)495505d05c7Sgtb krb5_rc_io_close(krb5_context context, krb5_rc_iostuff *d)
4967c478bd9Sstevel@tonic-gate {
497505d05c7Sgtb     if (d->fn != NULL) {
498159d09a2SMark Phalan 	FREE_RC(d->fn);
499505d05c7Sgtb 	d->fn = NULL;
500505d05c7Sgtb     }
501505d05c7Sgtb     if (d->fd != -1) {
502505d05c7Sgtb 	if (close(d->fd) == -1) /* can't happen */
503505d05c7Sgtb 	    return KRB5_RC_IO_UNKNOWN;
504505d05c7Sgtb 	d->fd = -1;
505505d05c7Sgtb     }
506505d05c7Sgtb     return 0;
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
510505d05c7Sgtb krb5_error_code
krb5_rc_io_destroy(krb5_context context,krb5_rc_iostuff * d)511505d05c7Sgtb krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d)
5127c478bd9Sstevel@tonic-gate {
513159d09a2SMark Phalan     if (unlink(d->fn) == -1)
514159d09a2SMark Phalan 	switch(errno)
515159d09a2SMark Phalan 	{
516159d09a2SMark Phalan 	case EIO:
517159d09a2SMark Phalan 	    krb5_set_error_message(context, KRB5_RC_IO_IO,
5185e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
5195e01956fSGlenn Barry 					"Can't destroy replay cache %s: %s"),
5205e01956fSGlenn Barry 				d->fn, strerror(errno));
521159d09a2SMark Phalan 	    return KRB5_RC_IO_IO;
522159d09a2SMark Phalan 	case EPERM:
523159d09a2SMark Phalan 	case EBUSY:
524159d09a2SMark Phalan 	case EROFS:
525159d09a2SMark Phalan 	    krb5_set_error_message(context, KRB5_RC_IO_PERM,
5265e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
5275e01956fSGlenn Barry 					"Can't destroy replay cache %s: %s"),
5285e01956fSGlenn Barry 				d->fn, strerror(errno));
529159d09a2SMark Phalan 	    return KRB5_RC_IO_PERM;
530159d09a2SMark Phalan 	case EBADF:
531159d09a2SMark Phalan 	default:
532159d09a2SMark Phalan 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
5335e01956fSGlenn Barry 				dgettext(TEXT_DOMAIN,
5345e01956fSGlenn Barry 					"Can't destroy replay cache %s: %s"),
5355e01956fSGlenn Barry 				d->fn, strerror(errno));
536159d09a2SMark Phalan 	    return KRB5_RC_IO_UNKNOWN;
537159d09a2SMark Phalan 	}
538159d09a2SMark Phalan     return 0;
5397c478bd9Sstevel@tonic-gate }
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate /*ARGSUSED*/
542505d05c7Sgtb krb5_error_code
krb5_rc_io_mark(krb5_context context,krb5_rc_iostuff * d)543505d05c7Sgtb krb5_rc_io_mark(krb5_context context, krb5_rc_iostuff *d)
5447c478bd9Sstevel@tonic-gate {
545505d05c7Sgtb     d->mark = lseek(d->fd, (off_t) 0, SEEK_CUR); /* can't fail */
546505d05c7Sgtb     return 0;
5477c478bd9Sstevel@tonic-gate }
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate /*ARGSUSED*/
550505d05c7Sgtb krb5_error_code
krb5_rc_io_unmark(krb5_context context,krb5_rc_iostuff * d)551505d05c7Sgtb krb5_rc_io_unmark(krb5_context context, krb5_rc_iostuff *d)
5527c478bd9Sstevel@tonic-gate {
553505d05c7Sgtb     (void) lseek(d->fd, d->mark, SEEK_SET); /* if it fails, tough luck */
554505d05c7Sgtb     return 0;
5557c478bd9Sstevel@tonic-gate }
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5587c478bd9Sstevel@tonic-gate long
krb5_rc_io_size(krb5_context context,krb5_rc_iostuff * d)559505d05c7Sgtb krb5_rc_io_size(krb5_context context, krb5_rc_iostuff *d)
5607c478bd9Sstevel@tonic-gate {
5617c478bd9Sstevel@tonic-gate     struct stat statb;
5627c478bd9Sstevel@tonic-gate 
563505d05c7Sgtb     if (fstat(d->fd, &statb) == 0)
5647c478bd9Sstevel@tonic-gate 	return statb.st_size;
5657c478bd9Sstevel@tonic-gate     else
5667c478bd9Sstevel@tonic-gate 	return 0;
5677c478bd9Sstevel@tonic-gate }
568