* $Source$
* $Author$
*
+ * Copyright (c) Hewlett-Packard Company 1991
+ * Released to the Massachusetts Institute of Technology for inclusion
+ * in the Kerberos source code distribution.
+ *
* Copyright 1990,1991 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* The format is as follows:
*
* <file format vno>
- * principal vno key
- * principal vno key
+ * <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_int16 in each case. The specific format, then, is
* multiple entries concatinated with no separators. An entry has this
* then, each component listed in ordser.
* For each component, sizeof(krb5_int16) bytes for the number of bytes
* in the component, followed by the component.
+ * sizeof(krb5_int32) for the principal type (for KEYTAB V2 and higher)
+ * sizeof(krb5_timestamp) bytes for the timestamp
* sizeof(krb5_kvno) bytes for the key version number
* sizeof(krb5_keytype) bytes for the keytype
* sizeof(krb5_int32) bytes for the key length, followed by the key
*
- * Extra garbage at the end of a keytab will be not be searched for, but
- *
- *
*/
#if !defined(lint) && !defined(SABER)
#include <krb5/ext-proto.h>
#include <krb5/libos.h>
#include <krb5/los-proto.h>
+#include <krb5/osconf.h>
+#include <netinet/in.h>
+#include <stdio.h>
#include "ktfile.h"
-#include <krb5/osconf.h>
-/* keytab version 1 didn't do byte swapping correctly; call this version 2
- so old files will be recognized as old instead of badly formatted. */
-#define KRB5_KT_VNO 0x0502 /* krb5, keytab v 2 */
+#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
extern int errno;
krb5_keytab id;
int mode;
{
- register FILE *fp;
krb5_error_code kerror;
+ krb5_kt_vno kt_vno;
int writevno = 0;
-#ifdef POSIX_TYPES
- mode_t omask;
-#else
- int omask;
-#endif
-
- /* Make sure nobody else can read the new file. It might be better
- to use open with mode 600 followed by fdopen on UNIX systems. */
- omask = umask(066);
-#ifdef ANSI_STDIO
- fp = fopen(KTFILENAME(id),
- (mode == KRB5_LOCKMODE_EXCLUSIVE) ? "rb+" : "rb");
-#else
- fp = fopen(KTFILENAME(id),
- (mode == KRB5_LOCKMODE_EXCLUSIVE) ? "r+" : "r");
-#endif
- if (!fp) {
+ 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 */
-#ifdef ANSI_STDIO
- fp = fopen(KTFILENAME(id), "ab+");
-#else
- fp = fopen(KTFILENAME(id), "a+");
-#endif
- if (!fp) {
- (void) umask (omask);
+ krb5_create_secure_file(KTFILENAME(id));
+ KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
+ if (!KTFILEP(id))
return errno;
- }
writevno = 1;
- } else { /* some other error */
- (void) umask (omask);
+ } else /* some other error */
return errno;
- }
}
- (void) umask (omask);
-
- if (kerror = krb5_lock_file(fp, KTFILENAME(id), mode)) {
- (void) fclose(fp);
+ if (kerror = krb5_lock_file(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) {
- /* Write a version number, MSB first. */
- if (putc((KRB5_KT_VNO >> 8), fp) == EOF || putc(KRB5_KT_VNO, fp) == EOF) {
- (void) krb5_unlock_file(fp, KTFILENAME(id));
- (void) fclose(fp);
- return KRB5_KT_IOERR;
+ 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(KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ return kerror;
}
} else {
- int c1, c2;
-
- /* Verify version number. */
- c1 = getc(fp);
- c2 = getc(fp);
-
- if (c1 == EOF || c2 == EOF) {
- kerror = feof(fp) ? KRB5_KT_END : KRB5_KT_IOERR;
- (void) krb5_unlock_file(fp, KTFILENAME(id));
- (void) fclose(fp);
+ /* gotta verify it instead... */
+ if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
+ kerror = errno;
+ (void) krb5_unlock_file(KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
return kerror;
}
- if ((c1 << 8) + c2 != KRB5_KT_VNO) {
- (void) krb5_unlock_file(fp, KTFILENAME(id));
- (void) fclose(fp);
- return KRB5_KEYTAB_BADVNO;
- }
- }
- /* seek to the end for writers */
- if (mode == KRB5_LOCKMODE_EXCLUSIVE) {
- if (fseek(fp, 0, 2)) {
- (void) krb5_unlock_file(fp, KTFILENAME(id));
- (void) fclose(fp);
- return KRB5_KT_IOERR;
+ kt_vno = KTVERSION(id) = ntohs(kt_vno);
+ if ((kt_vno != KRB5_KT_VNO) &&
+ (kt_vno != KRB5_KT_VNO_1)) {
+ (void) krb5_unlock_file(KTFILEP(id), KTFILENAME(id));
+ (void) fclose(KTFILEP(id));
+ return -1 /* KRB5_KEYTAB_BADVNO */;
}
}
- KTFILEP(id) = fp;
return 0;
}
return kerror;
}
-/* Keytab file format. This is not documented anywhere and is not known
- outside this file.
-
- Each entry in the file contains:
+krb5_error_code
+krb5_ktfileint_delete_entry(id, delete_point)
+krb5_keytab id;
+krb5_int32 delete_point;
+{
+ krb5_int32 size;
+ krb5_int32 len;
+ char iobuf[BUFSIZ];
- prinicipal name:
- 2 byte count of number of components in name
- component:
- 2 byte count of number of bytes
- data
- 1 byte key version number
- 2 byte key type
- 4 byte key length
- key data
+ if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
+ return errno;
+ }
+ if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
+ return KRB5_KT_END;
+ }
+ if (size > 0) {
+ size = -size;
+ if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
+ return errno;
+ }
+ if (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
+ return KRB5_KT_IOERR;
+ }
+
+ size = -size;
+ if (size < BUFSIZ) {
+ len = size;
+ } else {
+ len = BUFSIZ;
+ }
+
+ memset(iobuf, 0, len);
+ while (size > 0) {
+ xfwrite(iobuf, 1, len, KTFILEP(id));
+ size -= len;
+ if (size < len) {
+ len = size;
+ }
+ }
+
+ return krb5_sync_disk_file(KTFILEP(id));
+ }
- The write file function could do range checking on 2 byte quantities,
- but doesn't. Values greater than 2 ^ 15 are unlikely.
-*/
+ return 0;
+}
krb5_error_code
-krb5_ktfileint_read_entry(id, entryp)
+krb5_ktfileint_internal_read_entry(id, entrypp, delete_point)
krb5_keytab id;
-krb5_keytab_entry **entryp;
+krb5_keytab_entry **entrypp;
+krb5_int32 *delete_point;
{
- krb5_keytab_entry *entry;
- register FILE *fp = KTFILEP(id);
- int i; /* index into principal component array; failure cleanup
- code uses this to determine how much to free */
- int count;
- int size;
- int c1, c2;
- krb5_error_code error;
-
- entry = (krb5_keytab_entry *)malloc (sizeof (krb5_keytab_entry));
- if (entry == 0)
- return ENOMEM;
-
- /* Read a character at a time to avoid any problems with byte order. */
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- return KRB5_KT_END;
-
- count = (c1 << 8) + c2;
-
- if (!(entry->principal = (krb5_principal)malloc(sizeof(krb5_principal_data))))
- return ENOMEM;
- if (!(entry->principal->data = (krb5_data *)malloc(count * sizeof(krb5_data))))
+ register krb5_keytab_entry *ret_entry;
+ krb5_int16 count;
+ krb5_int16 princ_size;
+ register int i;
+ krb5_int32 size;
+ krb5_int32 start_pos;
+ krb5_error_code error;
+ char *tmpdata;
+ krb5_data *princ;
+
+ if (!(ret_entry = (krb5_keytab_entry *)calloc(1, sizeof(*ret_entry))))
+ return ENOMEM;
+
+ /* fseek to synchronise buffered I/O on the key table. */
+
+ if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
{
- free((char *)entry->principal);
- return ENOMEM;
+ return errno;
}
- entry->principal->length = count;
-
- {
- char *tmpdata;
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- {
- error = KRB5_KT_END;
- goto fail;
- }
- size = (c1 << 8) + c2;
- krb5_princ_set_realm_length(entry->principal, size);
- if ((tmpdata = malloc (size)) == 0)
- {
- error = ENOMEM;
- goto fail;
- }
- if (fread(tmpdata, 1, size, fp) != size)
- {
- free (tmpdata);
- error = KRB5_KT_END;
- goto fail;
- }
- krb5_princ_set_realm_data(entry->principal, tmpdata);
- }
+ 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;
+ }
- for (i = 0; i < count; i++)
- {
- krb5_data *princ = krb5_princ_component(entry->principal, i);
-
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- {
- error = KRB5_KT_END;
- goto fail;
- }
+ start_pos = ftell(KTFILEP(id));
- size = (c1 << 8) + c2;
+ /* deal with guts of parsing... */
- princ->length = size;
- if ((princ->data = malloc (size)) == 0)
- {
- error = ENOMEM;
- goto fail;
- }
- if (fread(princ->data, 1, size, fp) != size)
- {
- free (princ->data);
- error = KRB5_KT_END;
- goto fail;
- }
+ /* 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->length = count;
+ ret_entry->principal->data = (krb5_data *)calloc(count, sizeof(krb5_data));
+ if (!ret_entry->principal->data) {
+ free(ret_entry->principal);
+ return ENOMEM;
}
- /* key version number: 1 byte */
- c1 = getc(fp);
- if (c1 == EOF)
- {
- error = KRB5_KT_END;
- goto fail;
- }
- entry->vno = c1;
- /* keyblock: keytype (2), length (4), contents */
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- {
- error = KRB5_KT_END;
- goto fail;
+ /* Now, get the realm data */
+ if (!xfread(&princ_size, sizeof(princ_size), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
}
- entry->key.keytype = (c1 << 8) | c2;
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- {
- 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;
}
- size = (c1 << 24) + (c2 << 16);
- c1 = getc(fp);
- c2 = getc(fp);
- if (c1 == EOF || c2 == EOF)
- {
- error = KRB5_KT_END;
- goto fail;
+ krb5_princ_set_realm_length(ret_entry->principal, princ_size);
+ tmpdata = malloc(princ_size+1);
+ if (!tmpdata) {
+ error = ENOMEM;
+ goto fail;
}
- size += (c1 << 8) + c2;
-
- entry->key.length = size;
- if ((entry->key.contents = (krb5_octet *)malloc(size)) == 0)
- {
- error = ENOMEM;
- goto fail;
+ if (fread(tmpdata, 1, princ_size, KTFILEP(id)) != 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(ret_entry->principal, tmpdata);
+
+ for (i = 0; i < count; i++) {
+ princ = krb5_princ_component(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 */
}
- if (fread((char *)entry->key.contents, 1, size, fp) != size)
- {
- free(entry->key.contents);
- error = KRB5_KT_END;
- goto fail;
+ /* 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;
}
- *entryp = entry;
- return 0;
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ ret_entry->timestamp = ntohl(ret_entry->timestamp);
+
+ /* read in the version number */
+ if (!xfread(&ret_entry->vno, sizeof(ret_entry->vno), 1, KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+
+ /* key type */
+ if (!xfread(&ret_entry->key.keytype, sizeof(ret_entry->key.keytype), 1,
+ KTFILEP(id))) {
+ error = KRB5_KT_END;
+ goto fail;
+ }
+ if (KTVERSION(id) != KRB5_KT_VNO_1)
+ ret_entry->key.keytype = ntohs(ret_entry->key.keytype);
+
+ /* key contents */
+ 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;
+ }
+
+ *entrypp = ret_entry;
- fail:
- free((char *)entry->principal->data);
- free((char *)entry->principal);
- return error;
+ /*
+ * 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(ret_entry->principal, i);
+ if (princ->data)
+ free(princ->data);
+ }
+ free(ret_entry->principal->data);
+ free(ret_entry->principal);
+ return error;
}
krb5_error_code
-krb5_ktfileint_write_entry(id, entry)
+krb5_ktfileint_read_entry(id, entrypp)
krb5_keytab id;
-register krb5_keytab_entry *entry;
+krb5_keytab_entry **entrypp;
{
- register FILE *fp = KTFILEP(id);
- int count, size;
- unsigned char c1, c2;
- register int i;
-
- /* Do all I/O and check for error once at the end. This function isn't
- expensive, and errors should be rare. */
-
- count = krb5_princ_size(entry->principal);
-
- /* 2 byte count of number of components in name, MSB first. */
+ krb5_int32 delete_point;
- c2 = count;
- c1 = count >> 8;
-
- putc(c1, fp);
- putc(c2, fp);
+ return krb5_ktfileint_internal_read_entry(id, entrypp, &delete_point);
+}
+krb5_error_code
+krb5_ktfileint_write_entry(id, entry)
+krb5_keytab id;
+krb5_keytab_entry *entry;
+{
+ 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(entry, &size_needed);
+ if (retval)
+ return retval;
+ retval = krb5_ktfileint_find_slot(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)
{
- size = krb5_princ_realm(entry->principal)->length;
-
- c2 = size;
- c1 = size >> 8;
+ return errno;
+ }
- putc(c1, fp);
- putc(c2, fp);
+ if (KTVERSION(id) == KRB5_KT_VNO_1) {
+ count = entry->principal->length + 1;
+ } else {
+ count = htons(entry->principal->length);
+ }
+
+ if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) {
+ abend:
+ setbuf(KTFILEP(id), 0);
+ return KRB5_KT_IOERR;
+ }
+ size = krb5_princ_realm(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(entry->principal)->data, sizeof(char),
+ krb5_princ_realm(entry->principal)->length, KTFILEP(id))) {
+ goto abend;
+ }
- fwrite(krb5_princ_realm(entry->principal)->data, 1, size, fp);
+ count = entry->principal->length;
+ for (i = 0; i < count; i++) {
+ princ = krb5_princ_component(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;
+ }
}
- for (i = 0; i < count; i++)
- {
- size = krb5_princ_component(entry->principal, i)->length;
+ /*
+ * Write out the principal type
+ */
+ if (KTVERSION(id) != KRB5_KT_VNO_1) {
+ princ_type = htonl(krb5_princ_type(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(&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 */
+ if (!xfwrite(&entry->vno, sizeof(entry->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(KTFILEP(id));
+ (void) memset(iobuf, 0, sizeof(iobuf));
+ setbuf(KTFILEP(id), 0);
+
+ if (retval) {
+ return retval;
+ }
- c2 = size;
- c1 = size >> 8;
+ 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(KTFILEP(id));
- putc(c1, fp);
- putc(c2, fp);
+ return retval;
+}
- fwrite(krb5_princ_component(entry->principal, i)->data, 1, size, fp);
+/*
+ * Determine the size needed for a file entry for the given
+ * keytab entry.
+ */
+krb5_error_code
+krb5_ktfileint_size_entry(entry, size_needed)
+krb5_keytab_entry *entry;
+krb5_int32 *size_needed;
+{
+ krb5_int16 count, size;
+ krb5_int32 total_size, i;
+ krb5_error_code retval = 0;
+
+ count = entry->principal->length;
+
+ total_size = sizeof(count);
+ total_size += krb5_princ_realm(entry->principal)->length + (sizeof(size));
+
+ for (i = 0; i < count; i++) {
+ total_size += krb5_princ_component(entry->principal,i)->length
+ + (sizeof(size));
}
- /* Version number is one byte. */
- putc(entry->vno, fp);
- /* Key type is 2 bytes. */
- c2 = entry->key.keytype;
- c1 = entry->key.keytype >> 8;
+ total_size += sizeof(entry->principal->type);
+ total_size += sizeof(entry->timestamp);
+ total_size += sizeof(entry->vno);
+ total_size += sizeof(entry->key.keytype);
+ total_size += sizeof(size) + entry->key.length;
- putc(c1, fp);
- putc(c2, fp);
-
- size = entry->key.length;
+ *size_needed = total_size;
+ return retval;
+}
- c1 = size >> 24;
- c2 = size >> 16;
- putc(c1, fp);
- putc(c2, fp);
- c1 = size >> 8;
- c2 = size;
- putc(c1, fp);
- putc(c2, fp);
+/*
+ * 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.
+ *
+ * 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(id, size_needed, commit_point)
+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];
+
+ /*
+ * 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;
+ }
- fwrite((char *)entry->key.contents, 1, size, fp);
+ 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;
+
+ /* 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))) {
+ abend:
+ 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);
+ xfwrite(iobuf, 1, 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;
+ }
+ }
+ }
+ }
- if (fflush(fp) == EOF || ferror(fp))
- return KRB5_KT_IOERR;
- return 0;
+ return 0;
}
+