/*
- * $Source$
- * $Author$
+ * lib/krb5/keytab/file/ktf_util.c
*
- * Copyright 1990 by the Massachusetts Institute of Technology.
+ * Copyright (c) Hewlett-Packard Company 1991
+ * Released to the Massachusetts Institute of Technology for inclusion
+ * in the Kerberos source code distribution.
*
- * For copying and distribution information, please see the file
- * <krb5/mit-copyright.h>.
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * 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 function contains utilities for the file based implementation of
* the keytab. There are no public functions in this file.
*
* The format is as follows:
*
- * principal vno key
- * principal vno key
+ * <file format vno>
+ * <record length>
+ * principal timestamp vno key
+ * <record length>
+ * principal timestamp vno key
* ....
*
- * There are no separators between fields of an entry or between entries.
+ * A length field (sizeof(krb5_int32)) exists between entries. When this
+ * length is positive it indicates an active entry, when negative a hole.
+ * The length indicates the size of the block in the file (this may be
+ * larger than the size of the next record, since we are using a first
+ * fit algorithm for re-using holes and the first fit may be larger than
+ * the entry we are writing). Another (compatible) implementation could
+ * break up holes when allocating them to smaller entries to minimize
+ * wasted space. (Such an implementation should also coalesce adjacent
+ * holes to reduce fragmentation). This implementation does neither.
+ *
+ * There are no separators between fields of an entry.
* A principal is a length-encoded array of length-encoded strings. The
- * length is a krb5_length XXX in each case. The specific format, then, is
+ * length is a krb5_int16 in each case. The specific format, then, is
* multiple entries concatinated with no separators. An entry has this
* exact format:
*
- * sizeof(krb5_length) bytes for number of components in the principal;
+ * sizeof(krb5_int16) bytes for number of components in the principal;
* then, each component listed in ordser.
- * For each component, sizeof(krb5_length) bytes for the number of bytes
+ * For each component, sizeof(krb5_int16) bytes for the number of bytes
* in the component, followed by the component.
- * sizeof(krb5_kvno) bytes for the key version number
- * sizeof(krb5_key_block) bytes for the key
+ * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher)
+ * sizeof(krb5_int32) bytes for the timestamp
+ * sizeof(krb5_octet) bytes for the key version number
+ * sizeof(krb5_int16) bytes for the keytype
+ * sizeof(krb5_int32) bytes for the key length, followed by the key
+ */
+
+#define NEED_SOCKETS
+#include "krb5.h"
+#include "k5-int.h"
+#include <stdio.h>
+
+#include "ktfile.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#endif
+
+typedef krb5_int16 krb5_kt_vno;
+
+krb5_kt_vno krb5_kt_default_vno = KRB5_KT_DEFAULT_VNO;
+
+#define xfwrite(a, b, c, d) fwrite((char *)a, b, c, d)
+#define xfread(a, b, c, d) fread((char *)a, b, c, d)
+
+#ifdef ANSI_STDIO
+static char *fopen_mode_rbplus= "rb+";
+static char *fopen_mode_rb = "rb";
+#else
+static char *fopen_mode_rbplus= "r+";
+static char *fopen_mode_rb = "r";
+#endif
+
+#ifndef HAVE_ERRNO
+extern int errno;
+#endif
+
+static krb5_error_code
+krb5_ktfileint_open(context, id, mode)
+ krb5_context context;
+krb5_keytab id;
+int mode;
+{
+ krb5_error_code kerror;
+ krb5_kt_vno kt_vno;
+ int writevno = 0;
+
+ KTFILEP(id) = fopen(KTFILENAME(id),
+ (mode == KRB5_LOCKMODE_EXCLUSIVE) ?
+ fopen_mode_rbplus : fopen_mode_rb);
+ if (!KTFILEP(id)) {
+ if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) {
+ /* try making it first time around */
+ krb5_create_secure_file(context, KTFILENAME(id));
+ KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
+ if (!KTFILEP(id))
+ return errno;
+ writevno = 1;
+ } else /* some other error */
+ return errno;
+ }
+ if (kerror = krb5_lock_file(context, KTFILEP(id), KTFILENAME(id),
+ mode)) {
+ (void) fclose(KTFILEP(id));
+ KTFILEP(id) = 0;
+ return kerror;
+ }
+ /* assume ANSI or BSD-style stdio */
+ setbuf(KTFILEP(id), NULL);
+
+ /* get the vno and verify it */
+ if (writevno) {
+ kt_vno = htons(krb5_kt_default_vno);
+ KTVERSION(id) = krb5_kt_default_vno;
+ if (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
+ kerror = errno;
+ (void) krb5_unlock_file(context, KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ return kerror;
+ }
+ } else {
+ /* gotta verify it instead... */
+ if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
+ kerror = errno;
+ (void) krb5_unlock_file(context, KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ return kerror;
+ }
+ kt_vno = KTVERSION(id) = ntohs(kt_vno);
+ if ((kt_vno != KRB5_KT_VNO) &&
+ (kt_vno != KRB5_KT_VNO_1)) {
+ (void) krb5_unlock_file(context, KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ return -1 /* KRB5_KEYTAB_BADVNO */;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code
+krb5_ktfileint_openr(context, id)
+ krb5_context context;
+krb5_keytab id;
+{
+ return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED);
+}
+
+krb5_error_code
+krb5_ktfileint_openw(context, id)
+ krb5_context context;
+krb5_keytab id;
+{
+ return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE);
+}
+
+krb5_error_code
+krb5_ktfileint_close(context, id)
+ krb5_context context;
+krb5_keytab id;
+{
+ krb5_error_code kerror;
+
+ if (!KTFILEP(id))
+ return 0;
+ kerror = krb5_unlock_file(context, KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ KTFILEP(id) = 0;
+ return kerror;
+}
+
+krb5_error_code
+krb5_ktfileint_delete_entry(context, id, delete_point)
+ krb5_context context;
+krb5_keytab id;
+krb5_int32 delete_point;
+{
+ krb5_int32 size;
+ krb5_int32 len;
+ char iobuf[BUFSIZ];
+
+ if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
+ return errno;
+ }
+ if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
+ return KRB5_KT_END;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = ntohl(size);
+
+ if (size > 0) {
+ krb5_int32 minus_size = -size;
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ minus_size = htonl(minus_size);
+
+ if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
+ return errno;
+ }
+
+ if (!xfwrite(&minus_size, sizeof(minus_size), 1, KTFILEP(id))) {
+ return KRB5_KT_IOERR;
+ }
+
+ if (size < BUFSIZ) {
+ len = size;
+ } else {
+ len = BUFSIZ;
+ }
+
+ memset(iobuf, 0, (size_t) len);
+ while (size > 0) {
+ xfwrite(iobuf, 1, (size_t) len, KTFILEP(id));
+ size -= len;
+ if (size < len) {
+ len = size;
+ }
+ }
+
+ return krb5_sync_disk_file(context, KTFILEP(id));
+ }
+
+ return 0;
+}
+
+krb5_error_code
+krb5_ktfileint_internal_read_entry(context, id, ret_entry, delete_point)
+ krb5_context context;
+krb5_keytab id;
+krb5_keytab_entry *ret_entry;
+krb5_int32 *delete_point;
+{
+ krb5_octet vno;
+ krb5_int16 count;
+ krb5_int16 keytype;
+ krb5_int16 princ_size;
+ register int i;
+ krb5_int32 size;
+ krb5_int32 start_pos;
+ krb5_error_code error;
+ char *tmpdata;
+ krb5_data *princ;
+
+ memset(ret_entry, 0, sizeof(krb5_keytab_entry));
+ ret_entry->magic = KV5M_KEYTAB_ENTRY;
+
+ /* fseek to synchronise buffered I/O on the key table. */
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+ do {
+ *delete_point = ftell(KTFILEP(id));
+ if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
+ return KRB5_KT_END;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = ntohl(size);
+
+ if (size < 0) {
+ if (fseek(KTFILEP(id), -size, SEEK_CUR)) {
+ return errno;
+ }
+ }
+ } while (size < 0);
+
+ if (size == 0) {
+ return KRB5_KT_END;
+ }
+
+ start_pos = ftell(KTFILEP(id));
+
+ /* deal with guts of parsing... */
+
+ /* first, int16 with #princ components */
+ if (!xfread(&count, sizeof(count), 1, KTFILEP(id)))
+ return KRB5_KT_END;
+ if (KTVERSION(id) == KRB5_KT_VNO_1) {
+ count -= 1; /* V1 includes the realm in the count */
+ } else {
+ count = ntohs(count);
+ }
+ if (!count || (count < 0))
+ return KRB5_KT_END;
+ ret_entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
+ if (!ret_entry->principal)
+ return ENOMEM;
+
+ ret_entry->principal->magic = KV5M_PRINCIPAL;
+ ret_entry->principal->length = count;
+ ret_entry->principal->data = (krb5_data *)calloc(count, sizeof(krb5_data));
+ if (!ret_entry->principal->data) {
+ free(ret_entry->principal);
+ ret_entry->principal = 0;
+ return ENOMEM;
+ }
+
+ /* Now, get the realm data */
+ if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ princ_size = ntohs(princ_size);
+ if (!princ_size || (princ_size < 0)) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ krb5_princ_set_realm_length(context, ret_entry->principal, princ_size);
+ tmpdata = malloc(princ_size+1);
+ if (!tmpdata) {
+ error = ENOMEM;
+ goto fail;
+ }
+ if (fread(tmpdata, 1, princ_size, KTFILEP(id)) != (size_t) princ_size) {
+ free(tmpdata);
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ tmpdata[princ_size] = 0; /* Some things might be expecting null */
+ /* termination... ``Be conservative in */
+ /* what you send out'' */
+ krb5_princ_set_realm_data(context, ret_entry->principal, tmpdata);
+
+ for (i = 0; i < count; i++) {
+ princ = krb5_princ_component(context, ret_entry->principal, i);
+ if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ princ_size = ntohs(princ_size);
+ if (!princ_size || (princ_size < 0)) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+
+ princ->length = princ_size;
+ princ->data = malloc(princ_size+1);
+ if (!princ->data) {
+ error = ENOMEM;
+ goto fail;
+ }
+ if (!xfread(princ->data, sizeof(char), princ_size, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ princ->data[princ_size] = 0; /* Null terminate */
+ }
+
+ /* read in the principal type, if we can get it */
+ if (KTVERSION(id) != KRB5_KT_VNO_1) {
+ if (!xfread(&ret_entry->principal->type,
+ sizeof(ret_entry->principal->type), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ ret_entry->principal->type = ntohl(ret_entry->principal->type);
+ }
+
+ /* read in the timestamp */
+ if (!xfread(&ret_entry->timestamp, sizeof(ret_entry->timestamp), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ ret_entry->timestamp = ntohl(ret_entry->timestamp);
+
+ /* read in the version number */
+ if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ ret_entry->vno = (krb5_kvno)vno;
+
+ /* key type */
+ if (!xfread(&keytype, sizeof(keytype), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ ret_entry->key.keytype = (krb5_keytype)keytype;
+
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ ret_entry->key.keytype = ntohs(ret_entry->key.keytype);
+
+ /* key contents */
+ ret_entry->key.magic = KV5M_KEYBLOCK;
+ ret_entry->key.etype = ETYPE_UNKNOWN;
+
+ if (!xfread(&count, sizeof(count), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ count = ntohs(count);
+ if (!count || (count < 0)) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ ret_entry->key.length = count;
+
+ ret_entry->key.contents = (krb5_octet *)malloc(count);
+ if (!ret_entry->key.contents) {
+ error = ENOMEM;
+ goto fail;
+ }
+ if (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count,
+ KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+
+ /*
+ * Reposition file pointer to the next inter-record length field.
+ */
+ fseek(KTFILEP(id), start_pos + size, SEEK_SET);
+ return 0;
+fail:
+
+ for (i = 0; i < ret_entry->principal->length; i++) {
+ princ = krb5_princ_component(context, ret_entry->principal, i);
+ if (princ->data)
+ free(princ->data);
+ }
+ free(ret_entry->principal->data);
+ ret_entry->principal->data = 0;
+ free(ret_entry->principal);
+ ret_entry->principal = 0;
+ return error;
+}
+
+krb5_error_code
+krb5_ktfileint_read_entry(context, id, entryp)
+ krb5_context context;
+krb5_keytab id;
+krb5_keytab_entry *entryp;
+{
+ krb5_int32 delete_point;
+
+ return krb5_ktfileint_internal_read_entry(context, id, entryp, &delete_point);
+}
+
+krb5_error_code
+krb5_ktfileint_write_entry(context, id, entry)
+ krb5_context context;
+krb5_keytab id;
+krb5_keytab_entry *entry;
+{
+ krb5_octet vno;
+ krb5_data *princ;
+ krb5_int16 count, size, keytype;
+ krb5_error_code retval = 0;
+ krb5_timestamp timestamp;
+ krb5_int32 princ_type;
+ krb5_int32 size_needed;
+ krb5_int32 commit_point;
+ int i;
+ char iobuf[BUFSIZ];
+
+ retval = krb5_ktfileint_size_entry(context, entry, &size_needed);
+ if (retval)
+ return retval;
+ retval = krb5_ktfileint_find_slot(context, id, &size_needed, &commit_point);
+ if (retval)
+ return retval;
+
+ setbuf(KTFILEP(id), iobuf);
+
+ /* fseek to synchronise buffered I/O on the key table. */
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+ if (KTVERSION(id) == KRB5_KT_VNO_1) {
+ count = (krb5_int16) entry->principal->length + 1;
+ } else {
+ count = htons((u_short) entry->principal->length);
+ }
+
+ if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) {
+ abend:
+ setbuf(KTFILEP(id), 0);
+ return KRB5_KT_IOERR;
+ }
+ size = krb5_princ_realm(context, entry->principal)->length;
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = htons(size);
+ if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ if (!xfwrite(krb5_princ_realm(context, entry->principal)->data, sizeof(char),
+ krb5_princ_realm(context, entry->principal)->length, KTFILEP(id))) {
+ goto abend;
+ }
+
+ count = (krb5_int16) entry->principal->length;
+ for (i = 0; i < count; i++) {
+ princ = krb5_princ_component(context, entry->principal, i);
+ size = princ->length;
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = htons(size);
+ if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ if (!xfwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) {
+ goto abend;
+ }
+ }
+
+ /*
+ * Write out the principal type
+ */
+ if (KTVERSION(id) != KRB5_KT_VNO_1) {
+ princ_type = htonl(krb5_princ_type(context, entry->principal));
+ if (!xfwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ }
+
+ /*
+ * Fill in the time of day the entry was written to the keytab.
+ */
+ if (krb5_timeofday(context, &entry->timestamp)) {
+ entry->timestamp = 0;
+ }
+ if (KTVERSION(id) == KRB5_KT_VNO_1)
+ timestamp = entry->timestamp;
+ else
+ timestamp = htonl(entry->timestamp);
+ if (!xfwrite(×tamp, sizeof(timestamp), 1, KTFILEP(id))) {
+ goto abend;
+ }
+
+ /* key version number */
+ vno = (krb5_octet)entry->vno;
+ if (!xfwrite(&vno, sizeof(vno), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ /* key type */
+ if (KTVERSION(id) == KRB5_KT_VNO_1)
+ keytype = entry->key.keytype;
+ else
+ keytype = htons(entry->key.keytype);
+ if (!xfwrite(&keytype, sizeof(keytype), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ /* key length */
+ if (KTVERSION(id) == KRB5_KT_VNO_1)
+ size = entry->key.length;
+ else
+ size = htons(entry->key.length);
+ if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ if (!xfwrite(entry->key.contents, sizeof(krb5_octet),
+ entry->key.length, KTFILEP(id))) {
+ memset(iobuf, 0, sizeof(iobuf));
+ setbuf(KTFILEP(id), 0);
+ return KRB5_KT_IOERR;
+ }
+
+ retval = krb5_sync_disk_file(context, KTFILEP(id));
+ (void) memset(iobuf, 0, sizeof(iobuf));
+ setbuf(KTFILEP(id), 0);
+
+ if (retval) {
+ return retval;
+ }
+
+ if (fseek(KTFILEP(id), commit_point, SEEK_SET)) {
+ return errno;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size_needed = htonl(size_needed);
+ if (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) {
+ goto abend;
+ }
+ retval = krb5_sync_disk_file(context, KTFILEP(id));
+
+ return retval;
+}
+
+/*
+ * Determine the size needed for a file entry for the given
+ * keytab entry.
+ */
+krb5_error_code
+krb5_ktfileint_size_entry(context, entry, size_needed)
+ krb5_context context;
+krb5_keytab_entry *entry;
+krb5_int32 *size_needed;
+{
+ krb5_int16 count;
+ krb5_int32 total_size, i;
+ krb5_error_code retval = 0;
+
+ count = (krb5_int16) entry->principal->length;
+
+ total_size = sizeof(count);
+ total_size += krb5_princ_realm(context, entry->principal)->length + (sizeof(krb5_int16));
+
+ for (i = 0; i < count; i++) {
+ total_size += krb5_princ_component(context, entry->principal,i)->length
+ + (sizeof(krb5_int16));
+ }
+
+ total_size += sizeof(entry->principal->type);
+ total_size += sizeof(entry->timestamp);
+ total_size += sizeof(krb5_octet);
+ total_size += sizeof(krb5_int16);
+ total_size += sizeof(krb5_int16) + entry->key.length;
+
+ *size_needed = total_size;
+ return retval;
+}
+
+/*
+ * Find and reserve a slot in the file for an entry of the needed size.
+ * The commit point will be set to the position in the file where the
+ * the length (sizeof(krb5_int32) bytes) of this node should be written
+ * when commiting the write. The file position left as a result of this
+ * call is the position where the actual data should be written.
*
- * Extra garbage at the end of a keytab will be not be searched for, but
- *
- *
+ * The size_needed argument may be adjusted if we find a hole that is
+ * larger than the size needed. (Recall that size_needed will be used
+ * to commit the write, but that this field must indicate the size of the
+ * block in the file rather than the size of the actual entry)
*/
+krb5_error_code
+krb5_ktfileint_find_slot(context, id, size_needed, commit_point)
+ krb5_context context;
+krb5_keytab id;
+krb5_int32 *size_needed;
+krb5_int32 *commit_point;
+{
+ krb5_int32 size;
+ krb5_int32 remainder;
+ krb5_int32 zero_point;
+ krb5_kt_vno kt_vno;
+ krb5_boolean found = FALSE;
+ char iobuf[BUFSIZ];
-#if !defined(lint) && !defined(SABER)
-static char rcsid_ktf_util_c[] =
-"$Id$";
-#endif /* !lint & !SABER */
+ /*
+ * Skip over file version number
+ */
+ if (fseek(KTFILEP(id), 0, SEEK_SET)) {
+ return errno;
+ }
+ if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
+ return KRB5_KT_IOERR;
+ }
-#include <krb5/copyright.h>
-#include <krb5/krb5.h>
+ while (!found) {
+ *commit_point = ftell(KTFILEP(id));
+ if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
+ /*
+ * Hit the end of file, reserve this slot.
+ */
+ setbuf(KTFILEP(id), 0);
+ size = 0;
-#include "ktfile.h"
+ /* fseek to synchronise buffered I/O on the key table. */
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+#ifdef notdef
+ /* We don't have to do this because htonl(0) == 0 */
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = htonl(size);
+#endif
+
+ if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
+ return KRB5_KT_IOERR;
+ }
+ found = TRUE;
+ }
+
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ size = ntohl(size);
+
+ if (size > 0) {
+ if (fseek(KTFILEP(id), size, SEEK_CUR)) {
+ return errno;
+ }
+ } else if (!found) {
+ size = -size;
+ if (size >= *size_needed) {
+ *size_needed = size;
+ found = TRUE;
+ } else if (size > 0) {
+ /*
+ * The current hole is not large enough, so skip it
+ */
+ if (fseek(KTFILEP(id), size, SEEK_CUR)) {
+ return errno;
+ }
+ } else {
+
+ /* fseek to synchronise buffered I/O on the key table. */
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+ /*
+ * Found the end of the file (marked by a 0 length buffer)
+ * Make sure we zero any trailing data.
+ */
+ zero_point = ftell(KTFILEP(id));
+ setbuf(KTFILEP(id), iobuf);
+ while (size = xfread(iobuf, 1, sizeof(iobuf), KTFILEP(id))) {
+ if (size != sizeof(iobuf)) {
+ remainder = size % sizeof(krb5_int32);
+ if (remainder) {
+ size += sizeof(krb5_int32) - remainder;
+ }
+ }
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+ memset(iobuf, 0, (size_t) size);
+ xfwrite(iobuf, 1, (size_t) size, KTFILEP(id));
+ if (feof(KTFILEP(id))) {
+ break;
+ }
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
+ {
+ return errno;
+ }
+
+ }
+ setbuf(KTFILEP(id), 0);
+ if (fseek(KTFILEP(id), zero_point, SEEK_SET)) {
+ return errno;
+ }
+ }
+ }
+ }
+
+ return 0;
+}