#include "k5-int.h"
#include <stdio.h>
#include <errno.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NETINET_IN_H
#if !defined(_WIN32)
#include <netinet/in.h>
#else
#include "port-sockets.h"
#endif
#else
# error find some way to use net-byte-order file version numbers.
#endif
static krb5_error_code KRB5_CALLCONV krb5_fcc_close
(krb5_context, krb5_ccache id);
static krb5_error_code KRB5_CALLCONV krb5_fcc_destroy
(krb5_context, krb5_ccache id);
static krb5_error_code KRB5_CALLCONV krb5_fcc_end_seq_get
(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
static krb5_error_code KRB5_CALLCONV krb5_fcc_generate_new
(krb5_context, krb5_ccache *id);
static const char * KRB5_CALLCONV krb5_fcc_get_name
(krb5_context, krb5_ccache id);
static krb5_error_code KRB5_CALLCONV krb5_fcc_get_principal
(krb5_context, krb5_ccache id, krb5_principal *princ);
static krb5_error_code KRB5_CALLCONV krb5_fcc_initialize
(krb5_context, krb5_ccache id, krb5_principal princ);
static krb5_error_code KRB5_CALLCONV krb5_fcc_next_cred
(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor,
krb5_creds *creds);
static krb5_error_code krb5_fcc_read
(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
static krb5_error_code krb5_fcc_read_principal
(krb5_context, krb5_ccache id, krb5_principal *princ);
static krb5_error_code krb5_fcc_read_keyblock
(krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
static krb5_error_code krb5_fcc_read_data
(krb5_context, krb5_ccache id, krb5_data *data);
static krb5_error_code krb5_fcc_read_int32
(krb5_context, krb5_ccache id, krb5_int32 *i);
static krb5_error_code krb5_fcc_read_ui_2
(krb5_context, krb5_ccache id, krb5_ui_2 *i);
static krb5_error_code krb5_fcc_read_octet
(krb5_context, krb5_ccache id, krb5_octet *i);
static krb5_error_code krb5_fcc_read_times
(krb5_context, krb5_ccache id, krb5_ticket_times *t);
static krb5_error_code krb5_fcc_read_addrs
(krb5_context, krb5_ccache, krb5_address ***);
static krb5_error_code krb5_fcc_read_addr
(krb5_context, krb5_ccache, krb5_address *);
static krb5_error_code krb5_fcc_read_authdata
(krb5_context, krb5_ccache, krb5_authdata ***);
static krb5_error_code krb5_fcc_read_authdatum
(krb5_context, krb5_ccache, krb5_authdata *);
static krb5_error_code KRB5_CALLCONV krb5_fcc_resolve
(krb5_context, krb5_ccache *id, const char *residual);
static krb5_error_code KRB5_CALLCONV krb5_fcc_retrieve
(krb5_context, krb5_ccache id, krb5_flags whichfields,
krb5_creds *mcreds, krb5_creds *creds);
static krb5_error_code KRB5_CALLCONV krb5_fcc_start_seq_get
(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
static krb5_error_code KRB5_CALLCONV krb5_fcc_store
(krb5_context, krb5_ccache id, krb5_creds *creds);
static krb5_error_code krb5_fcc_skip_header
(krb5_context, krb5_ccache);
static krb5_error_code krb5_fcc_skip_principal
(krb5_context, krb5_ccache id);
static krb5_error_code KRB5_CALLCONV krb5_fcc_set_flags
(krb5_context, krb5_ccache id, krb5_flags flags);
extern const krb5_cc_ops krb5_cc_file_ops;
krb5_error_code krb5_change_cache (void);
static krb5_error_code krb5_fcc_write
(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
static krb5_error_code krb5_fcc_store_principal
(krb5_context, krb5_ccache id, krb5_principal princ);
static krb5_error_code krb5_fcc_store_keyblock
(krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
static krb5_error_code krb5_fcc_store_data
(krb5_context, krb5_ccache id, krb5_data *data);
static krb5_error_code krb5_fcc_store_int32
(krb5_context, krb5_ccache id, krb5_int32 i);
static krb5_error_code krb5_fcc_store_ui_4
(krb5_context, krb5_ccache id, krb5_ui_4 i);
static krb5_error_code krb5_fcc_store_ui_2
(krb5_context, krb5_ccache id, krb5_int32 i);
static krb5_error_code krb5_fcc_store_octet
(krb5_context, krb5_ccache id, krb5_int32 i);
static krb5_error_code krb5_fcc_store_times
(krb5_context, krb5_ccache id, krb5_ticket_times *t);
static krb5_error_code krb5_fcc_store_addrs
(krb5_context, krb5_ccache, krb5_address **);
static krb5_error_code krb5_fcc_store_addr
(krb5_context, krb5_ccache, krb5_address *);
static krb5_error_code krb5_fcc_store_authdata
(krb5_context, krb5_ccache, krb5_authdata **);
static krb5_error_code krb5_fcc_store_authdatum
(krb5_context, krb5_ccache, krb5_authdata *);
static krb5_error_code krb5_fcc_interpret
(krb5_context, int);
struct _krb5_fcc_data;
static krb5_error_code krb5_fcc_close_file
(krb5_context, struct _krb5_fcc_data *data);
static krb5_error_code krb5_fcc_open_file
(krb5_context, krb5_ccache, int);
#define KRB5_OK 0
#define KRB5_FCC_MAXLEN 100
#define KRB5_FCC_FVNO_1 0x0501
#define KRB5_FCC_FVNO_2 0x0502
#define KRB5_FCC_FVNO_3 0x0503
#define KRB5_FCC_FVNO_4 0x0504
#define FCC_OPEN_AND_ERASE 1
#define FCC_OPEN_RDWR 2
#define FCC_OPEN_RDONLY 3
#define FCC_TAG_DELTATIME 1
#ifndef TKT_ROOT
#ifdef MSDOS_FILESYSTEM
#define TKT_ROOT "\\tkt"
#else
#define TKT_ROOT "/tmp/tkt"
#endif
#endif
#define OPENCLOSE(id) (((krb5_fcc_data *)id->data)->flags & KRB5_TC_OPENCLOSE)
typedef struct _krb5_fcc_data {
char *filename;
k5_mutex_t lock;
int file;
krb5_flags flags;
int mode;
int version;
#define FCC_BUFSIZ 1024
int valid_bytes;
int cur_offset;
char buf[FCC_BUFSIZ];
} krb5_fcc_data;
static inline void invalidate_cache(krb5_fcc_data *data)
{
data->valid_bytes = 0;
}
static off_t fcc_lseek(krb5_fcc_data *data, off_t offset, int whence)
{
if (whence == SEEK_CUR && data->valid_bytes) {
assert(data->valid_bytes > 0);
assert(data->cur_offset > 0);
assert(data->cur_offset <= data->valid_bytes);
offset -= (data->valid_bytes - data->cur_offset);
}
invalidate_cache(data);
return lseek(data->file, offset, whence);
}
struct fcc_set {
struct fcc_set *next;
krb5_fcc_data *data;
unsigned int refcount;
};
k5_mutex_t krb5int_cc_file_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
static struct fcc_set *fccs = NULL;
typedef struct _krb5_fcc_cursor {
off_t pos;
} krb5_fcc_cursor;
#define MAYBE_OPEN(CONTEXT, ID, MODE) \
{ \
k5_assert_locked(&((krb5_fcc_data *)(ID)->data)->lock); \
if (OPENCLOSE (ID)) { \
krb5_error_code maybe_open_ret; \
maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE); \
if (maybe_open_ret) { \
k5_mutex_unlock(&((krb5_fcc_data *)(ID)->data)->lock); \
return maybe_open_ret; \
} \
} \
}
#define MAYBE_CLOSE(CONTEXT, ID, RET) \
{ \
if (OPENCLOSE (ID)) { \
krb5_error_code maybe_close_ret; \
maybe_close_ret = krb5_fcc_close_file (CONTEXT, \
(krb5_fcc_data *)(ID)->data); \
if (!(RET)) RET = maybe_close_ret; } }
#define MAYBE_CLOSE_IGNORE(CONTEXT, ID) \
{ \
if (OPENCLOSE (ID)) { \
(void) krb5_fcc_close_file (CONTEXT,(krb5_fcc_data *)(ID)->data); } }
#define CHECK(ret) if (ret != KRB5_OK) goto errout;
#define NO_FILE -1
static krb5_error_code
krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
{
#if 0
int ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len);
if (ret == -1)
return krb5_fcc_interpret(context, errno);
if (ret != len)
return KRB5_CC_END;
else
return KRB5_OK;
#else
krb5_fcc_data *data = (krb5_fcc_data *) id->data;
k5_assert_locked(&data->lock);
while (len > 0) {
int nread, e;
size_t ncopied;
assert (data->valid_bytes >= 0);
if (data->valid_bytes > 0)
assert(data->cur_offset <= data->valid_bytes);
if (data->valid_bytes == 0
|| data->cur_offset == data->valid_bytes) {
nread = read(data->file, data->buf, sizeof(data->buf));
e = errno;
if (nread < 0)
return krb5_fcc_interpret(context, e);
if (nread == 0)
return KRB5_CC_END;
data->valid_bytes = nread;
data->cur_offset = 0;
}
assert(data->cur_offset < data->valid_bytes);
ncopied = len;
assert(ncopied == len);
if (data->valid_bytes - data->cur_offset < ncopied)
ncopied = data->valid_bytes - data->cur_offset;
memcpy(buf, data->buf + data->cur_offset, ncopied);
data->cur_offset += ncopied;
assert(data->cur_offset > 0);
assert(data->cur_offset <= data->valid_bytes);
len -= ncopied;
assert(len >= 0);
buf = (char*)buf + ncopied;
}
return 0;
#endif
}
#define ALLOC(NUM,TYPE) \
(((NUM) <= (((size_t)0-1)/ sizeof(TYPE))) \
? (TYPE *) calloc((NUM), sizeof(TYPE)) \
: (errno = ENOMEM,(TYPE *) 0))
static krb5_error_code
krb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code kret;
register krb5_principal tmpprinc;
krb5_int32 length, type;
int i;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
*princ = NULL;
if (data->version == KRB5_FCC_FVNO_1) {
type = KRB5_NT_UNKNOWN;
} else {
kret = krb5_fcc_read_int32(context, id, &type);
if (kret != KRB5_OK)
return kret;
}
kret = krb5_fcc_read_int32(context, id, &length);
if (kret != KRB5_OK)
return kret;
if (data->version == KRB5_FCC_FVNO_1)
length--;
if (length < 0)
return KRB5_CC_NOMEM;
tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data));
if (tmpprinc == NULL)
return KRB5_CC_NOMEM;
if (length) {
size_t msize = length;
if (msize != length) {
free(tmpprinc);
return KRB5_CC_NOMEM;
}
tmpprinc->data = ALLOC (msize, krb5_data);
if (tmpprinc->data == 0) {
free((char *)tmpprinc);
return KRB5_CC_NOMEM;
}
} else
tmpprinc->data = 0;
tmpprinc->magic = KV5M_PRINCIPAL;
tmpprinc->length = length;
tmpprinc->type = type;
kret = krb5_fcc_read_data(context, id, krb5_princ_realm(context, tmpprinc));
i = 0;
CHECK(kret);
for (i=0; i < length; i++) {
kret = krb5_fcc_read_data(context, id, krb5_princ_component(context, tmpprinc, i));
CHECK(kret);
}
*princ = tmpprinc;
return KRB5_OK;
errout:
while(--i >= 0)
free(krb5_princ_component(context, tmpprinc, i)->data);
free((char *)tmpprinc->data);
free((char *)tmpprinc);
return kret;
}
static krb5_error_code
krb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs)
{
krb5_error_code kret;
krb5_int32 length;
size_t msize;
int i;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
*addrs = 0;
kret = krb5_fcc_read_int32(context, id, &length);
CHECK(kret);
msize = length;
msize += 1;
if (msize == 0 || msize - 1 != length || length < 0)
return KRB5_CC_NOMEM;
*addrs = ALLOC (msize, krb5_address *);
if (*addrs == NULL)
return KRB5_CC_NOMEM;
for (i=0; i < length; i++) {
(*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address));
if ((*addrs)[i] == NULL) {
krb5_free_addresses(context, *addrs);
return KRB5_CC_NOMEM;
}
(*addrs)[i]->contents = NULL;
kret = krb5_fcc_read_addr(context, id, (*addrs)[i]);
CHECK(kret);
}
return KRB5_OK;
errout:
if (*addrs) {
krb5_free_addresses(context, *addrs);
*addrs = NULL;
}
return kret;
}
static krb5_error_code
krb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code kret;
krb5_ui_2 ui2;
krb5_int32 int32;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
keyblock->magic = KV5M_KEYBLOCK;
keyblock->contents = 0;
kret = krb5_fcc_read_ui_2(context, id, &ui2);
keyblock->enctype = ui2;
CHECK(kret);
if (data->version == KRB5_FCC_FVNO_3) {
kret = krb5_fcc_read_ui_2(context, id, &ui2);
CHECK(kret);
}
kret = krb5_fcc_read_int32(context, id, &int32);
CHECK(kret);
if (int32 < 0)
return KRB5_CC_NOMEM;
keyblock->length = int32;
if (keyblock->length != int32)
return KRB5_CC_NOMEM;
if ( keyblock->length == 0 )
return KRB5_OK;
keyblock->contents = ALLOC (keyblock->length, krb5_octet);
if (keyblock->contents == NULL)
return KRB5_CC_NOMEM;
kret = krb5_fcc_read(context, id, keyblock->contents, keyblock->length);
if (kret)
goto errout;
return KRB5_OK;
errout:
if (keyblock->contents) {
krb5_xfree(keyblock->contents);
keyblock->contents = NULL;
}
return kret;
}
static krb5_error_code
krb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data)
{
krb5_error_code kret;
krb5_int32 len;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
data->magic = KV5M_DATA;
data->data = 0;
kret = krb5_fcc_read_int32(context, id, &len);
CHECK(kret);
if (len < 0)
return KRB5_CC_NOMEM;
data->length = len;
if (data->length != len || data->length + 1 == 0)
return KRB5_CC_NOMEM;
if (data->length == 0) {
data->data = 0;
return KRB5_OK;
}
data->data = (char *) malloc(data->length+1);
if (data->data == NULL)
return KRB5_CC_NOMEM;
kret = krb5_fcc_read(context, id, data->data, (unsigned) data->length);
CHECK(kret);
data->data[data->length] = 0;
return KRB5_OK;
errout:
if (data->data) {
krb5_xfree(data->data);
data->data = NULL;
}
return kret;
}
static krb5_error_code
krb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
{
krb5_error_code kret;
krb5_ui_2 ui2;
krb5_int32 int32;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
addr->magic = KV5M_ADDRESS;
addr->contents = 0;
kret = krb5_fcc_read_ui_2(context, id, &ui2);
CHECK(kret);
addr->addrtype = ui2;
kret = krb5_fcc_read_int32(context, id, &int32);
CHECK(kret);
if ((int32 & VALID_INT_BITS) != int32)
return KRB5_CC_NOMEM;
addr->length = int32;
if (addr->length != int32)
return KRB5_CC_NOMEM;
if (addr->length == 0)
return KRB5_OK;
addr->contents = (krb5_octet *) malloc(addr->length);
if (addr->contents == NULL)
return KRB5_CC_NOMEM;
kret = krb5_fcc_read(context, id, addr->contents, addr->length);
CHECK(kret);
return KRB5_OK;
errout:
if (addr->contents) {
krb5_xfree(addr->contents);
addr->contents = NULL;
}
return kret;
}
static krb5_error_code
krb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code retval;
unsigned char buf[4];
krb5_int32 val;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32));
else {
retval = krb5_fcc_read(context, id, buf, 4);
if (retval)
return retval;
val = buf[0];
val = (val << 8) | buf[1];
val = (val << 8) | buf[2];
val = (val << 8) | buf[3];
*i = val;
return 0;
}
}
static krb5_error_code
krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code retval;
unsigned char buf[2];
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2));
else {
retval = krb5_fcc_read(context, id, buf, 2);
if (retval)
return retval;
*i = (buf[0] << 8) + buf[1];
return 0;
}
}
static krb5_error_code
krb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i)
{
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
return krb5_fcc_read(context, id, (krb5_pointer) i, 1);
}
static krb5_error_code
krb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code retval;
krb5_int32 i;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times));
else {
retval = krb5_fcc_read_int32(context, id, &i);
CHECK(retval);
t->authtime = i;
retval = krb5_fcc_read_int32(context, id, &i);
CHECK(retval);
t->starttime = i;
retval = krb5_fcc_read_int32(context, id, &i);
CHECK(retval);
t->endtime = i;
retval = krb5_fcc_read_int32(context, id, &i);
CHECK(retval);
t->renew_till = i;
}
return 0;
errout:
return retval;
}
static krb5_error_code
krb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a)
{
krb5_error_code kret;
krb5_int32 length;
size_t msize;
int i;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
*a = 0;
kret = krb5_fcc_read_int32(context, id, &length);
CHECK(kret);
if (length == 0)
return KRB5_OK;
msize = length;
msize += 1;
if (msize == 0 || msize - 1 != length || length < 0)
return KRB5_CC_NOMEM;
*a = ALLOC (msize, krb5_authdata *);
if (*a == NULL)
return KRB5_CC_NOMEM;
for (i=0; i < length; i++) {
(*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata));
if ((*a)[i] == NULL) {
krb5_free_authdata(context, *a);
return KRB5_CC_NOMEM;
}
(*a)[i]->contents = NULL;
kret = krb5_fcc_read_authdatum(context, id, (*a)[i]);
CHECK(kret);
}
return KRB5_OK;
errout:
if (*a) {
krb5_free_authdata(context, *a);
*a = NULL;
}
return kret;
}
static krb5_error_code
krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
{
krb5_error_code kret;
krb5_int32 int32;
krb5_ui_2 ui2;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
a->magic = KV5M_AUTHDATA;
a->contents = NULL;
kret = krb5_fcc_read_ui_2(context, id, &ui2);
CHECK(kret);
a->ad_type = (krb5_authdatatype)ui2;
kret = krb5_fcc_read_int32(context, id, &int32);
CHECK(kret);
if ((int32 & VALID_INT_BITS) != int32)
return KRB5_CC_NOMEM;
a->length = int32;
if (a->length != int32)
return KRB5_CC_NOMEM;
if (a->length == 0 )
return KRB5_OK;
a->contents = (krb5_octet *) malloc(a->length);
if (a->contents == NULL)
return KRB5_CC_NOMEM;
kret = krb5_fcc_read(context, id, a->contents, a->length);
CHECK(kret);
return KRB5_OK;
errout:
if (a->contents) {
krb5_xfree(a->contents);
a->contents = NULL;
}
return kret;
}
#undef CHECK
#define CHECK(ret) if (ret != KRB5_OK) return ret;
static krb5_error_code
krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
{
int ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
invalidate_cache((krb5_fcc_data *) id->data);
ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len);
if (ret < 0)
return krb5_fcc_interpret(context, errno);
if (ret != len)
return KRB5_CC_WRITE;
return KRB5_OK;
}
static krb5_error_code
krb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code ret;
krb5_int32 i, length, tmp, type;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
type = krb5_princ_type(context, princ);
tmp = length = krb5_princ_size(context, princ);
if (data->version == KRB5_FCC_FVNO_1) {
tmp++;
} else {
ret = krb5_fcc_store_int32(context, id, type);
CHECK(ret);
}
ret = krb5_fcc_store_int32(context, id, tmp);
CHECK(ret);
ret = krb5_fcc_store_data(context, id, krb5_princ_realm(context, princ));
CHECK(ret);
for (i=0; i < length; i++) {
ret = krb5_fcc_store_data(context, id, krb5_princ_component(context, princ, i));
CHECK(ret);
}
return KRB5_OK;
}
static krb5_error_code
krb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs)
{
krb5_error_code ret;
krb5_address **temp;
krb5_int32 i, length = 0;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if (addrs) {
temp = addrs;
while (*temp++)
length += 1;
}
ret = krb5_fcc_store_int32(context, id, length);
CHECK(ret);
for (i=0; i < length; i++) {
ret = krb5_fcc_store_addr(context, id, addrs[i]);
CHECK(ret);
}
return KRB5_OK;
}
static krb5_error_code
krb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
CHECK(ret);
if (data->version == KRB5_FCC_FVNO_3) {
ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
CHECK(ret);
}
ret = krb5_fcc_store_ui_4(context, id, keyblock->length);
CHECK(ret);
return krb5_fcc_write(context, id, (char *) keyblock->contents, keyblock->length);
}
static krb5_error_code
krb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
{
krb5_error_code ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ret = krb5_fcc_store_ui_2(context, id, addr->addrtype);
CHECK(ret);
ret = krb5_fcc_store_ui_4(context, id, addr->length);
CHECK(ret);
return krb5_fcc_write(context, id, (char *) addr->contents, addr->length);
}
static krb5_error_code
krb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data)
{
krb5_error_code ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ret = krb5_fcc_store_ui_4(context, id, data->length);
CHECK(ret);
return krb5_fcc_write(context, id, data->data, data->length);
}
static krb5_error_code
krb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
unsigned char buf[4];
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
else {
buf[3] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[2] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[1] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
return krb5_fcc_write(context, id, buf, 4);
}
}
static krb5_error_code
krb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
unsigned char buf[4];
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
else {
buf[3] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[2] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[1] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
return krb5_fcc_write(context, id, buf, 4);
}
}
static krb5_error_code
krb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_ui_2 ibuf;
unsigned char buf[2];
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2)) {
ibuf = (krb5_ui_2) i;
return krb5_fcc_write(context, id, (char *) &ibuf, sizeof(krb5_ui_2));
} else {
buf[1] = (unsigned char) (i & 0xFF);
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
return krb5_fcc_write(context, id, buf, 2);
}
}
static krb5_error_code
krb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i)
{
krb5_octet ibuf;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ibuf = (krb5_octet) i;
return krb5_fcc_write(context, id, (char *) &ibuf, 1);
}
static krb5_error_code
krb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code retval;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if ((data->version == KRB5_FCC_FVNO_1) ||
(data->version == KRB5_FCC_FVNO_2))
return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times));
else {
retval = krb5_fcc_store_int32(context, id, t->authtime);
CHECK(retval);
retval = krb5_fcc_store_int32(context, id, t->starttime);
CHECK(retval);
retval = krb5_fcc_store_int32(context, id, t->endtime);
CHECK(retval);
retval = krb5_fcc_store_int32(context, id, t->renew_till);
CHECK(retval);
return 0;
}
}
static krb5_error_code
krb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a)
{
krb5_error_code ret;
krb5_authdata **temp;
krb5_int32 i, length=0;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
if (a != NULL) {
for (temp=a; *temp; temp++)
length++;
}
ret = krb5_fcc_store_int32(context, id, length);
CHECK(ret);
for (i=0; i<length; i++) {
ret = krb5_fcc_store_authdatum (context, id, a[i]);
CHECK(ret);
}
return KRB5_OK;
}
static krb5_error_code
krb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a)
{
krb5_error_code ret;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
ret = krb5_fcc_store_ui_2(context, id, a->ad_type);
CHECK(ret);
ret = krb5_fcc_store_ui_4(context, id, a->length);
CHECK(ret);
return krb5_fcc_write(context, id, (krb5_pointer) a->contents, a->length);
}
#undef CHECK
static krb5_error_code
krb5_fcc_close_file (krb5_context context, krb5_fcc_data *data)
{
int ret;
krb5_error_code retval;
k5_assert_locked(&data->lock);
if (data->file == NO_FILE)
return KRB5_FCC_INTERNAL;
retval = krb5_unlock_file(context, data->file);
ret = close (data->file);
data->file = NO_FILE;
if (retval)
return retval;
return ret ? krb5_fcc_interpret (context, errno) : 0;
}
#if defined(ANSI_STDIO) || defined(_WIN32)
#define BINARY_MODE "b"
#else
#define BINARY_MODE ""
#endif
#ifndef HAVE_SETVBUF
#undef setvbuf
#define setvbuf(FILE,BUF,MODE,SIZE) \
((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF))
#endif
static krb5_error_code
krb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode)
{
krb5_os_context os_ctx = (krb5_os_context)context->os_context;
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_ui_2 fcc_fvno;
krb5_ui_2 fcc_flen;
krb5_ui_2 fcc_tag;
krb5_ui_2 fcc_taglen;
int f, open_flag;
int lock_flag;
krb5_error_code retval = 0;
k5_assert_locked(&data->lock);
invalidate_cache(data);
if (data->file != NO_FILE) {
(void) krb5_unlock_file(context, data->file);
(void) close (data->file);
data->file = NO_FILE;
}
switch(mode) {
case FCC_OPEN_AND_ERASE:
unlink(data->filename);
open_flag = O_CREAT|O_EXCL|O_TRUNC|O_RDWR;
break;
case FCC_OPEN_RDWR:
open_flag = O_RDWR;
break;
case FCC_OPEN_RDONLY:
default:
open_flag = O_RDONLY;
break;
}
f = THREEPARAMOPEN (data->filename, open_flag | O_BINARY, 0600);
if (f == NO_FILE)
return krb5_fcc_interpret (context, errno);
data->mode = mode;
if (data->mode == FCC_OPEN_RDONLY)
lock_flag = KRB5_LOCKMODE_SHARED;
else
lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
if ((retval = krb5_lock_file(context, f, lock_flag))) {
(void) close(f);
return retval;
}
if (mode == FCC_OPEN_AND_ERASE) {
int cnt;
fcc_fvno = htons(context->fcc_default_format);
data->version = context->fcc_default_format;
if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) !=
sizeof(fcc_fvno)) {
retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) :
KRB5_CC_IO);
goto done;
}
data->file = f;
if (data->version == KRB5_FCC_FVNO_4) {
fcc_flen = 0;
if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32));
retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen);
if (retval) goto done;
if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
fcc_tag = FCC_TAG_DELTATIME;
fcc_taglen = 2*sizeof(krb5_int32);
retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag);
if (retval) goto done;
retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen);
if (retval) goto done;
retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset);
if (retval) goto done;
retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset);
if (retval) goto done;
}
}
invalidate_cache(data);
goto done;
}
invalidate_cache(data);
if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) {
retval = KRB5_CC_FORMAT;
goto done;
}
data->version = ntohs(fcc_fvno);
if ((data->version != KRB5_FCC_FVNO_4) &&
(data->version != KRB5_FCC_FVNO_3) &&
(data->version != KRB5_FCC_FVNO_2) &&
(data->version != KRB5_FCC_FVNO_1)) {
retval = KRB5_CCACHE_BADVNO;
goto done;
}
data->file = f;
if (data->version == KRB5_FCC_FVNO_4) {
char buf[1024];
if (krb5_fcc_read_ui_2(context, id, &fcc_flen) ||
(fcc_flen > sizeof(buf)))
{
retval = KRB5_CC_FORMAT;
goto done;
}
while (fcc_flen) {
if ((fcc_flen < (2 * sizeof(krb5_ui_2))) ||
krb5_fcc_read_ui_2(context, id, &fcc_tag) ||
krb5_fcc_read_ui_2(context, id, &fcc_taglen) ||
(fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2))))
{
retval = KRB5_CC_FORMAT;
goto done;
}
switch (fcc_tag) {
case FCC_TAG_DELTATIME:
if (fcc_taglen != 2*sizeof(krb5_int32)) {
retval = KRB5_CC_FORMAT;
goto done;
}
if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
{
if (krb5_fcc_read(context, id, buf, fcc_taglen)) {
retval = KRB5_CC_FORMAT;
goto done;
}
break;
}
if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) ||
krb5_fcc_read_int32(context, id, &os_ctx->usec_offset))
{
retval = KRB5_CC_FORMAT;
goto done;
}
os_ctx->os_flags =
((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
KRB5_OS_TOFFSET_VALID);
break;
default:
if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) {
retval = KRB5_CC_FORMAT;
goto done;
}
break;
}
fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen);
}
}
done:
if (retval) {
data->file = -1;
(void) krb5_unlock_file(context, f);
(void) close(f);
}
return retval;
}
static krb5_error_code
krb5_fcc_skip_header(krb5_context context, krb5_ccache id)
{
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
krb5_error_code kret;
krb5_ui_2 fcc_flen;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET);
if (data->version == KRB5_FCC_FVNO_4) {
kret = krb5_fcc_read_ui_2(context, id, &fcc_flen);
if (kret) return kret;
if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0)
return errno;
}
return KRB5_OK;
}
static krb5_error_code
krb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
{
krb5_error_code kret;
krb5_principal princ;
k5_assert_locked(&((krb5_fcc_data *) id->data)->lock);
kret = krb5_fcc_read_principal(context, id, &princ);
if (kret != KRB5_OK)
return kret;
krb5_free_principal(context, princ);
return KRB5_OK;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
{
krb5_error_code kret = 0;
int reti = 0;
kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
if (kret)
return kret;
MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE);
#if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
{
#ifdef HAVE_FCHMOD
reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE);
#else
reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE);
#endif
if (reti == -1) {
kret = krb5_fcc_interpret(context, errno);
MAYBE_CLOSE(context, id, kret);
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
return kret;
}
}
#endif
kret = krb5_fcc_store_principal(context, id, princ);
MAYBE_CLOSE(context, id, kret);
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
krb5_change_cache ();
return kret;
}
static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data)
{
krb5_error_code kerr;
struct fcc_set **fccsp;
kerr = k5_mutex_lock(&krb5int_cc_file_mutex);
if (kerr)
return kerr;
for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next)
if ((*fccsp)->data == data)
break;
assert(*fccsp != NULL);
assert((*fccsp)->data == data);
(*fccsp)->refcount--;
if ((*fccsp)->refcount == 0) {
struct fcc_set *temp;
data = (*fccsp)->data;
temp = *fccsp;
*fccsp = (*fccsp)->next;
free(temp);
k5_mutex_unlock(&krb5int_cc_file_mutex);
k5_mutex_assert_unlocked(&data->lock);
free(data->filename);
zap(data->buf, sizeof(data->buf));
if (data->file >= 0) {
k5_mutex_lock(&data->lock);
krb5_fcc_close_file(context, data);
k5_mutex_unlock(&data->lock);
}
k5_mutex_destroy(&data->lock);
free(data);
} else
k5_mutex_unlock(&krb5int_cc_file_mutex);
return 0;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_close(krb5_context context, krb5_ccache id)
{
dereference(context, (krb5_fcc_data *) id->data);
krb5_xfree(id);
return KRB5_OK;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_destroy(krb5_context context, krb5_ccache id)
{
krb5_error_code kret = 0;
krb5_fcc_data *data = (krb5_fcc_data *) id->data;
register int ret;
struct stat buf;
unsigned long i, size;
unsigned int wlen;
char zeros[BUFSIZ];
kret = k5_mutex_lock(&data->lock);
if (kret)
return kret;
if (OPENCLOSE(id)) {
invalidate_cache(data);
ret = THREEPARAMOPEN(data->filename,
O_RDWR | O_BINARY, 0);
if (ret < 0) {
kret = krb5_fcc_interpret(context, errno);
goto cleanup;
}
data->file = ret;
}
else
fcc_lseek(data, (off_t) 0, SEEK_SET);
#ifdef MSDOS_FILESYSTEM
ret = fstat(data->file, &buf);
if (ret == -1) {
kret = krb5_fcc_interpret(context, errno);
size = 0;
} else
size = (unsigned long) buf.st_size;
memset(zeros, 0, BUFSIZ);
while (size > 0) {
wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size);
i = write(data->file, zeros, wlen);
if (i < 0) {
kret = krb5_fcc_interpret(context, errno);
break;
}
size -= i;
}
if (OPENCLOSE(id)) {
(void) close(((krb5_fcc_data *)id->data)->file);
data->file = -1;
}
ret = unlink(data->filename);
if (ret < 0) {
kret = krb5_fcc_interpret(context, errno);
goto cleanup;
}
#else
ret = unlink(data->filename);
if (ret < 0) {
kret = krb5_fcc_interpret(context, errno);
if (OPENCLOSE(id)) {
(void) close(((krb5_fcc_data *)id->data)->file);
data->file = -1;
kret = ret;
}
goto cleanup;
}
ret = fstat(data->file, &buf);
if (ret < 0) {
kret = krb5_fcc_interpret(context, errno);
if (OPENCLOSE(id)) {
(void) close(((krb5_fcc_data *)id->data)->file);
data->file = -1;
}
goto cleanup;
}
size = (unsigned long) buf.st_size;
memset(zeros, 0, BUFSIZ);
for (i=0; i < size / BUFSIZ; i++)
if (write(data->file, zeros, BUFSIZ) < 0) {
kret = krb5_fcc_interpret(context, errno);
if (OPENCLOSE(id)) {
(void) close(((krb5_fcc_data *)id->data)->file);
data->file = -1;
}
goto cleanup;
}
wlen = (unsigned int) (size % BUFSIZ);
if (write(data->file, zeros, wlen) < 0) {
kret = krb5_fcc_interpret(context, errno);
if (OPENCLOSE(id)) {
(void) close(((krb5_fcc_data *)id->data)->file);
data->file = -1;
}
goto cleanup;
}
ret = close(data->file);
data->file = -1;
if (ret)
kret = krb5_fcc_interpret(context, errno);
#endif
cleanup:
k5_mutex_unlock(&data->lock);
dereference(context, data);
krb5_xfree(id);
krb5_change_cache ();
return kret;
}
extern const krb5_cc_ops krb5_fcc_ops;
static krb5_error_code KRB5_CALLCONV
krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
{
krb5_ccache lid;
krb5_error_code kret;
krb5_fcc_data *data;
struct fcc_set *setptr;
kret = k5_mutex_lock(&krb5int_cc_file_mutex);
if (kret)
return kret;
for (setptr = fccs; setptr; setptr = setptr->next) {
if (!strcmp(setptr->data->filename, residual))
break;
}
if (setptr) {
data = setptr->data;
assert(setptr->refcount != 0);
setptr->refcount++;
assert(setptr->refcount != 0);
kret = k5_mutex_lock(&data->lock);
if (kret) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
return kret;
}
k5_mutex_unlock(&krb5int_cc_file_mutex);
} else {
data = malloc(sizeof(krb5_fcc_data));
if (data == NULL) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
return KRB5_CC_NOMEM;
}
data->filename = strdup(residual);
if (data->filename == NULL) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
free(data);
return KRB5_CC_NOMEM;
}
kret = k5_mutex_init(&data->lock);
if (kret) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
free(data->filename);
free(data);
return kret;
}
kret = k5_mutex_lock(&data->lock);
if (kret) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
k5_mutex_destroy(&data->lock);
free(data->filename);
free(data);
return kret;
}
data->version = data->mode = 0;
data->flags = KRB5_TC_OPENCLOSE;
data->file = -1;
data->valid_bytes = 0;
setptr = malloc(sizeof(struct fcc_set));
if (setptr == NULL) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
k5_mutex_destroy(&data->lock);
free(data->filename);
free(data);
return KRB5_CC_NOMEM;
}
setptr->refcount = 1;
setptr->data = data;
setptr->next = fccs;
fccs = setptr;
k5_mutex_unlock(&krb5int_cc_file_mutex);
}
k5_mutex_assert_locked(&data->lock);
k5_mutex_unlock(&data->lock);
lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
if (lid == NULL) {
dereference(context, data);
return KRB5_CC_NOMEM;
}
lid->ops = &krb5_fcc_ops;
lid->data = data;
lid->magic = KV5M_CCACHE;
*id = lid;
return KRB5_OK;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id,
krb5_cc_cursor *cursor)
{
krb5_fcc_cursor *fcursor;
krb5_error_code kret = KRB5_OK;
krb5_fcc_data *data = (krb5_fcc_data *)id->data;
kret = k5_mutex_lock(&data->lock);
if (kret)
return kret;
fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
if (fcursor == NULL) {
k5_mutex_unlock(&data->lock);
return KRB5_CC_NOMEM;
}
if (OPENCLOSE(id)) {
kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
if (kret) {
krb5_xfree(fcursor);
k5_mutex_unlock(&data->lock);
return kret;
}
}
kret = krb5_fcc_skip_header(context, id);
if (kret) goto done;
kret = krb5_fcc_skip_principal(context, id);
if (kret) goto done;
fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR);
*cursor = (krb5_cc_cursor) fcursor;
done:
MAYBE_CLOSE(context, id, kret);
k5_mutex_unlock(&data->lock);
return kret;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
krb5_creds *creds)
{
#define TCHECK(ret) if (ret != KRB5_OK) goto lose;
krb5_error_code kret;
krb5_fcc_cursor *fcursor;
krb5_int32 int32;
krb5_octet octet;
krb5_fcc_data *d = (krb5_fcc_data *) id->data;
kret = k5_mutex_lock(&d->lock);
if (kret)
return kret;
memset((char *)creds, 0, sizeof(*creds));
MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
fcursor = (krb5_fcc_cursor *) *cursor;
kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1);
if (kret) {
kret = krb5_fcc_interpret(context, errno);
MAYBE_CLOSE(context, id, kret);
k5_mutex_unlock(&d->lock);
return kret;
}
kret = krb5_fcc_read_principal(context, id, &creds->client);
TCHECK(kret);
kret = krb5_fcc_read_principal(context, id, &creds->server);
TCHECK(kret);
kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock);
TCHECK(kret);
kret = krb5_fcc_read_times(context, id, &creds->times);
TCHECK(kret);
kret = krb5_fcc_read_octet(context, id, &octet);
TCHECK(kret);
creds->is_skey = octet;
kret = krb5_fcc_read_int32(context, id, &int32);
TCHECK(kret);
creds->ticket_flags = int32;
kret = krb5_fcc_read_addrs(context, id, &creds->addresses);
TCHECK(kret);
kret = krb5_fcc_read_authdata(context, id, &creds->authdata);
TCHECK(kret);
kret = krb5_fcc_read_data(context, id, &creds->ticket);
TCHECK(kret);
kret = krb5_fcc_read_data(context, id, &creds->second_ticket);
TCHECK(kret);
fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR);
cursor = (krb5_cc_cursor *) fcursor;
lose:
MAYBE_CLOSE (context, id, kret);
k5_mutex_unlock(&d->lock);
if (kret != KRB5_OK)
krb5_free_cred_contents(context, creds);
return kret;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
{
krb5_xfree((krb5_fcc_cursor *) *cursor);
return 0;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
{
krb5_ccache lid;
int ret;
krb5_error_code retcode = 0;
char scratch[sizeof(TKT_ROOT)+6+1];
krb5_fcc_data *data;
krb5_int16 fcc_fvno = htons(context->fcc_default_format);
krb5_int16 fcc_flen = 0;
int errsave, cnt;
struct fcc_set *setptr;
krb5_error_code kret;
lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
if (lid == NULL)
return KRB5_CC_NOMEM;
lid->ops = &krb5_fcc_ops;
(void) strcpy(scratch, TKT_ROOT);
(void) strcat(scratch, "XXXXXX");
#ifdef HAVE_MKSTEMP
ret = mkstemp(scratch);
if (ret == -1) {
return krb5_fcc_interpret(context, errno);
}
#else
mktemp(scratch);
ret = THREEPARAMOPEN(scratch, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0);
if (ret == -1) {
return krb5_fcc_interpret(context, errno);
}
#endif
lid->data = (krb5_pointer) malloc(sizeof(krb5_fcc_data));
if (lid->data == NULL) {
close(ret);
unlink(scratch);
krb5_xfree(lid);
return KRB5_CC_NOMEM;
}
((krb5_fcc_data *) lid->data)->filename = (char *)
malloc(strlen(scratch) + 1);
if (((krb5_fcc_data *) lid->data)->filename == NULL) {
close(ret);
unlink(scratch);
krb5_xfree(((krb5_fcc_data *) lid->data));
krb5_xfree(lid);
return KRB5_CC_NOMEM;
}
((krb5_fcc_data *) lid->data)->flags = 0;
((krb5_fcc_data *) lid->data)->file = -1;
((krb5_fcc_data *) lid->data)->valid_bytes = 0;
data = (krb5_fcc_data *) lid->data;
retcode = k5_mutex_init(&data->lock);
if (retcode) {
close(ret);
unlink(scratch);
goto err_out;
}
strcpy(((krb5_fcc_data *) lid->data)->filename, scratch);
#ifndef HAVE_FCHMOD
#ifdef HAVE_CHMOD
chmod(((krb5_fcc_data *) lid->data)->filename, S_IRUSR | S_IWUSR);
#endif
#else
fchmod(ret, S_IRUSR | S_IWUSR);
#endif
if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno)))
!= sizeof(fcc_fvno)) {
errsave = errno;
(void) close(ret);
(void) unlink(((krb5_fcc_data *) lid->data)->filename);
retcode = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
goto err_out;
}
if (context->fcc_default_format == KRB5_FCC_FVNO_4) {
if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen)))
!= sizeof(fcc_flen)) {
errsave = errno;
(void) close(ret);
(void) unlink(((krb5_fcc_data *) lid->data)->filename);
retcode = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
goto err_out;
}
}
if (close(ret) == -1) {
errsave = errno;
(void) unlink(((krb5_fcc_data *) lid->data)->filename);
retcode = krb5_fcc_interpret(context, errsave);
goto err_out;
}
*id = lid;
((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
krb5_change_cache ();
kret = k5_mutex_lock(&krb5int_cc_file_mutex);
if (kret) {
(void) unlink(((krb5_fcc_data *) lid->data)->filename);
retcode = kret;
goto err_out;
}
setptr = malloc(sizeof(struct fcc_set));
if (setptr == NULL) {
k5_mutex_unlock(&krb5int_cc_file_mutex);
(void) unlink(((krb5_fcc_data *) lid->data)->filename);
retcode = KRB5_CC_NOMEM;
goto err_out;
}
setptr->refcount = 1;
setptr->data = data;
setptr->next = fccs;
fccs = setptr;
k5_mutex_unlock(&krb5int_cc_file_mutex);
return KRB5_OK;
err_out:
krb5_xfree(((krb5_fcc_data *) lid->data)->filename);
krb5_xfree(((krb5_fcc_data *) lid->data));
krb5_xfree(lid);
return retcode;
}
static const char * KRB5_CALLCONV
krb5_fcc_get_name (krb5_context context, krb5_ccache id)
{
return (char *) ((krb5_fcc_data *) id->data)->filename;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
{
krb5_error_code kret = KRB5_OK;
kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
if (kret)
return kret;
MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
kret = krb5_fcc_skip_header(context, id);
if (kret) goto done;
kret = krb5_fcc_read_principal(context, id, princ);
done:
MAYBE_CLOSE(context, id, kret);
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
return kret;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
{
return krb5_cc_retrieve_cred_default (context, id, whichfields,
mcreds, creds);
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
{
#define TCHECK(ret) if (ret != KRB5_OK) goto lose;
krb5_error_code ret;
ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
if (ret)
return ret;
MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END);
if (ret < 0) {
MAYBE_CLOSE_IGNORE(context, id);
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
return krb5_fcc_interpret(context, errno);
}
ret = krb5_fcc_store_principal(context, id, creds->client);
TCHECK(ret);
ret = krb5_fcc_store_principal(context, id, creds->server);
TCHECK(ret);
ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock);
TCHECK(ret);
ret = krb5_fcc_store_times(context, id, &creds->times);
TCHECK(ret);
ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey);
TCHECK(ret);
ret = krb5_fcc_store_int32(context, id, creds->ticket_flags);
TCHECK(ret);
ret = krb5_fcc_store_addrs(context, id, creds->addresses);
TCHECK(ret);
ret = krb5_fcc_store_authdata(context, id, creds->authdata);
TCHECK(ret);
ret = krb5_fcc_store_data(context, id, &creds->ticket);
TCHECK(ret);
ret = krb5_fcc_store_data(context, id, &creds->second_ticket);
TCHECK(ret);
lose:
MAYBE_CLOSE(context, id, ret);
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
krb5_change_cache ();
return ret;
#undef TCHECK
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
krb5_creds *creds)
{
return KRB5_CC_NOSUPP;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
{
krb5_error_code ret = KRB5_OK;
ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
if (ret)
return ret;
if (flags & KRB5_TC_OPENCLOSE) {
if (!OPENCLOSE(id)
&& ((krb5_fcc_data *) id->data)->file != NO_FILE)
(void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data));
} else {
MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
}
((krb5_fcc_data *) id->data)->flags = flags;
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
return ret;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
{
krb5_error_code ret = KRB5_OK;
ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock);
if (ret)
return ret;
*flags = ((krb5_fcc_data *) id->data)->flags;
k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock);
return ret;
}
static krb5_error_code
krb5_fcc_interpret(krb5_context context, int errnum)
{
register krb5_error_code retval;
switch (errnum) {
case ENOENT:
retval = KRB5_FCC_NOFILE;
break;
case EPERM:
case EACCES:
#ifdef EISDIR
case EISDIR:
#endif
case ENOTDIR:
#ifdef ELOOP
case ELOOP:
#endif
#ifdef ETXTBSY
case ETXTBSY:
#endif
case EBUSY:
case EROFS:
retval = KRB5_FCC_PERM;
break;
case EINVAL:
case EEXIST:
case EFAULT:
case EBADF:
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
#endif
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#endif
retval = KRB5_FCC_INTERNAL;
break;
#ifdef EDQUOT
case EDQUOT:
#endif
case ENOSPC:
case EIO:
case ENFILE:
case EMFILE:
case ENXIO:
default:
retval = KRB5_CC_IO;
krb5_set_error_message(context, retval,
"Credentials cache I/O operation failed (%s)",
strerror(errnum));
}
return retval;
}
const krb5_cc_ops krb5_fcc_ops = {
0,
"FILE",
krb5_fcc_get_name,
krb5_fcc_resolve,
krb5_fcc_generate_new,
krb5_fcc_initialize,
krb5_fcc_destroy,
krb5_fcc_close,
krb5_fcc_store,
krb5_fcc_retrieve,
krb5_fcc_get_principal,
krb5_fcc_start_seq_get,
krb5_fcc_next_cred,
krb5_fcc_end_seq_get,
krb5_fcc_remove_cred,
krb5_fcc_set_flags,
krb5_fcc_get_flags,
};
#if defined(_WIN32)
krb5_error_code
krb5_change_cache (void) {
PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
return 0;
}
unsigned int KRB5_CALLCONV
krb5_get_notification_message (void) {
static unsigned int message = 0;
if (message == 0)
message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
return message;
}
#else
krb5_error_code
krb5_change_cache (void)
{
return 0;
}
unsigned int
krb5_get_notification_message (void)
{
return 0;
}
#endif
const krb5_cc_ops krb5_cc_file_ops = {
0,
"FILE",
krb5_fcc_get_name,
krb5_fcc_resolve,
krb5_fcc_generate_new,
krb5_fcc_initialize,
krb5_fcc_destroy,
krb5_fcc_close,
krb5_fcc_store,
krb5_fcc_retrieve,
krb5_fcc_get_principal,
krb5_fcc_start_seq_get,
krb5_fcc_next_cred,
krb5_fcc_end_seq_get,
krb5_fcc_remove_cred,
krb5_fcc_set_flags,
krb5_fcc_get_flags,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};