#ifndef LEAN_CLIENT
#include "k5-int.h"
#include "../os/os-proto.h"
#include <stdio.h>
#define KRB5_KT_VNO_1 0x0501
#define KRB5_KT_VNO 0x0502
#define KRB5_KT_DEFAULT_VNO KRB5_KT_VNO
typedef struct _krb5_ktfile_data {
char *name;
FILE *openf;
char iobuf[BUFSIZ];
int version;
unsigned int iter_count;
long start_offset;
k5_mutex_t lock;
} krb5_ktfile_data;
#define KTPRIVATE(id) ((krb5_ktfile_data *)(id)->data)
#define KTFILENAME(id) (((krb5_ktfile_data *)(id)->data)->name)
#define KTFILEP(id) (((krb5_ktfile_data *)(id)->data)->openf)
#define KTFILEBUFP(id) (((krb5_ktfile_data *)(id)->data)->iobuf)
#define KTVERSION(id) (((krb5_ktfile_data *)(id)->data)->version)
#define KTITERS(id) (((krb5_ktfile_data *)(id)->data)->iter_count)
#define KTSTARTOFF(id) (((krb5_ktfile_data *)(id)->data)->start_offset)
#define KTLOCK(id) k5_mutex_lock(&((krb5_ktfile_data *)(id)->data)->lock)
#define KTUNLOCK(id) k5_mutex_unlock(&((krb5_ktfile_data *)(id)->data)->lock)
#define KTCHECKLOCK(id) k5_mutex_assert_locked(&((krb5_ktfile_data *)(id)->data)->lock)
extern const struct _krb5_kt_ops krb5_ktf_ops;
extern const struct _krb5_kt_ops krb5_ktf_writable_ops;
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_resolve(krb5_context, const char *, krb5_keytab *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_name(krb5_context, krb5_keytab, char *, unsigned int);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_close(krb5_context, krb5_keytab);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_entry(krb5_context, krb5_keytab, krb5_const_principal,
krb5_kvno, krb5_enctype, krb5_keytab_entry *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_start_seq_get(krb5_context, krb5_keytab, krb5_kt_cursor *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_next(krb5_context, krb5_keytab, krb5_keytab_entry *,
krb5_kt_cursor *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_end_get(krb5_context, krb5_keytab, krb5_kt_cursor *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_add(krb5_context, krb5_keytab, krb5_keytab_entry *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_remove(krb5_context, krb5_keytab, krb5_keytab_entry *);
static krb5_error_code
krb5_ktfileint_openr(krb5_context, krb5_keytab);
static krb5_error_code
krb5_ktfileint_openw(krb5_context, krb5_keytab);
static krb5_error_code
krb5_ktfileint_close(krb5_context, krb5_keytab);
static krb5_error_code
krb5_ktfileint_read_entry(krb5_context, krb5_keytab, krb5_keytab_entry *);
static krb5_error_code
krb5_ktfileint_write_entry(krb5_context, krb5_keytab, krb5_keytab_entry *);
static krb5_error_code
krb5_ktfileint_delete_entry(krb5_context, krb5_keytab, krb5_int32);
static krb5_error_code
krb5_ktfileint_internal_read_entry(krb5_context, krb5_keytab,
krb5_keytab_entry *, krb5_int32 *);
static krb5_error_code
krb5_ktfileint_size_entry(krb5_context, krb5_keytab_entry *, krb5_int32 *);
static krb5_error_code
krb5_ktfileint_find_slot(krb5_context, krb5_keytab, krb5_int32 *,
krb5_int32 *);
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_resolve(krb5_context context, const char *name,
krb5_keytab *id_out)
{
krb5_ktfile_data *data = NULL;
krb5_error_code err = ENOMEM;
krb5_keytab id;
*id_out = NULL;
id = calloc(1, sizeof(*id));
if (id == NULL)
return ENOMEM;
id->ops = &krb5_ktf_ops;
data = calloc(1, sizeof(krb5_ktfile_data));
if (data == NULL)
goto cleanup;
data->name = strdup(name);
if (data->name == NULL)
goto cleanup;
err = k5_mutex_init(&data->lock);
if (err)
goto cleanup;
data->openf = 0;
data->version = 0;
data->iter_count = 0;
id->data = (krb5_pointer) data;
id->magic = KV5M_KEYTAB;
*id_out = id;
return 0;
cleanup:
if (data)
free(data->name);
free(data);
free(id);
return err;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_close(krb5_context context, krb5_keytab id)
{
free(KTFILENAME(id));
zap(KTFILEBUFP(id), BUFSIZ);
k5_mutex_destroy(&((krb5_ktfile_data *)id->data)->lock);
free(id->data);
id->ops = 0;
free(id);
return (0);
}
static krb5_boolean
more_recent(const krb5_keytab_entry *k1, const krb5_keytab_entry *k2)
{
if (!ts_after(k2->timestamp, k1->timestamp) &&
k1->vno < 128 && k2->vno > 240)
return TRUE;
if (!ts_after(k1->timestamp, k2->timestamp) &&
k1->vno > 240 && k2->vno < 128)
return FALSE;
return k1->vno > k2->vno;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_entry(krb5_context context, krb5_keytab id,
krb5_const_principal principal, krb5_kvno kvno,
krb5_enctype enctype, krb5_keytab_entry *entry)
{
krb5_keytab_entry cur_entry, new_entry;
krb5_error_code kerror = 0;
int found_wrong_kvno = 0;
int was_open;
char *princname;
KTLOCK(id);
if (KTFILEP(id) != NULL) {
was_open = 1;
if (fseek(KTFILEP(id), KTSTARTOFF(id), SEEK_SET) == -1) {
KTUNLOCK(id);
return errno;
}
} else {
was_open = 0;
if ((kerror = krb5_ktfileint_openr(context, id))) {
KTUNLOCK(id);
return(kerror);
}
}
cur_entry.principal = 0;
cur_entry.vno = 0;
cur_entry.key.contents = 0;
while (TRUE) {
if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
break;
if (!krb5_principal_compare(context, principal, new_entry.principal)) {
krb5_kt_free_entry(context, &new_entry);
continue;
}
if (enctype != IGNORE_ENCTYPE && enctype != new_entry.key.enctype) {
krb5_kt_free_entry(context, &new_entry);
continue;
}
if (kvno == IGNORE_VNO || new_entry.vno == IGNORE_VNO) {
if (cur_entry.principal == NULL ||
more_recent(&new_entry, &cur_entry)) {
krb5_kt_free_entry(context, &cur_entry);
cur_entry = new_entry;
} else {
krb5_kt_free_entry(context, &new_entry);
}
} else {
if (new_entry.vno == kvno) {
krb5_kt_free_entry(context, &cur_entry);
cur_entry = new_entry;
if (new_entry.vno == kvno)
break;
} else if (new_entry.vno == (kvno & 0xff) &&
cur_entry.principal == NULL) {
cur_entry = new_entry;
} else {
found_wrong_kvno++;
krb5_kt_free_entry(context, &new_entry);
}
}
}
if (kerror == KRB5_KT_END) {
if (cur_entry.principal)
kerror = 0;
else if (found_wrong_kvno)
kerror = KRB5_KT_KVNONOTFOUND;
else {
kerror = KRB5_KT_NOTFOUND;
if (krb5_unparse_name(context, principal, &princname) == 0) {
k5_setmsg(context, kerror,
_("No key table entry found for %s"), princname);
free(princname);
}
}
}
if (kerror) {
if (was_open == 0)
(void) krb5_ktfileint_close(context, id);
KTUNLOCK(id);
krb5_kt_free_entry(context, &cur_entry);
return kerror;
}
if (was_open == 0 && (kerror = krb5_ktfileint_close(context, id)) != 0) {
KTUNLOCK(id);
krb5_kt_free_entry(context, &cur_entry);
return kerror;
}
KTUNLOCK(id);
*entry = cur_entry;
return 0;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
{
int result;
memset(name, 0, len);
result = snprintf(name, len, "%s:%s", id->ops->prefix, KTFILENAME(id));
if (SNPRINTF_OVERFLOW(result, len))
return(KRB5_KT_NAME_TOOLONG);
return(0);
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
{
krb5_error_code retval;
long *fileoff;
KTLOCK(id);
if (KTITERS(id) == 0) {
if ((retval = krb5_ktfileint_openr(context, id))) {
KTUNLOCK(id);
return retval;
}
}
if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) {
if (KTITERS(id) == 0)
krb5_ktfileint_close(context, id);
KTUNLOCK(id);
return ENOMEM;
}
*fileoff = KTSTARTOFF(id);
KTITERS(id)++;
if (KTITERS(id) == 0) {
KTITERS(id)--;
KTUNLOCK(id);
free(fileoff);
k5_setmsg(context, KRB5_KT_IOERR, "Too many keytab iterators active");
return KRB5_KT_IOERR;
}
*cursorp = (krb5_kt_cursor)fileoff;
KTUNLOCK(id);
return 0;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
{
long *fileoff = (long *)*cursor;
krb5_keytab_entry cur_entry;
krb5_error_code kerror;
KTLOCK(id);
if (KTFILEP(id) == NULL) {
KTUNLOCK(id);
return KRB5_KT_IOERR;
}
if (fseek(KTFILEP(id), *fileoff, 0) == -1) {
KTUNLOCK(id);
return KRB5_KT_END;
}
if ((kerror = krb5_ktfileint_read_entry(context, id, &cur_entry))) {
KTUNLOCK(id);
return kerror;
}
*fileoff = ftell(KTFILEP(id));
*entry = cur_entry;
KTUNLOCK(id);
return 0;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
{
krb5_error_code kerror;
free(*cursor);
KTLOCK(id);
KTITERS(id)--;
if (KTFILEP(id) != NULL && KTITERS(id) == 0)
kerror = krb5_ktfileint_close(context, id);
else
kerror = 0;
KTUNLOCK(id);
return kerror;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_error_code retval;
KTLOCK(id);
if (KTFILEP(id)) {
KTUNLOCK(id);
k5_setmsg(context, KRB5_KT_IOERR,
_("Cannot change keytab with keytab iterators active"));
return KRB5_KT_IOERR;
}
if ((retval = krb5_ktfileint_openw(context, id))) {
KTUNLOCK(id);
return retval;
}
if (fseek(KTFILEP(id), 0, 2) == -1) {
KTUNLOCK(id);
return KRB5_KT_END;
}
retval = krb5_ktfileint_write_entry(context, id, entry);
krb5_ktfileint_close(context, id);
KTUNLOCK(id);
return retval;
}
static krb5_error_code KRB5_CALLCONV
krb5_ktfile_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_keytab_entry cur_entry;
krb5_error_code kerror;
krb5_int32 delete_point;
KTLOCK(id);
if (KTFILEP(id)) {
KTUNLOCK(id);
k5_setmsg(context, KRB5_KT_IOERR,
_("Cannot change keytab with keytab iterators active"));
return KRB5_KT_IOERR;
}
if ((kerror = krb5_ktfileint_openw(context, id))) {
KTUNLOCK(id);
return kerror;
}
while (TRUE) {
if ((kerror = krb5_ktfileint_internal_read_entry(context, id,
&cur_entry,
&delete_point)))
break;
if ((entry->vno == cur_entry.vno) &&
(entry->key.enctype == cur_entry.key.enctype) &&
krb5_principal_compare(context, entry->principal, cur_entry.principal)) {
krb5_kt_free_entry(context, &cur_entry);
break;
}
krb5_kt_free_entry(context, &cur_entry);
}
if (kerror == KRB5_KT_END)
kerror = KRB5_KT_NOTFOUND;
if (kerror) {
(void) krb5_ktfileint_close(context, id);
KTUNLOCK(id);
return kerror;
}
kerror = krb5_ktfileint_delete_entry(context, id, delete_point);
if (kerror) {
(void) krb5_ktfileint_close(context, id);
} else {
kerror = krb5_ktfileint_close(context, id);
}
KTUNLOCK(id);
return kerror;
}
const struct _krb5_kt_ops krb5_ktf_ops = {
0,
"FILE",
krb5_ktfile_resolve,
krb5_ktfile_get_name,
krb5_ktfile_close,
krb5_ktfile_get_entry,
krb5_ktfile_start_seq_get,
krb5_ktfile_get_next,
krb5_ktfile_end_get,
krb5_ktfile_add,
krb5_ktfile_remove
};
const struct _krb5_kt_ops krb5_ktf_writable_ops = {
0,
"WRFILE",
krb5_ktfile_resolve,
krb5_ktfile_get_name,
krb5_ktfile_close,
krb5_ktfile_get_entry,
krb5_ktfile_start_seq_get,
krb5_ktfile_get_next,
krb5_ktfile_end_get,
krb5_ktfile_add,
krb5_ktfile_remove
};
const krb5_kt_ops krb5_kt_dfl_ops = {
0,
"FILE",
krb5_ktfile_resolve,
krb5_ktfile_get_name,
krb5_ktfile_close,
krb5_ktfile_get_entry,
krb5_ktfile_start_seq_get,
krb5_ktfile_get_next,
krb5_ktfile_end_get,
0,
0
};
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#endif
typedef krb5_int16 krb5_kt_vno;
#define krb5_kt_default_vno ((krb5_kt_vno)KRB5_KT_DEFAULT_VNO)
static krb5_error_code
krb5_ktfileint_open(krb5_context context, krb5_keytab id, int mode)
{
krb5_error_code kerror;
krb5_kt_vno kt_vno;
int writevno = 0;
KTCHECKLOCK(id);
errno = 0;
KTFILEP(id) = fopen(KTFILENAME(id),
(mode == KRB5_LOCKMODE_EXCLUSIVE) ? "rb+" : "rb");
if (!KTFILEP(id)) {
if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) {
k5_create_secure_file(context, KTFILENAME(id));
errno = 0;
KTFILEP(id) = fopen(KTFILENAME(id), "rb+");
if (!KTFILEP(id))
goto report_errno;
writevno = 1;
} else {
report_errno:
switch (errno) {
case 0:
return EMFILE;
case ENOENT:
k5_setmsg(context, ENOENT,
_("Key table file '%s' not found"), KTFILENAME(id));
return ENOENT;
default:
return errno;
}
}
}
set_cloexec_file(KTFILEP(id));
if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) {
(void) fclose(KTFILEP(id));
KTFILEP(id) = 0;
return kerror;
}
setbuf(KTFILEP(id), KTFILEBUFP(id));
if (writevno) {
kt_vno = htons(krb5_kt_default_vno);
KTVERSION(id) = krb5_kt_default_vno;
if (!fwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
kerror = errno;
(void) krb5_unlock_file(context, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
KTFILEP(id) = 0;
return kerror;
}
} else {
if (!fread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
if (feof(KTFILEP(id)))
kerror = KRB5_KEYTAB_BADVNO;
else
kerror = errno;
(void) krb5_unlock_file(context, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
KTFILEP(id) = 0;
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, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
KTFILEP(id) = 0;
return KRB5_KEYTAB_BADVNO;
}
}
KTSTARTOFF(id) = ftell(KTFILEP(id));
return 0;
}
static krb5_error_code
krb5_ktfileint_openr(krb5_context context, krb5_keytab id)
{
return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED);
}
static krb5_error_code
krb5_ktfileint_openw(krb5_context context, krb5_keytab id)
{
return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE);
}
static krb5_error_code
krb5_ktfileint_close(krb5_context context, krb5_keytab id)
{
krb5_error_code kerror;
KTCHECKLOCK(id);
if (!KTFILEP(id))
return 0;
kerror = krb5_unlock_file(context, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
KTFILEP(id) = 0;
return kerror;
}
static krb5_error_code
krb5_ktfileint_delete_entry(krb5_context context, krb5_keytab id, krb5_int32 delete_point)
{
krb5_int32 size;
krb5_int32 len;
char iobuf[BUFSIZ];
KTCHECKLOCK(id);
if (fseek(KTFILEP(id), delete_point, SEEK_SET)) {
return errno;
}
if (!fread(&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 (!fwrite(&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) {
if (!fwrite(iobuf, 1, (size_t) len, KTFILEP(id))) {
return KRB5_KT_IOERR;
}
size -= len;
if (size < len) {
len = size;
}
}
return k5_sync_disk_file(context, KTFILEP(id));
}
return 0;
}
static krb5_error_code
krb5_ktfileint_internal_read_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *ret_entry, krb5_int32 *delete_point)
{
krb5_octet vno;
krb5_int16 count;
unsigned int u_count, u_princ_size;
krb5_int16 enctype;
krb5_int16 princ_size;
int i;
krb5_int32 size;
krb5_int32 start_pos, pos;
krb5_error_code error;
char *tmpdata;
krb5_data *princ;
uint32_t vno32;
KTCHECKLOCK(id);
memset(ret_entry, 0, sizeof(krb5_keytab_entry));
ret_entry->magic = KV5M_KEYTAB_ENTRY;
if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
{
return errno;
}
do {
*delete_point = ftell(KTFILEP(id));
if (!fread(&size, sizeof(size), 1, KTFILEP(id))) {
return KRB5_KT_END;
}
if (KTVERSION(id) != KRB5_KT_VNO_1)
size = ntohl(size);
if (size < 0) {
if (size == INT32_MIN)
return KRB5_KT_FORMAT;
if (fseek(KTFILEP(id), -size, SEEK_CUR)) {
return errno;
}
}
} while (size < 0);
if (size == 0) {
return KRB5_KT_END;
}
start_pos = ftell(KTFILEP(id));
if (!fread(&count, sizeof(count), 1, KTFILEP(id)))
return KRB5_KT_END;
if (KTVERSION(id) == KRB5_KT_VNO_1) {
count -= 1;
} 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;
u_count = count;
ret_entry->principal->magic = KV5M_PRINCIPAL;
ret_entry->principal->length = u_count;
ret_entry->principal->data = (krb5_data *)
calloc(u_count, sizeof(krb5_data));
if (!ret_entry->principal->data) {
free(ret_entry->principal);
ret_entry->principal = 0;
return ENOMEM;
}
if (!fread(&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;
}
u_princ_size = princ_size;
ret_entry->principal->realm.length = u_princ_size;
tmpdata = malloc(u_princ_size+1);
if (!tmpdata) {
error = ENOMEM;
goto fail;
}
if (fread(tmpdata, 1, u_princ_size, KTFILEP(id)) != (size_t) princ_size) {
free(tmpdata);
error = KRB5_KT_END;
goto fail;
}
tmpdata[princ_size] = 0;
ret_entry->principal->realm.data = tmpdata;
for (i = 0; i < count; i++) {
princ = &ret_entry->principal->data[i];
if (!fread(&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;
}
u_princ_size = princ_size;
princ->length = u_princ_size;
princ->data = malloc(u_princ_size+1);
if (!princ->data) {
error = ENOMEM;
goto fail;
}
if (!fread(princ->data, sizeof(char), u_princ_size, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
princ->data[princ_size] = 0;
}
if (KTVERSION(id) != KRB5_KT_VNO_1) {
if (!fread(&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);
}
if (!fread(&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);
if (!fread(&vno, sizeof(vno), 1, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
ret_entry->vno = (krb5_kvno)vno;
if (!fread(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
if (KTVERSION(id) != KRB5_KT_VNO_1)
enctype = ntohs(enctype);
ret_entry->key.enctype = (krb5_enctype)enctype;
ret_entry->key.magic = KV5M_KEYBLOCK;
if (!fread(&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;
}
u_count = count;
ret_entry->key.length = u_count;
ret_entry->key.contents = (krb5_octet *)malloc(u_count);
if (!ret_entry->key.contents) {
error = ENOMEM;
goto fail;
}
if (!fread(ret_entry->key.contents, sizeof(krb5_octet), count,
KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
pos = ftell(KTFILEP(id));
if (pos - start_pos + 4 <= size) {
if (!fread(&vno32, sizeof(vno32), 1, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
if (KTVERSION(id) != KRB5_KT_VNO_1)
vno32 = ntohl(vno32);
if (vno32)
ret_entry->vno = vno32;
}
if (fseek(KTFILEP(id), start_pos + size, SEEK_SET) == -1) {
error = errno;
goto fail;
}
return 0;
fail:
for (i = 0; i < ret_entry->principal->length; i++)
free(ret_entry->principal->data[i].data);
free(ret_entry->principal->data);
ret_entry->principal->data = 0;
free(ret_entry->principal);
ret_entry->principal = 0;
return error;
}
static krb5_error_code
krb5_ktfileint_read_entry(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);
}
static krb5_error_code
krb5_ktfileint_write_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_octet vno;
krb5_data *princ;
krb5_int16 count, size, enctype;
krb5_error_code retval = 0;
krb5_timestamp timestamp;
krb5_int32 princ_type;
krb5_int32 size_needed;
krb5_int32 commit_point = -1;
uint32_t vno32;
int i;
KTCHECKLOCK(id);
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;
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 (!fwrite(&count, sizeof(count), 1, KTFILEP(id))) {
abend:
return KRB5_KT_IOERR;
}
size = entry->principal->realm.length;
if (KTVERSION(id) != KRB5_KT_VNO_1)
size = htons(size);
if (!fwrite(&size, sizeof(size), 1, KTFILEP(id))) {
goto abend;
}
if (!fwrite(entry->principal->realm.data, sizeof(char),
entry->principal->realm.length, KTFILEP(id))) {
goto abend;
}
count = (krb5_int16)entry->principal->length;
for (i = 0; i < count; i++) {
princ = &entry->principal->data[i];
size = princ->length;
if (KTVERSION(id) != KRB5_KT_VNO_1)
size = htons(size);
if (!fwrite(&size, sizeof(size), 1, KTFILEP(id))) {
goto abend;
}
if (!fwrite(princ->data, sizeof(char), princ->length, KTFILEP(id))) {
goto abend;
}
}
if (KTVERSION(id) != KRB5_KT_VNO_1) {
princ_type = htonl(entry->principal->type);
if (!fwrite(&princ_type, sizeof(princ_type), 1, KTFILEP(id))) {
goto abend;
}
}
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 (!fwrite(×tamp, sizeof(timestamp), 1, KTFILEP(id))) {
goto abend;
}
vno = (krb5_octet)entry->vno;
if (!fwrite(&vno, sizeof(vno), 1, KTFILEP(id))) {
goto abend;
}
if (KTVERSION(id) == KRB5_KT_VNO_1)
enctype = entry->key.enctype;
else
enctype = htons(entry->key.enctype);
if (!fwrite(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
goto abend;
}
if (KTVERSION(id) == KRB5_KT_VNO_1)
size = entry->key.length;
else
size = htons(entry->key.length);
if (!fwrite(&size, sizeof(size), 1, KTFILEP(id))) {
goto abend;
}
if (!fwrite(entry->key.contents, sizeof(krb5_octet),
entry->key.length, KTFILEP(id))) {
goto abend;
}
vno32 = entry->vno;
if (KTVERSION(id) != KRB5_KT_VNO_1)
vno32 = htonl(vno32);
if (!fwrite(&vno32, sizeof(vno32), 1, KTFILEP(id)))
goto abend;
if (fflush(KTFILEP(id)))
goto abend;
retval = k5_sync_disk_file(context, KTFILEP(id));
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 (!fwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) {
goto abend;
}
if (fflush(KTFILEP(id)))
goto abend;
retval = k5_sync_disk_file(context, KTFILEP(id));
return retval;
}
static krb5_error_code
krb5_ktfileint_size_entry(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 += entry->principal->realm.length + sizeof(krb5_int16);
for (i = 0; i < count; i++)
total_size += entry->principal->data[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;
total_size += sizeof(uint32_t);
*size_needed = total_size;
return retval;
}
static krb5_error_code
krb5_ktfileint_find_slot(krb5_context context, krb5_keytab id, krb5_int32 *size_needed, krb5_int32 *commit_point_ptr)
{
FILE *fp;
krb5_int32 size, zero_point, commit_point;
krb5_kt_vno kt_vno;
KTCHECKLOCK(id);
fp = KTFILEP(id);
if (fseek(fp, 0, SEEK_SET))
return errno;
if (!fread(&kt_vno, sizeof(kt_vno), 1, fp))
return errno;
for (;;) {
commit_point = ftell(fp);
if (commit_point == -1)
return errno;
if (!fread(&size, sizeof(size), 1, fp)) {
if (fseek(fp, 0, SEEK_CUR))
return errno;
size = 0;
if (!fwrite(&size, sizeof(size), 1, fp))
return errno;
break;
}
if (KTVERSION(id) != KRB5_KT_VNO_1)
size = ntohl(size);
if (size > 0) {
if (fseek(fp, size, SEEK_CUR))
return errno;
} else if (size < 0) {
if (size == INT32_MIN)
return KRB5_KT_FORMAT;
size = -size;
if (size >= *size_needed) {
*size_needed = size;
break;
} else {
if (fseek(fp, size, SEEK_CUR))
return errno;
}
} else {
zero_point = ftell(fp);
if (zero_point == -1)
return errno;
if (fseek(fp, *size_needed, SEEK_CUR))
return errno;
if (!fwrite(&size, sizeof(size), 1, fp))
return errno;
if (fseek(fp, zero_point, SEEK_SET))
return errno;
break;
}
}
*commit_point_ptr = commit_point;
return 0;
}
#endif