From: Theodore Tso Date: Tue, 29 Sep 1992 13:54:43 +0000 (+0000) Subject: Folded in HP's changes to the keytab file format, plus our changes to X-Git-Tag: krb5-1.0-beta2~39 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=95626ac2e0592fdefdd21f44976b3cf46a79e7ab;p=krb5.git Folded in HP's changes to the keytab file format, plus our changes to handle the principal type storage. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2423 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/krb5/keytab/file/ktf_util.c b/src/lib/krb5/keytab/file/ktf_util.c index b8907bfa1..b97a3d105 100644 --- a/src/lib/krb5/keytab/file/ktf_util.c +++ b/src/lib/krb5/keytab/file/ktf_util.c @@ -2,6 +2,10 @@ * $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. * @@ -31,11 +35,23 @@ * The format is as follows: * * - * principal vno key - * principal vno key + * + * principal timestamp vno key + * + * 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 @@ -45,13 +61,12 @@ * 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) @@ -63,13 +78,31 @@ static char rcsid_ktf_util_c[] = #include #include #include +#include +#include +#include #include "ktfile.h" -#include -/* 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; @@ -78,87 +111,59 @@ krb5_ktfileint_open(id, mode) 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; } @@ -190,241 +195,564 @@ krb5_keytab id; 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; } +