#include "k5-int.h"
#include "kt-int.h"
#include <stdio.h>
#ifndef LEAN_CLIENT
#define HEIMDAL_COMPATIBLE
typedef struct _krb5_mkt_link {
struct _krb5_mkt_link *next;
krb5_keytab_entry *entry;
} krb5_mkt_link, *krb5_mkt_cursor;
typedef struct _krb5_mkt_data {
char *name;
k5_mutex_t lock;
krb5_int32 refcount;
krb5_mkt_cursor link;
} krb5_mkt_data;
typedef struct _krb5_mkt_list_node {
struct _krb5_mkt_list_node *next;
krb5_keytab keytab;
} krb5_mkt_list_node;
typedef struct _krb5_mkt_ptcursor_data {
struct _krb5_mkt_list_node *cur;
} krb5_mkt_ptcursor_data;
static krb5_mkt_list_node * krb5int_mkt_list = NULL;
static k5_mutex_t krb5int_mkt_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
#define KTLOCK(id) k5_mutex_lock(&(((krb5_mkt_data *)(id)->data)->lock))
#define KTUNLOCK(id) k5_mutex_unlock(&(((krb5_mkt_data *)(id)->data)->lock))
#define KTCHECKLOCK(id) k5_mutex_assert_locked(&(((krb5_mkt_data *)(id)->data)->lock))
#define KTGLOCK k5_mutex_lock(&krb5int_mkt_mutex)
#define KTGUNLOCK k5_mutex_unlock(&krb5int_mkt_mutex)
#define KTGCHECKLOCK k5_mutex_assert_locked(&krb5int_mkt_mutex)
#define KTLINK(id) (((krb5_mkt_data *)(id)->data)->link)
#define KTREFCNT(id) (((krb5_mkt_data *)(id)->data)->refcount)
#define KTNAME(id) (((krb5_mkt_data *)(id)->data)->name)
extern const struct _krb5_kt_ops krb5_mkt_ops;
krb5_error_code KRB5_CALLCONV krb5_mkt_resolve
(krb5_context,
const char *,
krb5_keytab *);
krb5_error_code KRB5_CALLCONV krb5_mkt_get_name
(krb5_context,
krb5_keytab,
char *,
unsigned int);
krb5_error_code KRB5_CALLCONV krb5_mkt_close
(krb5_context,
krb5_keytab);
krb5_error_code KRB5_CALLCONV krb5_mkt_get_entry
(krb5_context,
krb5_keytab,
krb5_const_principal,
krb5_kvno,
krb5_enctype,
krb5_keytab_entry *);
krb5_error_code KRB5_CALLCONV krb5_mkt_start_seq_get
(krb5_context,
krb5_keytab,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_mkt_get_next
(krb5_context,
krb5_keytab,
krb5_keytab_entry *,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_mkt_end_get
(krb5_context,
krb5_keytab,
krb5_kt_cursor *);
krb5_error_code KRB5_CALLCONV krb5_mkt_add
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
krb5_error_code KRB5_CALLCONV krb5_mkt_remove
(krb5_context,
krb5_keytab,
krb5_keytab_entry *);
int krb5int_mkt_initialize(void) {
return k5_mutex_finish_init(&krb5int_mkt_mutex);
}
void krb5int_mkt_finalize(void) {
krb5_mkt_list_node *node, *next_node;
krb5_mkt_cursor cursor, next_cursor;
k5_mutex_destroy(&krb5int_mkt_mutex);
for (node = krb5int_mkt_list; node; node = next_node) {
next_node = node->next;
krb5_xfree(KTNAME(node->keytab));
for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
next_cursor = cursor->next;
krb5_kt_free_entry(NULL, cursor->entry);
krb5_xfree(cursor->entry);
krb5_xfree(cursor);
}
k5_mutex_destroy(&(((krb5_mkt_data *)node->keytab->data)->lock));
krb5_xfree(node->keytab->data);
krb5_xfree(node->keytab);
krb5_xfree(node);
}
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_resolve(krb5_context context, const char *name, krb5_keytab *id)
{
krb5_mkt_data *data = 0;
krb5_mkt_list_node *list;
krb5_error_code err = 0;
err = KTGLOCK;
if (err)
return(err);
for (list = krb5int_mkt_list; list; list = list->next)
{
if (strcmp(name,KTNAME(list->keytab)) == 0) {
*id = list->keytab;
goto done;
}
}
if ((list = (krb5_mkt_list_node *)malloc(sizeof(krb5_mkt_list_node))) == NULL) {
err = ENOMEM;
goto done;
}
if ((list->keytab = (krb5_keytab)malloc(sizeof(struct _krb5_kt))) == NULL) {
krb5_xfree(list);
err = ENOMEM;
goto done;
}
list->keytab->ops = &krb5_mkt_ops;
if ((data = (krb5_mkt_data *)malloc(sizeof(krb5_mkt_data))) == NULL) {
krb5_xfree(list->keytab);
krb5_xfree(list);
err = ENOMEM;
goto done;
}
data->name = NULL;
err = k5_mutex_init(&data->lock);
if (err) {
krb5_xfree(data);
krb5_xfree(list->keytab);
krb5_xfree(list);
goto done;
}
if ((data->name = strdup(name)) == NULL) {
k5_mutex_destroy(&data->lock);
krb5_xfree(data);
krb5_xfree(list->keytab);
krb5_xfree(list);
err = ENOMEM;
goto done;
}
data->link = NULL;
data->refcount = 0;
list->keytab->data = (krb5_pointer)data;
list->keytab->magic = KV5M_KEYTAB;
list->next = krb5int_mkt_list;
krb5int_mkt_list = list;
*id = list->keytab;
done:
err = KTLOCK(*id);
if (err) {
k5_mutex_destroy(&data->lock);
if (data && data->name)
krb5_xfree(data->name);
krb5_xfree(data);
if (list && list->keytab)
krb5_xfree(list->keytab);
krb5_xfree(list);
} else {
KTREFCNT(*id)++;
KTUNLOCK(*id);
}
KTGUNLOCK;
return(err);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_close(krb5_context context, krb5_keytab id)
{
krb5_mkt_list_node **listp;
#ifdef HEIMDAL_COMPATIBLE
krb5_mkt_list_node *node;
krb5_mkt_data * data;
#endif
krb5_error_code err = 0;
err = KTGLOCK;
if (err)
return(err);
for (listp = &krb5int_mkt_list; *listp; listp = &((*listp)->next))
{
if (id == (*listp)->keytab) {
break;
}
}
if (*listp == NULL) {
err = KRB5_KT_NOTFOUND;
goto done;
}
err = KTLOCK(id);
if (err)
goto done;
KTREFCNT(id)--;
KTUNLOCK(id);
#ifdef HEIMDAL_COMPATIBLE
data = (krb5_mkt_data *)id->data;
if (data->refcount == 0) {
krb5_mkt_cursor cursor, next_cursor;
node = *listp;
*listp = node->next;
krb5_xfree(data->name);
for (cursor = KTLINK(node->keytab); cursor; cursor = next_cursor) {
next_cursor = cursor->next;
krb5_kt_free_entry(context, cursor->entry);
krb5_xfree(cursor->entry);
krb5_xfree(cursor);
}
k5_mutex_destroy(&(data->lock));
krb5_xfree(data);
krb5_xfree(node->keytab);
krb5_xfree(node);
}
#endif
done:
KTGUNLOCK;
return(err);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_get_entry(krb5_context context, krb5_keytab id,
krb5_const_principal principal, krb5_kvno kvno,
krb5_enctype enctype, krb5_keytab_entry *out_entry)
{
krb5_mkt_cursor cursor;
krb5_keytab_entry *entry, *match = NULL;
krb5_error_code err = 0;
int found_wrong_kvno = 0;
krb5_boolean similar = 0;
err = KTLOCK(id);
if (err)
return err;
for (cursor = KTLINK(id); cursor && cursor->entry; cursor = cursor->next) {
entry = cursor->entry;
if (!krb5_principal_compare(context, principal, entry->principal))
continue;
if (enctype != IGNORE_ENCTYPE) {
if ((err = krb5_c_enctype_compare(context, enctype,
entry->key.enctype,
&similar))) {
continue;
}
if (!similar)
continue;
}
if (kvno == IGNORE_VNO) {
if (match == NULL)
match = entry;
else if (entry->vno > match->vno)
match = entry;
} else {
if (entry->vno == kvno) {
match = entry;
break;
} else {
found_wrong_kvno++;
}
}
}
if (match) {
out_entry->magic = match->magic;
out_entry->timestamp = match->timestamp;
out_entry->vno = match->vno;
out_entry->key = match->key;
err = krb5_copy_keyblock_contents(context, &(match->key),
&(out_entry->key));
if(enctype != IGNORE_ENCTYPE)
out_entry->key.enctype = enctype;
if(!err) {
err = krb5_copy_principal(context,
match->principal,
&(out_entry->principal));
}
} else {
if (!err)
err = found_wrong_kvno ? KRB5_KT_KVNONOTFOUND : KRB5_KT_NOTFOUND;
}
KTUNLOCK(id);
return(err);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_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, KTNAME(id));
if (SNPRINTF_OVERFLOW(result, len))
return(KRB5_KT_NAME_TOOLONG);
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursorp)
{
krb5_error_code err = 0;
err = KTLOCK(id);
if (err)
return(err);
*cursorp = (krb5_kt_cursor)KTLINK(id);
KTUNLOCK(id);
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_get_next(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor)
{
krb5_mkt_cursor mkt_cursor = (krb5_mkt_cursor)*cursor;
krb5_error_code err = 0;
err = KTLOCK(id);
if (err)
return err;
if (mkt_cursor == NULL) {
KTUNLOCK(id);
return KRB5_KT_END;
}
entry->magic = mkt_cursor->entry->magic;
entry->timestamp = mkt_cursor->entry->timestamp;
entry->vno = mkt_cursor->entry->vno;
entry->key = mkt_cursor->entry->key;
err = krb5_copy_keyblock_contents(context, &(mkt_cursor->entry->key),
&(entry->key));
if (!err)
err = krb5_copy_principal(context, mkt_cursor->entry->principal,
&(entry->principal));
if (!err)
*cursor = (krb5_kt_cursor *)mkt_cursor->next;
KTUNLOCK(id);
return(err);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_end_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor)
{
*cursor = NULL;
return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_add(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_error_code err = 0;
krb5_mkt_cursor cursor;
err = KTLOCK(id);
if (err)
return err;
cursor = (krb5_mkt_cursor)malloc(sizeof(krb5_mkt_link));
if (cursor == NULL) {
err = ENOMEM;
goto done;
}
cursor->entry = (krb5_keytab_entry *)malloc(sizeof(krb5_keytab_entry));
if (cursor->entry == NULL) {
krb5_xfree(cursor);
err = ENOMEM;
goto done;
}
cursor->entry->magic = entry->magic;
cursor->entry->timestamp = entry->timestamp;
cursor->entry->vno = entry->vno;
err = krb5_copy_keyblock_contents(context, &(entry->key),
&(cursor->entry->key));
if (err) {
krb5_xfree(cursor->entry);
krb5_xfree(cursor);
goto done;
}
err = krb5_copy_principal(context, entry->principal, &(cursor->entry->principal));
if (err) {
krb5_free_keyblock_contents(context, &(cursor->entry->key));
krb5_xfree(cursor->entry);
krb5_xfree(cursor);
goto done;
}
if (KTLINK(id) == NULL) {
cursor->next = NULL;
KTLINK(id) = cursor;
} else {
cursor->next = KTLINK(id);
KTLINK(id) = cursor;
}
done:
KTUNLOCK(id);
return err;
}
krb5_error_code KRB5_CALLCONV
krb5_mkt_remove(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry)
{
krb5_mkt_cursor *pcursor, next;
krb5_error_code err = 0;
err = KTLOCK(id);
if (err)
return err;
if ( KTLINK(id) == NULL ) {
err = KRB5_KT_NOTFOUND;
goto done;
}
for ( pcursor = &KTLINK(id); *pcursor; pcursor = &(*pcursor)->next ) {
if ( (*pcursor)->entry->vno == entry->vno &&
(*pcursor)->entry->key.enctype == entry->key.enctype &&
krb5_principal_compare(context, (*pcursor)->entry->principal, entry->principal))
break;
}
if (!*pcursor) {
err = KRB5_KT_NOTFOUND;
goto done;
}
krb5_kt_free_entry(context, (*pcursor)->entry);
krb5_xfree((*pcursor)->entry);
next = (*pcursor)->next;
krb5_xfree(*pcursor);
(*pcursor) = next;
done:
KTUNLOCK(id);
return err;
}
const struct _krb5_kt_ops krb5_mkt_ops = {
0,
"MEMORY",
krb5_mkt_resolve,
krb5_mkt_get_name,
krb5_mkt_close,
krb5_mkt_get_entry,
krb5_mkt_start_seq_get,
krb5_mkt_get_next,
krb5_mkt_end_get,
krb5_mkt_add,
krb5_mkt_remove,
NULL
};
#endif