Path: blob/main/crypto/krb5/src/util/profile/prof_init.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* prof_init.c --- routines that manipulate the user-visible profile_t3* object.4*/56#include "prof_int.h"78#include <stdio.h>9#include <string.h>10#ifdef HAVE_STDLIB_H11#include <stdlib.h>12#endif13#include <errno.h>1415/* Create a vtable profile, possibly with a library handle. The new profile16* takes ownership of the handle refcount on success. */17static errcode_t18init_module(struct profile_vtable *vtable, void *cbdata,19prf_lib_handle_t handle, profile_t *ret_profile)20{21profile_t profile;22struct profile_vtable *vt_copy;2324/* Check that the vtable's minor version is sane and that mandatory methods25* are implemented. */26if (vtable->minor_ver < 1 || !vtable->get_values || !vtable->free_values)27return EINVAL;28if (vtable->cleanup && !vtable->copy)29return EINVAL;30if (vtable->iterator_create &&31(!vtable->iterator || !vtable->iterator_free || !vtable->free_string))32return EINVAL;3334profile = malloc(sizeof(*profile));35if (!profile)36return ENOMEM;37memset(profile, 0, sizeof(*profile));3839vt_copy = malloc(sizeof(*vt_copy));40if (!vt_copy) {41free(profile);42return ENOMEM;43}44/* It's safe to just copy the caller's vtable for now. If the minor45* version is bumped, we'll need to copy individual fields. */46*vt_copy = *vtable;4748profile->vt = vt_copy;49profile->cbdata = cbdata;50profile->lib_handle = handle;51profile->magic = PROF_MAGIC_PROFILE;52*ret_profile = profile;53return 0;54}5556/* Parse modspec into the module path and residual string. */57static errcode_t58parse_modspec(const char *modspec, char **ret_path, char **ret_residual)59{60const char *p;61char *path, *fullpath, *residual;62errcode_t ret;6364*ret_path = *ret_residual = NULL;6566/* Find the separator, skipping a Windows drive letter if present. */67p = (*modspec != '\0' && modspec[1] == ':') ? modspec + 2 : modspec;68p = strchr(p, ':');69if (p == NULL)70return PROF_MODULE_SYNTAX;7172/* Copy the path. */73path = malloc(p - modspec + 1);74if (path == NULL)75return ENOMEM;76memcpy(path, modspec, p - modspec);77path[p - modspec] = '\0';7879/* Compose the path with LIBDIR if it's not absolute. */80ret = k5_path_join(LIBDIR, path, &fullpath);81free(path);82if (ret)83return ret;8485residual = strdup(p + 1);86if (residual == NULL) {87free(fullpath);88return ENOMEM;89}9091*ret_path = fullpath;92*ret_residual = residual;93return 0;94}9596/* Load a dynamic profile module as specified by modspec and create a vtable97* profile for it in *ret_profile. */98static errcode_t99init_load_module(const char *modspec, profile_t *ret_profile)100{101char *modpath = NULL, *residual = NULL;102struct errinfo einfo = { 0 };103prf_lib_handle_t lib_handle = NULL;104struct plugin_file_handle *plhandle = NULL;105void *cbdata = NULL, (*fptr)(void);106int have_lock = 0, have_cbdata = 0;107struct profile_vtable vtable = { 1 }; /* Set minor_ver to 1, rest null. */108errcode_t err;109profile_module_init_fn initfn;110111err = parse_modspec(modspec, &modpath, &residual);112if (err)113goto cleanup;114115/* Allocate a reference-counted library handle container. */116lib_handle = malloc(sizeof(*lib_handle));117if (lib_handle == NULL)118goto cleanup;119err = k5_mutex_init(&lib_handle->lock);120if (err)121goto cleanup;122have_lock = 1;123124/* Open the module and get its initializer. */125err = krb5int_open_plugin(modpath, &plhandle, &einfo);126if (err)127goto cleanup;128err = krb5int_get_plugin_func(plhandle, "profile_module_init", &fptr,129&einfo);130if (err == ENOENT)131err = PROF_MODULE_INVALID;132if (err)133goto cleanup;134135/* Get the profile vtable and callback data pointer. */136initfn = (profile_module_init_fn)fptr;137err = (*initfn)(residual, &vtable, &cbdata);138if (err)139goto cleanup;140have_cbdata = 1;141142/* Create a vtable profile with the information obtained. */143lib_handle->plugin_handle = plhandle;144lib_handle->refcount = 1;145err = init_module(&vtable, cbdata, lib_handle, ret_profile);146147cleanup:148free(modpath);149free(residual);150k5_clear_error(&einfo);151if (err) {152if (have_cbdata && vtable.cleanup)153vtable.cleanup(cbdata);154if (have_lock)155k5_mutex_destroy(&lib_handle->lock);156free(lib_handle);157if (plhandle)158krb5int_close_plugin(plhandle);159}160return err;161}162163errcode_t KRB5_CALLCONV164profile_init_flags(const_profile_filespec_t *files, int flags,165profile_t *ret_profile)166{167const_profile_filespec_t *fs;168profile_t profile;169prf_file_t new_file, last = 0;170errcode_t retval = 0, access_retval = 0;171char *modspec = NULL, **modspec_arg;172173profile = malloc(sizeof(struct _profile_t));174if (!profile)175return ENOMEM;176memset(profile, 0, sizeof(struct _profile_t));177profile->magic = PROF_MAGIC_PROFILE;178179/*180* If the filenames list is not specified or empty, return an empty181* profile.182*/183if ( files && !PROFILE_LAST_FILESPEC(*files) ) {184for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {185/* Allow a module declaration if it is permitted by flags and this186* is the first file parsed. */187modspec_arg = ((flags & PROFILE_INIT_ALLOW_MODULE) && !last) ?188&modspec : NULL;189retval = profile_open_file(*fs, &new_file, modspec_arg);190if (retval == PROF_MODULE && modspec) {191/* Stop parsing files and load a dynamic module instead. */192free(profile);193retval = init_load_module(modspec, ret_profile);194free(modspec);195return retval;196}197/* if this file is missing, skip to the next */198if (retval == ENOENT) {199continue;200}201/* If we can't read this file, remember it but keep going. */202if (retval == EACCES || retval == EPERM) {203access_retval = retval;204continue;205}206if (retval) {207profile_release(profile);208return retval;209}210if (last)211last->next = new_file;212else213profile->first_file = new_file;214last = new_file;215}216/*217* If last is still null after the loop, then all the files were218* missing or unreadable, so return the appropriate error.219*/220if (!last) {221profile_release(profile);222return access_retval ? access_retval : ENOENT;223}224}225226*ret_profile = profile;227return 0;228}229230errcode_t KRB5_CALLCONV231profile_init(const_profile_filespec_t *files, profile_t *ret_profile)232{233return profile_init_flags(files, 0, ret_profile);234}235236errcode_t KRB5_CALLCONV237profile_init_vtable(struct profile_vtable *vtable, void *cbdata,238profile_t *ret_profile)239{240return init_module(vtable, cbdata, NULL, ret_profile);241}242243/* Copy a vtable profile. */244static errcode_t245copy_vtable_profile(profile_t profile, profile_t *ret_new_profile)246{247errcode_t err;248void *cbdata;249profile_t new_profile;250251*ret_new_profile = NULL;252253if (profile->vt->copy) {254/* Make a copy of profile's cbdata for the new profile. */255err = profile->vt->copy(profile->cbdata, &cbdata);256if (err)257return err;258err = init_module(profile->vt, cbdata, profile->lib_handle,259&new_profile);260if (err && profile->vt->cleanup)261profile->vt->cleanup(cbdata);262} else {263/* Use the same cbdata as the old profile. */264err = init_module(profile->vt, profile->cbdata, profile->lib_handle,265&new_profile);266}267if (err)268return err;269270/* Increment the refcount on the library handle if there is one. */271if (profile->lib_handle) {272k5_mutex_lock(&profile->lib_handle->lock);273profile->lib_handle->refcount++;274k5_mutex_unlock(&profile->lib_handle->lock);275}276277*ret_new_profile = new_profile;278return 0;279}280281#define COUNT_LINKED_LIST(COUNT, PTYPE, START, FIELD) \282{ \283size_t cll_counter = 0; \284PTYPE cll_ptr = (START); \285while (cll_ptr != NULL) { \286cll_counter++; \287cll_ptr = cll_ptr->FIELD; \288} \289(COUNT) = cll_counter; \290}291292errcode_t KRB5_CALLCONV293profile_copy(profile_t old_profile, profile_t *new_profile)294{295profile_t profile;296prf_file_t p, q, *nextp;297298*new_profile = NULL;299300if (old_profile->vt)301return copy_vtable_profile(old_profile, new_profile);302303profile = calloc(1, sizeof(*profile));304if (profile == NULL)305return ENOMEM;306profile->magic = PROF_MAGIC_PROFILE;307308nextp = &profile->first_file;309for (p = old_profile->first_file; p != NULL; p = p->next) {310q = profile_copy_file(p);311if (q == NULL) {312profile_abandon(profile);313return ENOMEM;314}315*nextp = q;316nextp = &q->next;317}318319*new_profile = profile;320return 0;321}322323errcode_t KRB5_CALLCONV324profile_init_path(const_profile_filespec_list_t filepath,325profile_t *ret_profile)326{327unsigned int n_entries;328size_t i;329unsigned int ent_len;330const char *s, *t;331profile_filespec_t *filenames;332errcode_t retval;333334/* count the distinct filename components */335for(s = filepath, n_entries = 1; *s; s++) {336if (*s == ':')337n_entries++;338}339340/* the array is NULL terminated */341filenames = (profile_filespec_t*) malloc((n_entries+1) * sizeof(char*));342if (filenames == 0)343return ENOMEM;344345/* measure, copy, and skip each one */346for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {347ent_len = (unsigned int) (t-s);348filenames[i] = (char*) malloc(ent_len + 1);349if (filenames[i] == 0) {350/* if malloc fails, free the ones that worked */351while (i > 0)352free(filenames[--i]);353free(filenames);354return ENOMEM;355}356strncpy(filenames[i], s, ent_len);357filenames[i][ent_len] = 0;358if (*t == 0) {359i++;360break;361}362}363/* cap the array */364filenames[i] = 0;365366retval = profile_init_flags((const_profile_filespec_t *) filenames, 0,367ret_profile);368369/* count back down and free the entries */370while (i > 0)371free(filenames[--i]);372free(filenames);373374return retval;375}376377errcode_t KRB5_CALLCONV378profile_is_writable(profile_t profile, int *writable)379{380if (!profile || profile->magic != PROF_MAGIC_PROFILE)381return PROF_MAGIC_PROFILE;382383if (!writable)384return EINVAL;385*writable = 0;386387if (profile->vt) {388if (profile->vt->writable)389return profile->vt->writable(profile->cbdata, writable);390else391return 0;392}393394if (profile->first_file)395*writable = profile_file_is_writable(profile->first_file);396397return 0;398}399400errcode_t KRB5_CALLCONV401profile_is_modified(profile_t profile, int *modified)402{403if (!profile || profile->magic != PROF_MAGIC_PROFILE)404return PROF_MAGIC_PROFILE;405406if (!modified)407return EINVAL;408*modified = 0;409410if (profile->vt) {411if (profile->vt->modified)412return profile->vt->modified(profile->cbdata, modified);413else414return 0;415}416417if (profile->first_file)418*modified = (profile->first_file->data->flags & PROFILE_FILE_DIRTY);419420return 0;421}422423errcode_t KRB5_CALLCONV424profile_flush(profile_t profile)425{426if (!profile || profile->magic != PROF_MAGIC_PROFILE)427return PROF_MAGIC_PROFILE;428429if (profile->vt) {430if (profile->vt->flush)431return profile->vt->flush(profile->cbdata);432return 0;433}434435if (profile->first_file)436return profile_flush_file(profile->first_file);437438return 0;439}440441errcode_t KRB5_CALLCONV442profile_flush_to_file(profile_t profile, const_profile_filespec_t outfile)443{444if (!profile || profile->magic != PROF_MAGIC_PROFILE)445return PROF_MAGIC_PROFILE;446447if (profile->vt)448return PROF_UNSUPPORTED;449450if (profile->first_file)451return profile_flush_file_to_file(profile->first_file,452outfile);453454return 0;455}456457errcode_t KRB5_CALLCONV458profile_flush_to_buffer(profile_t profile, char **buf)459{460if (profile->vt)461return PROF_UNSUPPORTED;462return profile_flush_file_data_to_buffer(profile->first_file->data, buf);463}464465void KRB5_CALLCONV466profile_free_buffer(profile_t profile, char *buf)467{468free(buf);469}470471void KRB5_CALLCONV472profile_abandon(profile_t profile)473{474prf_file_t p, next;475476if (!profile || profile->magic != PROF_MAGIC_PROFILE)477return;478479if (profile->vt) {480if (profile->vt->cleanup)481profile->vt->cleanup(profile->cbdata);482if (profile->lib_handle) {483/* Decrement the refcount on the handle and maybe free it. */484k5_mutex_lock(&profile->lib_handle->lock);485if (--profile->lib_handle->refcount == 0) {486krb5int_close_plugin(profile->lib_handle->plugin_handle);487k5_mutex_unlock(&profile->lib_handle->lock);488k5_mutex_destroy(&profile->lib_handle->lock);489free(profile->lib_handle);490} else491k5_mutex_unlock(&profile->lib_handle->lock);492}493free(profile->vt);494} else {495for (p = profile->first_file; p; p = next) {496next = p->next;497profile_free_file(p);498}499}500profile->magic = 0;501free(profile);502}503504void KRB5_CALLCONV505profile_release(profile_t profile)506{507prf_file_t p, next;508509if (!profile || profile->magic != PROF_MAGIC_PROFILE)510return;511512if (profile->vt) {513/* Flush the profile and then delegate to profile_abandon. */514if (profile->vt->flush)515profile->vt->flush(profile->cbdata);516profile_abandon(profile);517return;518} else {519for (p = profile->first_file; p; p = next) {520next = p->next;521profile_close_file(p);522}523}524profile->magic = 0;525free(profile);526}527528/*529* Here begins the profile serialization functions.530*/531errcode_t profile_ser_size(profile_t profile, size_t *sizep)532{533size_t required;534prf_file_t pfp;535536required = 3*sizeof(int32_t);537for (pfp = profile->first_file; pfp; pfp = pfp->next) {538required += sizeof(int32_t);539required += strlen(pfp->data->filespec);540}541*sizep += required;542return 0;543}544545static void pack_int32(int32_t oval, unsigned char **bufpp, size_t *remainp)546{547store_32_be(oval, *bufpp);548*bufpp += sizeof(int32_t);549*remainp -= sizeof(int32_t);550}551552errcode_t profile_ser_externalize(profile_t profile,553unsigned char **bufpp, size_t *remainp)554{555errcode_t retval;556size_t required;557unsigned char *bp;558size_t remain;559prf_file_t pfp;560int32_t fcount, slen;561562required = 0;563bp = *bufpp;564remain = *remainp;565retval = EINVAL;566if (profile) {567retval = ENOMEM;568(void) profile_ser_size(profile, &required);569if (required <= remain) {570fcount = 0;571for (pfp = profile->first_file; pfp; pfp = pfp->next)572fcount++;573pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);574pack_int32(fcount, &bp, &remain);575for (pfp = profile->first_file; pfp; pfp = pfp->next) {576slen = (int32_t) strlen(pfp->data->filespec);577pack_int32(slen, &bp, &remain);578if (slen) {579memcpy(bp, pfp->data->filespec, (size_t) slen);580bp += slen;581remain -= (size_t) slen;582}583}584pack_int32(PROF_MAGIC_PROFILE, &bp, &remain);585retval = 0;586*bufpp = bp;587*remainp = remain;588}589}590return(retval);591}592593static int unpack_int32(int32_t *intp, unsigned char **bufpp,594size_t *remainp)595{596if (*remainp >= sizeof(int32_t)) {597*intp = load_32_be(*bufpp);598*bufpp += sizeof(int32_t);599*remainp -= sizeof(int32_t);600return 0;601}602else603return 1;604}605606errcode_t profile_ser_internalize(profile_t *profilep,607unsigned char **bufpp, size_t *remainp)608{609errcode_t retval;610unsigned char *bp;611size_t remain;612int i;613int32_t fcount, tmp;614profile_filespec_t *flist = 0;615616bp = *bufpp;617remain = *remainp;618fcount = 0;619620if (remain >= 12)621(void) unpack_int32(&tmp, &bp, &remain);622else623tmp = 0;624625if (tmp != PROF_MAGIC_PROFILE) {626retval = EINVAL;627goto cleanup;628}629630(void) unpack_int32(&fcount, &bp, &remain);631retval = ENOMEM;632633flist = (profile_filespec_t *) malloc(sizeof(profile_filespec_t) * (size_t) (fcount + 1));634if (!flist)635goto cleanup;636637memset(flist, 0, sizeof(char *) * (size_t) (fcount+1));638for (i=0; i<fcount; i++) {639if (!unpack_int32(&tmp, &bp, &remain)) {640flist[i] = (char *) malloc((size_t) (tmp+1));641if (!flist[i])642goto cleanup;643memcpy(flist[i], bp, (size_t) tmp);644flist[i][tmp] = '\0';645bp += tmp;646remain -= (size_t) tmp;647}648}649650if (unpack_int32(&tmp, &bp, &remain) ||651(tmp != PROF_MAGIC_PROFILE)) {652retval = EINVAL;653goto cleanup;654}655656if ((retval = profile_init((const_profile_filespec_t *) flist,657profilep)))658goto cleanup;659660*bufpp = bp;661*remainp = remain;662663cleanup:664if (flist) {665for (i=0; i<fcount; i++) {666if (flist[i])667free(flist[i]);668}669free(flist);670}671return(retval);672}673674675