#define NEED_SOCKETS
#include "k5-int.h"
#include <stdio.h>
#define IGNORE_VNO 0
#define IGNORE_ENCTYPE 0
#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;
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 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;
krb5_error_code KRB5_CALLCONV krb5_ktfile_resolve
(krb5_context,
const char *,
krb5_keytab *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_wresolve
(krb5_context,
const char *,
krb5_keytab *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_get_name
(krb5_context,
krb5_keytab,
char *,
unsigned int);
krb5_error_code KRB5_CALLCONV krb5_ktfile_close
(krb5_context,
krb5_keytab);
krb5_error_code KRB5_CALLCONV krb5_ktfile_get_entry
(krb5_context,
krb5_keytab,
krb5_const_principal,
krb5_kvno,
krb5_enctype,
krb5_keytab_entry *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_start_seq_get
(krb5_context,
krb5_keytab,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_get_next
(krb5_context,
krb5_keytab,
krb5_keytab_entry *,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_end_get
(krb5_context,
krb5_keytab,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_add
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
krb5_error_code KRB5_CALLCONV krb5_ktfile_remove
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
krb5_error_code krb5_ktfileint_openr
(krb5_context,
krb5_keytab);
krb5_error_code krb5_ktfileint_openw
(krb5_context,
krb5_keytab);
krb5_error_code krb5_ktfileint_close
(krb5_context,
krb5_keytab);
krb5_error_code krb5_ktfileint_read_entry
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
krb5_error_code krb5_ktfileint_write_entry
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
krb5_error_code krb5_ktfileint_delete_entry
(krb5_context,
krb5_keytab,
krb5_int32);
krb5_error_code krb5_ktfileint_internal_read_entry
(krb5_context,
krb5_keytab,
krb5_keytab_entry *,
krb5_int32 *);
krb5_error_code krb5_ktfileint_size_entry
(krb5_context,
krb5_keytab_entry *,
krb5_int32 *);
krb5_error_code krb5_ktfileint_find_slot
(krb5_context,
krb5_keytab,
krb5_int32 *,
krb5_int32 *);
krb5_error_code KRB5_CALLCONV
krb5_ktfile_resolve(krb5_context context, const char *name, krb5_keytab *id)
{
krb5_ktfile_data *data;
krb5_error_code err;
if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
return(ENOMEM);
(*id)->ops = &krb5_ktf_ops;
if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
krb5_xfree(*id);
return(ENOMEM);
}
err = k5_mutex_init(&data->lock);
if (err) {
krb5_xfree(*id);
return err;
}
if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
k5_mutex_destroy(&data->lock);
krb5_xfree(data);
krb5_xfree(*id);
return(ENOMEM);
}
(void) strcpy(data->name, name);
data->openf = 0;
data->version = 0;
(*id)->data = (krb5_pointer)data;
(*id)->magic = KV5M_KEYTAB;
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_ktfile_close(krb5_context context, krb5_keytab id)
{
krb5_xfree(KTFILENAME(id));
zap(KTFILEBUFP(id), BUFSIZ);
k5_mutex_destroy(&((krb5_ktfile_data *)id->data)->lock);
krb5_xfree(id->data);
id->ops = 0;
krb5_xfree(id);
return (0);
}
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;
krb5_boolean similar;
int kvno_offset = 0;
kerror = KTLOCK(id);
if (kerror)
return kerror;
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) {
if ((kerror = krb5_c_enctype_compare(context, enctype,
new_entry.key.enctype,
&similar))) {
krb5_kt_free_entry(context, &new_entry);
break;
}
if (!similar) {
krb5_kt_free_entry(context, &new_entry);
continue;
}
new_entry.key.enctype = enctype;
}
if (kvno == IGNORE_VNO) {
#define M(VNO) (((VNO) - kvno_offset + 256) % 256)
if (new_entry.vno > 240)
kvno_offset = 128;
if (! cur_entry.principal ||
M(new_entry.vno) > M(cur_entry.vno)) {
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 & 0xff)) {
krb5_kt_free_entry(context, &cur_entry);
cur_entry = new_entry;
break;
} 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 (kerror) {
(void) krb5_ktfileint_close(context, id);
KTUNLOCK(id);
krb5_kt_free_entry(context, &cur_entry);
return kerror;
}
if ((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;
}
krb5_error_code KRB5_CALLCONV
krb5_ktfile_get_name(krb5_context context, krb5_keytab id, char *name, unsigned int len)
{
memset(name, 0, len);
if (len < strlen(id->ops->prefix)+2)
return(KRB5_KT_NAME_TOOLONG);
strcpy(name, id->ops->prefix);
name += strlen(id->ops->prefix);
name[0] = ':';
name++;
len -= strlen(id->ops->prefix)+1;
if (len < strlen(KTFILENAME(id)+1))
return(KRB5_KT_NAME_TOOLONG);
strcpy(name, KTFILENAME(id));
return(0);
}
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;
retval = KTLOCK(id);
if (retval)
return retval;
if ((retval = krb5_ktfileint_openr(context, id))) {
KTUNLOCK(id);
return retval;
}
if (!(fileoff = (long *)malloc(sizeof(*fileoff)))) {
krb5_ktfileint_close(context, id);
KTUNLOCK(id);
return ENOMEM;
}
*fileoff = ftell(KTFILEP(id));
*cursorp = (krb5_kt_cursor)fileoff;
KTUNLOCK(id);
return 0;
}
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;
kerror = KTLOCK(id);
if (kerror)
return kerror;
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;
}
krb5_error_code KRB5_CALLCONV
krb5_ktfile_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
{
krb5_error_code kerror;
krb5_xfree(*cursor);
KTLOCK(id);
kerror = krb5_ktfileint_close(context, id);
KTUNLOCK(id);
return kerror;
}
static const char ktfile_def_name[] = ".";
static krb5_error_code krb5_ktf_keytab_size
(krb5_context, krb5_pointer, size_t *);
static krb5_error_code krb5_ktf_keytab_externalize
(krb5_context, krb5_pointer, krb5_octet **, size_t *);
static krb5_error_code krb5_ktf_keytab_internalize
(krb5_context,krb5_pointer *, krb5_octet **, size_t *);
const krb5_ser_entry krb5_ktfile_ser_entry = {
KV5M_KEYTAB,
krb5_ktf_keytab_size,
krb5_ktf_keytab_externalize,
krb5_ktf_keytab_internalize
};
static krb5_error_code
krb5_ktf_keytab_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep)
{
krb5_error_code kret;
krb5_keytab keytab;
size_t required;
krb5_ktfile_data *ktdata;
kret = EINVAL;
if ((keytab = (krb5_keytab) arg)) {
required = sizeof(krb5_int32) * 7;
if (keytab->ops && keytab->ops->prefix)
required += (strlen(keytab->ops->prefix)+1);
ktdata = (krb5_ktfile_data *) keytab->data;
required += strlen((ktdata && ktdata->name) ?
ktdata->name : ktfile_def_name);
kret = 0;
if (!kret)
*sizep += required;
}
return(kret);
}
static krb5_error_code
krb5_ktf_keytab_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain)
{
krb5_error_code kret;
krb5_keytab keytab;
size_t required;
krb5_octet *bp;
size_t remain;
krb5_ktfile_data *ktdata;
krb5_int32 file_is_open;
krb5_int32 file_pos[2];
char *ktname;
size_t namelen;
const char *fnamep;
required = 0;
bp = *buffer;
remain = *lenremain;
kret = EINVAL;
if ((keytab = (krb5_keytab) arg)) {
kret = ENOMEM;
if (!krb5_ktf_keytab_size(kcontext, arg, &required) &&
(required <= remain)) {
(void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
ktdata = (krb5_ktfile_data *) keytab->data;
file_is_open = 0;
file_pos[0] = 0;
file_pos[1] = 0;
namelen = (keytab->ops && keytab->ops->prefix) ?
strlen(keytab->ops->prefix)+1 : 0;
if (ktdata && ktdata->name)
fnamep = ktdata->name;
else
fnamep = ktfile_def_name;
namelen += (strlen(fnamep)+1);
if ((ktname = (char *) malloc(namelen))) {
if (keytab->ops && keytab->ops->prefix)
sprintf(ktname, "%s:%s", keytab->ops->prefix, fnamep);
else
strcpy(ktname, fnamep);
if (ktdata) {
if (ktdata->openf) {
long fpos;
int fflags = 0;
file_is_open = 1;
#if !defined(_WIN32)
fflags = fcntl(fileno(ktdata->openf), F_GETFL, 0);
if (fflags > 0)
file_is_open |= ((fflags & O_ACCMODE) << 1);
#else
file_is_open = 0;
#endif
fpos = ftell(ktdata->openf);
#if SIZEOF_LONG == 4
file_pos[0] = fpos;
#else
file_pos[0] = fpos & 0xffffffff;
file_pos[1] = (fpos >> 32) & 0xffffffff;
#endif
}
}
(void) krb5_ser_pack_int32((krb5_int32) strlen(ktname),
&bp, &remain);
(void) krb5_ser_pack_bytes((krb5_octet *) ktname,
strlen(ktname),
&bp, &remain);
(void) krb5_ser_pack_int32(file_is_open, &bp, &remain);
(void) krb5_ser_pack_int32(file_pos[0], &bp, &remain);
(void) krb5_ser_pack_int32(file_pos[1], &bp, &remain);
(void) krb5_ser_pack_int32((krb5_int32) ((ktdata) ?
ktdata->version : 0),
&bp, &remain);
(void) krb5_ser_pack_int32(KV5M_KEYTAB, &bp, &remain);
kret = 0;
*buffer = bp;
*lenremain = remain;
free(ktname);
}
}
}
return(kret);
}
static krb5_error_code
krb5_ktf_keytab_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain)
{
krb5_error_code kret;
krb5_keytab keytab;
krb5_int32 ibuf;
krb5_octet *bp;
size_t remain;
char *ktname;
krb5_ktfile_data *ktdata;
krb5_int32 file_is_open;
krb5_int32 foffbuf[2];
bp = *buffer;
remain = *lenremain;
kret = EINVAL;
if (krb5_ser_unpack_int32(&ibuf, &bp, &remain))
ibuf = 0;
if (ibuf == KV5M_KEYTAB) {
kret = ENOMEM;
kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
if (!kret &&
(ktname = (char *) malloc((size_t) (ibuf+1))) &&
!(kret = krb5_ser_unpack_bytes((krb5_octet *) ktname,
(size_t) ibuf,
&bp, &remain))) {
ktname[ibuf] = '\0';
kret = krb5_kt_resolve(kcontext, ktname, &keytab);
if (!kret) {
kret = ENOMEM;
ktdata = (krb5_ktfile_data *) keytab->data;
if (!ktdata) {
keytab->data = (void *) malloc(sizeof(krb5_ktfile_data));
ktdata = (krb5_ktfile_data *) keytab->data;
memset(ktdata, 0, sizeof(krb5_ktfile_data));
if (strchr(ktname, (int) ':'))
ktdata->name = strdup(strchr(ktname, (int) ':')+1);
else
ktdata->name = strdup(ktname);
}
if (ktdata) {
if (remain >= (sizeof(krb5_int32)*5)) {
(void) krb5_ser_unpack_int32(&file_is_open,
&bp, &remain);
(void) krb5_ser_unpack_int32(&foffbuf[0],
&bp, &remain);
(void) krb5_ser_unpack_int32(&foffbuf[1],
&bp, &remain);
(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
ktdata->version = (int) ibuf;
(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
if (ibuf == KV5M_KEYTAB) {
if (file_is_open) {
int fmode;
long fpos;
#if !defined(_WIN32)
fmode = (file_is_open >> 1) & O_ACCMODE;
#else
fmode = 0;
#endif
if (fmode)
kret = krb5_ktfileint_openw(kcontext,
keytab);
else
kret = krb5_ktfileint_openr(kcontext,
keytab);
if (!kret) {
#if SIZEOF_LONG == 4
fpos = foffbuf[0];
#else
fpos = foffbuf[0] | ((long) foffbuf[1] << 32);
#endif
fseek(KTFILEP(keytab), fpos, SEEK_SET);
}
}
kret = 0;
}
else
kret = EINVAL;
}
}
if (kret) {
if (keytab->data) {
if (KTFILENAME(keytab))
krb5_xfree(KTFILENAME(keytab));
krb5_xfree(keytab->data);
}
krb5_xfree(keytab);
}
else {
*buffer = bp;
*lenremain = remain;
*argp = (krb5_pointer) keytab;
}
}
free(ktname);
}
}
return(kret);
}
krb5_error_code KRB5_CALLCONV
krb5_ktfile_wresolve(krb5_context context, const char *name, krb5_keytab *id)
{
krb5_ktfile_data *data;
krb5_error_code err;
if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
return(ENOMEM);
(*id)->ops = &krb5_ktf_writable_ops;
if ((data = (krb5_ktfile_data *)malloc(sizeof(krb5_ktfile_data))) == NULL) {
krb5_xfree(*id);
return(ENOMEM);
}
err = k5_mutex_init(&data->lock);
if (err) {
krb5_xfree(*id);
return err;
}
if ((data->name = (char *)calloc(strlen(name) + 1, sizeof(char))) == NULL) {
k5_mutex_destroy(&data->lock);
krb5_xfree(data);
krb5_xfree(*id);
return(ENOMEM);
}
(void) strcpy(data->name, name);
data->openf = 0;
data->version = 0;
(*id)->data = (krb5_pointer)data;
(*id)->magic = KV5M_KEYTAB;
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_ktfile_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_error_code retval;
retval = KTLOCK(id);
if (retval)
return retval;
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;
}
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;
kerror = KTLOCK(id);
if (kerror)
return kerror;
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,
0,
0,
&krb5_ktfile_ser_entry
};
const struct _krb5_kt_ops krb5_ktf_writable_ops = {
0,
"WRFILE",
krb5_ktfile_wresolve,
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,
&krb5_ktfile_ser_entry
};
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,
&krb5_ktfile_ser_entry
};
#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)
#define xfwrite(a, b, c, d) fwrite((char *)a, b, (unsigned) c, d)
#define xfread(a, b, c, d) fread((char *)a, b, (unsigned) c, d)
#ifdef ANSI_STDIO
static char *const fopen_mode_rbplus= "rb+";
static char *const fopen_mode_rb = "rb";
#else
static char *const fopen_mode_rbplus= "r+";
static char *const fopen_mode_rb = "r";
#endif
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) ?
fopen_mode_rbplus : fopen_mode_rb);
if (!KTFILEP(id)) {
if ((mode == KRB5_LOCKMODE_EXCLUSIVE) && (errno == ENOENT)) {
krb5_create_secure_file(context, KTFILENAME(id));
errno = 0;
KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
if (!KTFILEP(id))
return errno ? errno : EMFILE;
writevno = 1;
} else
return errno ? errno : EMFILE;
}
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 (!xfwrite(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
kerror = errno;
(void) krb5_unlock_file(context, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
return kerror;
}
} else {
if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
kerror = errno;
(void) krb5_unlock_file(context, fileno(KTFILEP(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, fileno(KTFILEP(id)));
(void) fclose(KTFILEP(id));
return KRB5_KEYTAB_BADVNO;
}
}
return 0;
}
krb5_error_code
krb5_ktfileint_openr(krb5_context context, krb5_keytab id)
{
return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_SHARED);
}
krb5_error_code
krb5_ktfileint_openw(krb5_context context, krb5_keytab id)
{
return krb5_ktfileint_open(context, id, KRB5_LOCKMODE_EXCLUSIVE);
}
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;
}
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 (!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(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;
register int i;
krb5_int32 size;
krb5_int32 start_pos;
krb5_error_code error;
char *tmpdata;
krb5_data *princ;
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 (!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));
if (!xfread(&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 (!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;
}
u_princ_size = princ_size;
krb5_princ_set_realm_length(context, ret_entry->principal, 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;
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;
}
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 (!xfread(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 (!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);
}
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);
if (!xfread(&vno, sizeof(vno), 1, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
ret_entry->vno = (krb5_kvno)vno;
if (!xfread(&enctype, sizeof(enctype), 1, KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
ret_entry->key.enctype = (krb5_enctype)enctype;
if (KTVERSION(id) != KRB5_KT_VNO_1)
ret_entry->key.enctype = ntohs(ret_entry->key.enctype);
ret_entry->key.magic = KV5M_KEYBLOCK;
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;
}
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 (!xfread(ret_entry->key.contents, sizeof(krb5_octet), count,
KTFILEP(id))) {
error = KRB5_KT_END;
goto fail;
}
fseek(KTFILEP(id), start_pos + size, SEEK_SET);
return 0;
fail:
for (i = 0; i < krb5_princ_size(context, ret_entry->principal); 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(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(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;
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) krb5_princ_size(context, entry->principal) + 1;
} else {
count = htons((u_short) krb5_princ_size(context, entry->principal));
}
if (!xfwrite(&count, sizeof(count), 1, KTFILEP(id))) {
abend:
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) krb5_princ_size(context, entry->principal);
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;
}
}
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;
}
}
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;
}
vno = (krb5_octet)entry->vno;
if (!xfwrite(&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 (!xfwrite(&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 (!xfwrite(&size, sizeof(size), 1, KTFILEP(id))) {
goto abend;
}
if (!xfwrite(entry->key.contents, sizeof(krb5_octet),
entry->key.length, KTFILEP(id))) {
goto abend;
}
if (fflush(KTFILEP(id)))
goto abend;
retval = krb5_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 (!xfwrite(&size_needed, sizeof(size_needed), 1, KTFILEP(id))) {
goto abend;
}
if (fflush(KTFILEP(id)))
goto abend;
retval = krb5_sync_disk_file(context, KTFILEP(id));
return retval;
}
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) krb5_princ_size(context, entry->principal);
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;
}
krb5_error_code
krb5_ktfileint_find_slot(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];
KTCHECKLOCK(id);
if (fseek(KTFILEP(id), 0, SEEK_SET)) {
return errno;
}
if (!xfread(&kt_vno, sizeof(kt_vno), 1, KTFILEP(id))) {
return KRB5_KT_IOERR;
}
while (!found) {
*commit_point = ftell(KTFILEP(id));
if (!xfread(&size, sizeof(size), 1, KTFILEP(id))) {
size = 0;
if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
{
return errno;
}
#ifdef notdef
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) {
if (fseek(KTFILEP(id), size, SEEK_CUR)) {
return errno;
}
} else {
if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
{
return errno;
}
zero_point = ftell(KTFILEP(id));
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));
fflush(KTFILEP(id));
if (feof(KTFILEP(id))) {
break;
}
if (fseek(KTFILEP(id), 0L, SEEK_CUR) < 0)
{
return errno;
}
}
if (fseek(KTFILEP(id), zero_point, SEEK_SET)) {
return errno;
}
}
}
}
return 0;
}