/* * lib/krb5/ccache/stdio/scc_maybe.c * * Copyright 1990,1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Copyright 1995 by Cygnus Support. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * This file contains the source code for conditional open/close calls. */ #include "scc.h" #include "k5-int.h" #ifdef macintosh /* * Kludge for the Macintosh, since fopen doesn't set errno, but open * does... */ static FILE *my_fopen(char *path, char *mode) { int fd, open_flags; FILE *f; f = fopen(path, mode); if (f) return f; /* * OK, fopen failed; let's try to figure out why.... */ if (strchr(mode, '+')) open_flags = O_RDWR; else if (strchr(mode, 'w') || strchr(mode, 'a')) open_flags = O_WRONLY; else open_flags = O_RDONLY; if (strchr(mode, 'a')) open_flags |= O_APPEND; fd = open(path, open_flags); if (fd == -1) return NULL; /* * fopen failed, but open succeeded? W*E*I*R*D..... */ close(fd); errno = KRB5_CC_IO; return NULL; } #endif krb5_error_code krb5_scc_close_file (context, id) krb5_context context; krb5_ccache id; { krb5_scc_data *data; int ret; krb5_error_code retval; data = (krb5_scc_data *) id->data; if (data->file == (FILE *) NULL) return KRB5_FCC_INTERNAL; #ifdef ultrix errno = 0; #endif ret = fflush (data->file); #ifdef ultrix /* their LIBC refuses to allow an fflush() of a read-only buffer! We patch around it by only calling it an error if errno is set by a (failed) syscall */ if (ret == EOF && !errno) ret = 0; #endif memset (data->stdio_buffer, 0, sizeof (data->stdio_buffer)); if (ret == EOF) { int errsave = errno; (void) krb5_unlock_file(context, fileno(data->file)); (void) fclose (data->file); data->file = 0; return krb5_scc_interpret (context, errsave); } retval = krb5_unlock_file(context, fileno(data->file)); ret = fclose (data->file); data->file = 0; if (retval) return retval; else return ret ? krb5_scc_interpret (context, errno) : 0; } krb5_error_code krb5_scc_open_file (context, id, mode) krb5_context context; krb5_ccache id; int mode; { krb5_os_context os_ctx = (krb5_os_context) context->os_context; krb5_scc_data *data = (krb5_scc_data *) id->data; char fvno_bytes[2]; /* In nework byte order */ krb5_ui_2 scc_tag; krb5_ui_2 scc_taglen; krb5_ui_2 scc_hlen; FILE *f; char *open_flag; krb5_error_code retval = 0; if (data->file) { /* Don't know what state it's in; shut down and start anew. */ (void) krb5_unlock_file(context, fileno(data->file)); (void) fclose (data->file); data->file = 0; } #ifdef ANSI_STDIO switch(mode) { case SCC_OPEN_AND_ERASE: unlink(data->filename); /* XXX should do an exclusive open here, but no way to do */ /* this under stdio */ open_flag = "wb+"; break; case SCC_OPEN_RDWR: open_flag = "rb+"; break; case SCC_OPEN_RDONLY: default: open_flag = "rb"; break; } #else switch(mode) { case SCC_OPEN_AND_ERASE: unlink(data->filename); /* XXX should do an exclusive open here, but no way to do */ /* this under stdio */ open_flag = "w+"; break; case SCC_OPEN_RDWR: open_flag = "r+"; break; case SCC_OPEN_RDONLY: default: open_flag = "r"; break; } #endif #ifdef macintosh f = my_fopen (data->filename, open_flag); #else f = fopen (data->filename, open_flag); #endif if (!f) return krb5_scc_interpret (context, errno); #ifdef HAVE_SETVBUF setvbuf(f, data->stdio_buffer, _IOFBF, sizeof (data->stdio_buffer)); #else setbuf (f, data->stdio_buffer); #endif switch (mode) { case SCC_OPEN_RDONLY: if ((retval = krb5_lock_file(context,fileno(f),KRB5_LOCKMODE_SHARED))){ (void) fclose(f); return retval; } break; case SCC_OPEN_RDWR: case SCC_OPEN_AND_ERASE: if ((retval = krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_EXCLUSIVE))) { (void) fclose(f); return retval; } break; } if (mode == SCC_OPEN_AND_ERASE) { /* write the version number */ data->file = f; data->version = context->scc_default_format; retval = krb5_scc_store_ui_2(context, id, data->version); if (retval) goto done; if (data->version == KRB5_SCC_FVNO_4) { scc_hlen = 0; if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) scc_hlen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32)); /* Write header length */ retval = krb5_scc_store_ui_2(context, id, (krb5_int32)scc_hlen); if (retval) goto done; if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) { /* Write time offset tag */ scc_tag = SCC_TAG_DELTATIME; scc_taglen = 2*sizeof(krb5_int32); retval = krb5_scc_store_ui_2(context,id,(krb5_int32)scc_tag); if (retval) goto done; retval = krb5_scc_store_ui_2(context,id,(krb5_int32)scc_taglen); if (retval) goto done; retval = krb5_scc_store_int32(context,id,os_ctx->time_offset); if (retval) goto done; retval = krb5_scc_store_int32(context,id,os_ctx->usec_offset); if (retval) goto done; } } goto done; } /* verify a valid version number is there */ if (!fread((char *)fvno_bytes, sizeof(fvno_bytes), 1, f)) { retval = KRB5_CC_FORMAT; goto done; } data->version = (fvno_bytes[0] << 8) + fvno_bytes[1]; if ((data->version != KRB5_SCC_FVNO_1) && (data->version != KRB5_SCC_FVNO_2) && (data->version != KRB5_SCC_FVNO_3) && (data->version != KRB5_SCC_FVNO_4)) { retval = KRB5_CCACHE_BADVNO; goto done; } data->file = f; if (data->version == KRB5_SCC_FVNO_4) { char buf[1024]; if (krb5_scc_read_ui_2(context, id, &scc_hlen) || (scc_hlen > sizeof(buf))) { retval = KRB5_CC_FORMAT; goto done; } while (scc_hlen) { if ((scc_hlen < (2*sizeof(krb5_ui_2))) || krb5_scc_read_ui_2(context, id, &scc_tag) || krb5_scc_read_ui_2(context, id, &scc_taglen) || (scc_taglen > (scc_hlen - 2*sizeof(krb5_ui_2)))) { retval = KRB5_CC_FORMAT; goto done; } switch (scc_tag) { case SCC_TAG_DELTATIME: if (scc_taglen != 2*sizeof(krb5_int32)) { retval = KRB5_CC_FORMAT; goto done; } if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) || (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) { if (krb5_scc_read(context, id, buf, scc_taglen)) { retval = KRB5_CC_FORMAT; goto done; } break; } if (krb5_scc_read_int32(context, id, &os_ctx->time_offset) || krb5_scc_read_int32(context, id, &os_ctx->usec_offset)) { retval = KRB5_CC_FORMAT; goto done; } os_ctx->os_flags = ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) | KRB5_OS_TOFFSET_VALID); break; default: if (scc_taglen && krb5_scc_read(context,id,buf,scc_taglen)) { retval = KRB5_CC_FORMAT; goto done; } break; } scc_hlen -= (2*sizeof(krb5_ui_2) + scc_taglen); } } done: if (retval) if (f) { data->file = 0; (void) krb5_unlock_file(context, fileno(f)); (void) fclose(f); } return retval; }