#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include "err_local.h"
struct conf_module_st {
char *name;
conf_init_func *init;
conf_finish_func *finish;
int links;
};
struct conf_imodule_st {
CONF_MODULE *mod;
char *value;
};
static STACK_OF(CONF_MODULE) *supported_modules = NULL;
static STACK_OF(CONF_IMODULE) *initialized_modules = NULL;
static void module_free(CONF_MODULE *mod);
static void imodule_free(CONF_IMODULE *imod);
static void module_finish(CONF_IMODULE *imod);
static int module_run(const CONF *cnf, char *name, char *value,
unsigned long flags);
static int module_add(const char *name, conf_init_func *ifunc,
conf_finish_func *ffunc);
static CONF_MODULE *module_find(char *name);
static int module_init(CONF_MODULE *mod, char *name, char *value,
const CONF *cnf);
int
CONF_modules_load(const CONF *cnf, const char *appname, unsigned long flags)
{
STACK_OF(CONF_VALUE) *values;
CONF_VALUE *vl;
char *vsection = NULL;
int ret, i;
if (!cnf)
return 1;
if (appname)
vsection = NCONF_get_string(cnf, NULL, appname);
if (!appname || (!vsection && (flags & CONF_MFLAGS_DEFAULT_SECTION)))
vsection = NCONF_get_string(cnf, NULL, "openssl_conf");
if (!vsection) {
ERR_clear_error();
return 1;
}
values = NCONF_get_section(cnf, vsection);
if (!values)
return 0;
for (i = 0; i < sk_CONF_VALUE_num(values); i++) {
vl = sk_CONF_VALUE_value(values, i);
ret = module_run(cnf, vl->name, vl->value, flags);
if (ret <= 0)
if (!(flags & CONF_MFLAGS_IGNORE_ERRORS))
return ret;
}
return 1;
}
LCRYPTO_ALIAS(CONF_modules_load);
int
CONF_modules_load_file(const char *filename, const char *appname,
unsigned long flags)
{
char *file = NULL;
CONF *conf = NULL;
int ret = 0;
conf = NCONF_new(NULL);
if (!conf)
goto err;
if (filename == NULL) {
file = CONF_get1_default_config_file();
if (!file)
goto err;
} else
file = (char *)filename;
if (NCONF_load(conf, file, NULL) <= 0) {
if ((flags & CONF_MFLAGS_IGNORE_MISSING_FILE) &&
(ERR_GET_REASON(ERR_peek_last_error()) ==
CONF_R_NO_SUCH_FILE)) {
ERR_clear_error();
ret = 1;
}
goto err;
}
ret = CONF_modules_load(conf, appname, flags);
err:
if (filename == NULL)
free(file);
NCONF_free(conf);
return ret;
}
LCRYPTO_ALIAS(CONF_modules_load_file);
static int
module_run(const CONF *cnf, char *name, char *value, unsigned long flags)
{
CONF_MODULE *mod;
int ret;
if ((mod = module_find(name)) == NULL) {
if (!(flags & CONF_MFLAGS_SILENT)) {
CONFerror(CONF_R_UNKNOWN_MODULE_NAME);
ERR_asprintf_error_data("module=%s", name);
}
return -1;
}
ret = module_init(mod, name, value, cnf);
if (ret <= 0) {
if (!(flags & CONF_MFLAGS_SILENT)) {
CONFerror(CONF_R_MODULE_INITIALIZATION_ERROR);
ERR_asprintf_error_data
("module=%s, value=%s, retcode=%-8d",
name, value, ret);
}
}
return ret;
}
static int
module_add(const char *name, conf_init_func *ifunc, conf_finish_func *ffunc)
{
CONF_MODULE *mod = NULL;
int ret = 0;
if (name == NULL)
goto err;
if (supported_modules == NULL)
supported_modules = sk_CONF_MODULE_new_null();
if (supported_modules == NULL)
goto err;
if ((mod = calloc(1, sizeof(*mod))) == NULL)
goto err;
if ((mod->name = strdup(name)) == NULL)
goto err;
mod->init = ifunc;
mod->finish = ffunc;
if (!sk_CONF_MODULE_push(supported_modules, mod))
goto err;
mod = NULL;
ret = 1;
err:
module_free(mod);
return ret;
}
static CONF_MODULE *
module_find(char *name)
{
CONF_MODULE *mod;
int i, nchar;
char *p;
p = strrchr(name, '.');
if (p)
nchar = p - name;
else
nchar = strlen(name);
for (i = 0; i < sk_CONF_MODULE_num(supported_modules); i++) {
mod = sk_CONF_MODULE_value(supported_modules, i);
if (!strncmp(mod->name, name, nchar))
return mod;
}
return NULL;
}
static int
module_init(CONF_MODULE *mod, char *name, char *value, const CONF *cnf)
{
CONF_IMODULE *imod = NULL;
int need_finish = 0;
int ret = -1;
if (name == NULL || value == NULL)
goto err;
if ((imod = calloc(1, sizeof(*imod))) == NULL)
goto err;
imod->mod = mod;
if ((imod->value = strdup(value)) == NULL)
goto err;
if (mod->init != NULL) {
need_finish = 1;
if (mod->init(imod, cnf) <= 0)
goto err;
}
if (initialized_modules == NULL)
initialized_modules = sk_CONF_IMODULE_new_null();
if (initialized_modules == NULL)
goto err;
if (!sk_CONF_IMODULE_push(initialized_modules, imod))
goto err;
imod = NULL;
need_finish = 0;
mod->links++;
ret = 1;
err:
if (need_finish && mod->finish != NULL)
mod->finish(imod);
imodule_free(imod);
return ret;
}
void
CONF_modules_unload(int all)
{
int i;
CONF_MODULE *mod;
CONF_modules_finish();
for (i = sk_CONF_MODULE_num(supported_modules) - 1; i >= 0; i--) {
mod = sk_CONF_MODULE_value(supported_modules, i);
if (!all)
continue;
(void)sk_CONF_MODULE_delete(supported_modules, i);
module_free(mod);
}
if (sk_CONF_MODULE_num(supported_modules) == 0) {
sk_CONF_MODULE_free(supported_modules);
supported_modules = NULL;
}
}
LCRYPTO_ALIAS(CONF_modules_unload);
static void
module_free(CONF_MODULE *mod)
{
if (mod == NULL)
return;
free(mod->name);
free(mod);
}
static void
imodule_free(CONF_IMODULE *imod)
{
if (imod == NULL)
return;
free(imod->value);
free(imod);
}
void
CONF_modules_finish(void)
{
CONF_IMODULE *imod;
while (sk_CONF_IMODULE_num(initialized_modules) > 0) {
imod = sk_CONF_IMODULE_pop(initialized_modules);
module_finish(imod);
}
sk_CONF_IMODULE_free(initialized_modules);
initialized_modules = NULL;
}
LCRYPTO_ALIAS(CONF_modules_finish);
static void
module_finish(CONF_IMODULE *imod)
{
if (imod->mod->finish)
imod->mod->finish(imod);
imod->mod->links--;
imodule_free(imod);
}
int
CONF_module_add(const char *name, conf_init_func *ifunc, conf_finish_func *ffunc)
{
return module_add(name, ifunc, ffunc);
}
void
CONF_modules_free(void)
{
CONF_modules_finish();
CONF_modules_unload(1);
}
LCRYPTO_ALIAS(CONF_modules_free);
const char *
CONF_imodule_get_value(const CONF_IMODULE *imod)
{
return imod->value;
}
char *
CONF_get1_default_config_file(void)
{
char *file = NULL;
if (asprintf(&file, "%s/openssl.cnf",
X509_get_default_cert_area()) == -1)
return (NULL);
return file;
}
LCRYPTO_ALIAS(CONF_get1_default_config_file);
int
CONF_parse_list(const char *list_, int sep, int nospc,
int (*list_cb)(const char *elem, int len, void *usr), void *arg)
{
int ret;
const char *lstart, *tmpend, *p;
if (list_ == NULL) {
CONFerror(CONF_R_LIST_CANNOT_BE_NULL);
return 0;
}
lstart = list_;
for (;;) {
if (nospc) {
while (*lstart && isspace((unsigned char)*lstart))
lstart++;
}
p = strchr(lstart, sep);
if (p == lstart || !*lstart)
ret = list_cb(NULL, 0, arg);
else {
if (p)
tmpend = p - 1;
else
tmpend = lstart + strlen(lstart) - 1;
if (nospc) {
while (isspace((unsigned char)*tmpend))
tmpend--;
}
ret = list_cb(lstart, tmpend - lstart + 1, arg);
}
if (ret <= 0)
return ret;
if (p == NULL)
return 1;
lstart = p + 1;
}
}