#include "prof_int.h"
#include <stdio.h>
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <errno.h>
static errcode_t
init_module(struct profile_vtable *vtable, void *cbdata,
prf_lib_handle_t handle, profile_t *ret_profile)
{
profile_t profile;
struct profile_vtable *vt_copy;
if (vtable->minor_ver < 1 || !vtable->get_values || !vtable->free_values)
return EINVAL;
if (vtable->cleanup && !vtable->copy)
return EINVAL;
if (vtable->iterator_create &&
(!vtable->iterator || !vtable->iterator_free || !vtable->free_string))
return EINVAL;
profile = malloc(sizeof(*profile));
if (!profile)
return ENOMEM;
memset(profile, 0, sizeof(*profile));
vt_copy = malloc(sizeof(*vt_copy));
if (!vt_copy) {
free(profile);
return ENOMEM;
}
*vt_copy = *vtable;
profile->vt = vt_copy;
profile->cbdata = cbdata;
profile->lib_handle = handle;
profile->magic = PROF_MAGIC_PROFILE;
*ret_profile = profile;
return 0;
}
static errcode_t
parse_modspec(const char *modspec, char **ret_path, char **ret_residual)
{
const char *p;
char *path, *fullpath, *residual;
errcode_t ret;
*ret_path = *ret_residual = NULL;
p = (*modspec != '\0' && modspec[1] == ':') ? modspec + 2 : modspec;
p = strchr(p, ':');
if (p == NULL)
return PROF_MODULE_SYNTAX;
path = malloc(p - modspec + 1);
if (path == NULL)
return ENOMEM;
memcpy(path, modspec, p - modspec);
path[p - modspec] = '\0';
ret = k5_path_join(LIBDIR, path, &fullpath);
free(path);
if (ret)
return ret;
residual = strdup(p + 1);
if (residual == NULL) {
free(fullpath);
return ENOMEM;
}
*ret_path = fullpath;
*ret_residual = residual;
return 0;
}
static errcode_t
init_load_module(const char *modspec, profile_t *ret_profile)
{
char *modpath = NULL, *residual = NULL;
struct errinfo einfo = { 0 };
prf_lib_handle_t lib_handle = NULL;
struct plugin_file_handle *plhandle = NULL;
void *cbdata = NULL, (*fptr)(void);
int have_lock = 0, have_cbdata = 0;
struct profile_vtable vtable = { 1 };
errcode_t err;
profile_module_init_fn initfn;
err = parse_modspec(modspec, &modpath, &residual);
if (err)
goto cleanup;
lib_handle = malloc(sizeof(*lib_handle));
if (lib_handle == NULL)
goto cleanup;
err = k5_mutex_init(&lib_handle->lock);
if (err)
goto cleanup;
have_lock = 1;
err = krb5int_open_plugin(modpath, &plhandle, &einfo);
if (err)
goto cleanup;
err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,
&einfo);
if (err == ENOENT)
err = PROF_MODULE_INVALID;
if (err)
goto cleanup;
initfn = (profile_module_init_fn)fptr;
err = (*initfn)(residual, &vtable, &cbdata);
if (err)
goto cleanup;
have_cbdata = 1;
lib_handle->plugin_handle = plhandle;
lib_handle->refcount = 1;
err = init_module(&vtable, cbdata, lib_handle, ret_profile);
cleanup:
free(modpath);
free(residual);
k5_clear_error(&einfo);
if (err) {
if (have_cbdata && vtable.cleanup)
vtable.cleanup(cbdata);
if (have_lock)
k5_mutex_destroy(&lib_handle->lock);
free(lib_handle);
if (plhandle)
krb5int_close_plugin(plhandle);
}
return err;
}
errcode_t KRB5_CALLCONV
profile_init_flags(const_profile_filespec_t *files, int flags,
profile_t *ret_profile)
{
const_profile_filespec_t *fs;
profile_t profile;
prf_file_t new_file, last = 0;
errcode_t retval = 0, access_retval = 0;
char *modspec = NULL, **modspec_arg;
profile = malloc(sizeof(struct _profile_t));
if (!profile)
return ENOMEM;
memset(profile, 0, sizeof(struct _profile_t));
profile->magic = PROF_MAGIC_PROFILE;
if ( files && !PROFILE_LAST_FILESPEC(*files) ) {
for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
modspec_arg = ((flags & PROFILE_INIT_ALLOW_MODULE) && !last) ?
&modspec : NULL;
retval = profile_open_file(*fs, &new_file, modspec_arg);
if (retval == PROF_MODULE && modspec) {
free(profile);
retval = init_load_module(modspec, ret_profile);
free(modspec);
return retval;
}
if (retval == ENOENT) {
continue;
}
if (retval == EACCES || retval == EPERM) {
access_retval = retval;
continue;
}
if (retval) {
profile_release(profile);
return retval;
}
if (last)
last->next = new_file;
else
profile->first_file = new_file;
last = new_file;
}
if (!last) {
profile_release(profile);
return access_retval ? access_retval : ENOENT;
}
}
*ret_profile = profile;
return 0;
}
errcode_t KRB5_CALLCONV
profile_init(const_profile_filespec_t *files, profile_t *ret_profile)
{
return profile_init_flags(files, 0, ret_profile);
}
errcode_t KRB5_CALLCONV
profile_init_vtable(struct profile_vtable *vtable, void *cbdata,
profile_t *ret_profile)
{
return init_module(vtable, cbdata, NULL, ret_profile);
}
static errcode_t
copy_vtable_profile(profile_t profile, profile_t *ret_new_profile)
{
errcode_t err;
void *cbdata;
profile_t new_profile;
*ret_new_profile = NULL;
if (profile->vt->copy) {
err = profile->vt->copy(profile->cbdata, &cbdata);
if (err)
return err;
err = init_module(profile->vt, cbdata, profile->lib_handle,
&new_profile);
if (err && profile->vt->cleanup)
profile->vt->cleanup(cbdata);
} else {
err = init_module(profile->vt, profile->cbdata, profile->lib_handle,
&new_profile);
}
if (err)
return err;
if (profile->lib_handle) {
k5_mutex_lock(&profile->lib_handle->lock);
profile->lib_handle->refcount++;
k5_mutex_unlock(&profile->lib_handle->lock);
}
*ret_new_profile = new_profile;
return 0;
}
#define COUNT_LINKED_LIST(COUNT, PTYPE, START, FIELD) \
{ \
size_t cll_counter = 0; \
PTYPE cll_ptr = (START); \
while (cll_ptr != NULL) { \
cll_counter++; \
cll_ptr = cll_ptr->FIELD; \
} \
(COUNT) = cll_counter; \
}
errcode_t KRB5_CALLCONV
profile_copy(profile_t old_profile, profile_t *new_profile)
{
profile_t profile;
prf_file_t p, q, *nextp;
*new_profile = NULL;
if (old_profile->vt)
return copy_vtable_profile(old_profile, new_profile);
profile = calloc(1, sizeof(*profile));
if (profile == NULL)
return ENOMEM;
profile->magic = PROF_MAGIC_PROFILE;
nextp = &profile->first_file;
for (p = old_profile->first_file; p != NULL; p = p->next) {
q = profile_copy_file(p);
if (q == NULL) {
profile_abandon(profile);
return ENOMEM;
}
*nextp = q;
nextp = &q->next;
}
*new_profile = profile;
return 0;
}
errcode_t KRB5_CALLCONV
profile_init_path(const_profile_filespec_list_t filepath,
profile_t *ret_profile)
{
unsigned int n_entries;
size_t i;
unsigned int ent_len;
const char *s, *t;
profile_filespec_t *filenames;
errcode_t retval;
for(s = filepath, n_entries = 1; *s; s++) {
if (*s == ':')
n_entries++;
}
filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*));
if (filenames == 0)
return ENOMEM;
for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
ent_len = (unsigned int) (t-s);
filenames[i] = (char*) malloc(ent_len + 1);
if (filenames[i] == 0) {
while (i > 0)
free(filenames[--i]);
free(filenames);
return ENOMEM;
}
strncpy(filenames[i], s, ent_len);
filenames[i][ent_len] = 0;
if (*t == 0) {
i++;
break;
}
}
filenames[i] = 0;
retval = profile_init_flags((const_profile_filespec_t *) filenames, 0,
ret_profile);
while (i > 0)
free(filenames[--i]);
free(filenames);
return retval;
}
errcode_t KRB5_CALLCONV
profile_is_writable(profile_t profile, int *writable)
{
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return PROF_MAGIC_PROFILE;
if (!writable)
return EINVAL;
*writable = 0;
if (profile->vt) {
if (profile->vt->writable)
return profile->vt->writable(profile->cbdata, writable);
else
return 0;
}
if (profile->first_file)
*writable = profile_file_is_writable(profile->first_file);
return 0;
}
errcode_t KRB5_CALLCONV
profile_is_modified(profile_t profile, int *modified)
{
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return PROF_MAGIC_PROFILE;
if (!modified)
return EINVAL;
*modified = 0;
if (profile->vt) {
if (profile->vt->modified)
return profile->vt->modified(profile->cbdata, modified);
else
return 0;
}
if (profile->first_file)
*modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY);
return 0;
}
errcode_t KRB5_CALLCONV
profile_flush(profile_t profile)
{
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return PROF_MAGIC_PROFILE;
if (profile->vt) {
if (profile->vt->flush)
return profile->vt->flush(profile->cbdata);
return 0;
}
if (profile->first_file)
return profile_flush_file(profile->first_file);
return 0;
}
errcode_t KRB5_CALLCONV
profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile)
{
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return PROF_MAGIC_PROFILE;
if (profile->vt)
return PROF_UNSUPPORTED;
if (profile->first_file)
return profile_flush_file_to_file(profile->first_file,
outfile);
return 0;
}
errcode_t KRB5_CALLCONV
profile_flush_to_buffer(profile_t profile, char **buf)
{
if (profile->vt)
return PROF_UNSUPPORTED;
return profile_flush_file_data_to_buffer(profile->first_file->data, buf);
}
void KRB5_CALLCONV
profile_free_buffer(profile_t profile, char *buf)
{
free(buf);
}
void KRB5_CALLCONV
profile_abandon(profile_t profile)
{
prf_file_t p, next;
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return;
if (profile->vt) {
if (profile->vt->cleanup)
profile->vt->cleanup(profile->cbdata);
if (profile->lib_handle) {
k5_mutex_lock(&profile->lib_handle->lock);
if (--profile->lib_handle->refcount == 0) {
krb5int_close_plugin(profile->lib_handle->plugin_handle);
k5_mutex_unlock(&profile->lib_handle->lock);
k5_mutex_destroy(&profile->lib_handle->lock);
free(profile->lib_handle);
} else
k5_mutex_unlock(&profile->lib_handle->lock);
}
free(profile->vt);
} else {
for (p = profile->first_file; p; p = next) {
next = p->next;
profile_free_file(p);
}
}
profile->magic = 0;
free(profile);
}
void KRB5_CALLCONV
profile_release(profile_t profile)
{
prf_file_t p, next;
if (!profile || profile->magic != PROF_MAGIC_PROFILE)
return;
if (profile->vt) {
if (profile->vt->flush)
profile->vt->flush(profile->cbdata);
profile_abandon(profile);
return;
} else {
for (p = profile->first_file; p; p = next) {
next = p->next;
profile_close_file(p);
}
}
profile->magic = 0;
free(profile);
}
errcode_t profile_ser_size(profile_t profile, size_t *sizep)
{
size_t required;
prf_file_t pfp;
required = 3*sizeof(int32_t);
for (pfp = profile->first_file; pfp; pfp = pfp->next) {
required += sizeof(int32_t);
required += strlen(pfp->data->filespec);
}
*sizep += required;
return 0;
}
static void pack_int32(int32_t oval, unsigned char **bufpp, size_t *remainp)
{
store_32_be(oval, *bufpp);
*bufpp += sizeof(int32_t);
*remainp -= sizeof(int32_t);
}
errcode_t profile_ser_externalize(profile_t profile,
unsigned char **bufpp, size_t *remainp)
{
errcode_t retval;
size_t required;
unsigned char *bp;
size_t remain;
prf_file_t pfp;
int32_t fcount, slen;
required = 0;
bp = *bufpp;
remain = *remainp;
retval = EINVAL;
if (profile) {
retval = ENOMEM;
(void) profile_ser_size(profile, &required);
if (required <= remain) {
fcount = 0;
for (pfp = profile->first_file; pfp; pfp = pfp->next)
fcount++;
pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);
pack_int32(fcount, &bp, &remain);
for (pfp = profile->first_file; pfp; pfp = pfp->next) {
slen = (int32_t) strlen(pfp->data->filespec);
pack_int32(slen, &bp, &remain);
if (slen) {
memcpy(bp, pfp->data->filespec, (size_t) slen);
bp += slen;
remain -= (size_t) slen;
}
}
pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);
retval = 0;
*bufpp = bp;
*remainp = remain;
}
}
return(retval);
}
static int unpack_int32(int32_t *intp, unsigned char **bufpp,
size_t *remainp)
{
if (*remainp >= sizeof(int32_t)) {
*intp = load_32_be(*bufpp);
*bufpp += sizeof(int32_t);
*remainp -= sizeof(int32_t);
return 0;
}
else
return 1;
}
errcode_t profile_ser_internalize(profile_t *profilep,
unsigned char **bufpp, size_t *remainp)
{
errcode_t retval;
unsigned char *bp;
size_t remain;
int i;
int32_t fcount, tmp;
profile_filespec_t *flist = 0;
bp = *bufpp;
remain = *remainp;
fcount = 0;
if (remain >= 12)
(void) unpack_int32(&tmp, &bp, &remain);
else
tmp = 0;
if (tmp != PROF_MAGIC_PROFILE) {
retval = EINVAL;
goto cleanup;
}
(void) unpack_int32(&fcount, &bp, &remain);
retval = ENOMEM;
flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (size_t) (fcount + 1));
if (!flist)
goto cleanup;
memset(flist, 0, sizeof(char *) * (size_t) (fcount+1));
for (i=0; i<fcount; i++) {
if (!unpack_int32(&tmp, &bp, &remain)) {
flist[i] = (char *) malloc((size_t) (tmp+1));
if (!flist[i])
goto cleanup;
memcpy(flist[i], bp, (size_t) tmp);
flist[i][tmp] = '\0';
bp += tmp;
remain -= (size_t) tmp;
}
}
if (unpack_int32(&tmp, &bp, &remain) ||
(tmp != PROF_MAGIC_PROFILE)) {
retval = EINVAL;
goto cleanup;
}
if ((retval = profile_init((const_profile_filespec_t *) flist,
profilep)))
goto cleanup;
*bufpp = bp;
*remainp = remain;
cleanup:
if (flist) {
for (i=0; i<fcount; i++) {
if (flist[i])
free(flist[i]);
}
free(flist);
}
return(retval);
}