Commit b524b808 authored by Tomas Mraz's avatar Tomas Mraz Committed by Matt Caswell
Browse files

Add support for .include directive in config files



Either files or directories of *.cnf or *.conf files
can be included.
Recursive inclusion of directories is not supported.

Reviewed-by: default avatarAndy Polyakov <appro@openssl.org>
Reviewed-by: default avatarMatt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5351)
parent aa2ed504
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3,6 +3,10 @@
# This is mostly being used for generation of certificate requests.
#

# Note that you can include other files from the main configuration
# file using the .include directive.
#.include filename

# This definition stops the following lines choking if HOME isn't
# defined.
HOME			= .
+4 −0
Original line number Diff line number Diff line
@@ -3,6 +3,10 @@
# This is mostly being used for generation of certificate requests.
#

# Note that you can include other files from the main configuration
# file using the .include directive.
#.include filename

# This definition stops the following lines choking if HOME isn't
# defined.
HOME			= .
+188 −14
Original line number Diff line number Diff line
/*
 * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
@@ -12,12 +12,20 @@
#include <stdio.h>
#include <string.h>
#include "internal/cryptlib.h"
#include "internal/o_dir.h"
#include <openssl/lhash.h>
#include <openssl/conf.h>
#include <openssl/conf_api.h>
#include "conf_def.h"
#include <openssl/buffer.h>
#include <openssl/err.h>
#ifndef OPENSSL_NO_POSIX_IO
# include <sys/stat.h>
# ifdef _WIN32
#  define stat    _stat
#  define strcasecmp _stricmp
# endif
#endif

/*
 * The maximum length we can grow a value to after variable expansion. 64k
@@ -26,12 +34,18 @@
#define MAX_CONF_VALUE_LENGTH       65536

static char *eat_ws(CONF *conf, char *p);
static void trim_ws(CONF *conf, char *start);
static char *eat_alpha_numeric(CONF *conf, char *p);
static void clear_comments(CONF *conf, char *p);
static int str_copy(CONF *conf, char *section, char **to, char *from);
static char *scan_quote(CONF *conf, char *p);
static char *scan_dquote(CONF *conf, char *p);
#define scan_esc(conf,p)        (((IS_EOF((conf),(p)[1]))?((p)+1):((p)+2)))
#ifndef OPENSSL_NO_POSIX_IO
static BIO *process_include(char *include, OPENSSL_DIR_CTX **dirctx,
                            char **dirpath);
static BIO *get_next_file(const char *path, OPENSSL_DIR_CTX **dirctx);
#endif

static CONF *def_create(CONF_METHOD *meth);
static int def_init_default(CONF *conf);
@@ -173,6 +187,11 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
    char *section = NULL, *buf;
    char *start, *psection, *pname;
    void *h = (void *)(conf->data);
    STACK_OF(BIO) *biosk = NULL;
#ifndef OPENSSL_NO_POSIX_IO
    char *dirpath = NULL;
    OPENSSL_DIR_CTX *dirctx = NULL;
#endif

    if ((buff = BUF_MEM_new()) == NULL) {
        CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB);
@@ -205,11 +224,39 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
        }
        p = &(buff->data[bufnum]);
        *p = '\0';
 read_retry:
        BIO_gets(in, p, CONFBUFSIZE - 1);
        p[CONFBUFSIZE - 1] = '\0';
        ii = i = strlen(p);
        if (i == 0 && !again)
        if (i == 0 && !again) {
            /* the currently processed BIO is at EOF */
            BIO *parent;

#ifndef OPENSSL_NO_POSIX_IO
            /* continue processing with the next file from directory */
            if (dirctx != NULL) {
                BIO *next;

                if ((next = get_next_file(dirpath, &dirctx)) != NULL) {
                    BIO_vfree(in);
                    in = next;
                    goto read_retry;
                } else {
                    OPENSSL_free(dirpath);
                    dirpath = NULL;
                }
            }
#endif
            /* no more files in directory, continue with processing parent */
            if ((parent = sk_BIO_pop(biosk)) == NULL) {
                /* everything processed get out of the loop */
                break;
            } else {
                BIO_vfree(in);
                in = parent;
                goto read_retry;
            }
        }
        again = 0;
        while (i > 0) {
            if ((p[i - 1] != '\r') && (p[i - 1] != '\n'))
@@ -285,7 +332,6 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
            continue;
        } else {
            pname = s;
            psection = NULL;
            end = eat_alpha_numeric(conf, s);
            if ((end[0] == ':') && (end[1] == ':')) {
                *end = '\0';
@@ -293,29 +339,57 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
                psection = pname;
                pname = end;
                end = eat_alpha_numeric(conf, end);
            } else {
                psection = section;
            }
            p = eat_ws(conf, end);
            if (*p != '=') {
            if (strncmp(pname, ".include", 8) == 0 && p != pname + 8) {
                char *include = NULL;
                BIO *next;

                trim_ws(conf, p);
                if (!str_copy(conf, psection, &include, p))
                    goto err;
                /* get the BIO of the included file */
#ifndef OPENSSL_NO_POSIX_IO
                next = process_include(include, &dirctx, &dirpath);
                if (include != dirpath) {
                    /* dirpath will contain include in case of a directory */
                    OPENSSL_free(include);
                }
#else
                next = BIO_new_file(include, "r");
                OPENSSL_free(include);
#endif
                if (next != NULL) {
                    /* push the currently processing BIO onto stack */
                    if (biosk == NULL) {
                        if ((biosk = sk_BIO_new_null()) == NULL) {
                            CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
                            goto err;
                        }
                    }
                    if (!sk_BIO_push(biosk, in)) {
                        CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
                        goto err;
                    }
                    /* continue with reading from the included BIO */
                    in = next;
                }
                continue;
            } else if (*p != '=') {
                CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_MISSING_EQUAL_SIGN);
                goto err;
            }
            *end = '\0';
            p++;
            start = eat_ws(conf, p);
            while (!IS_EOF(conf, *p))
                p++;
            p--;
            while ((p != start) && (IS_WS(conf, *p)))
                p--;
            p++;
            *p = '\0';
            trim_ws(conf, start);

            if ((v = OPENSSL_malloc(sizeof(*v))) == NULL) {
                CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
                goto err;
            }
            if (psection == NULL)
                psection = section;
            v->name = OPENSSL_strdup(pname);
            v->value = NULL;
            if (v->name == NULL) {
@@ -345,10 +419,17 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
    }
    BUF_MEM_free(buff);
    OPENSSL_free(section);
    sk_BIO_pop_free(biosk, BIO_vfree);
    return 1;
 err:
    BUF_MEM_free(buff);
    OPENSSL_free(section);
    sk_BIO_pop_free(biosk, BIO_vfree);
#ifndef OPENSSL_NO_POSIX_IO
    OPENSSL_free(dirpath);
    if (dirctx != NULL)
        OPENSSL_DIR_end(&dirctx);
#endif
    if (line != NULL)
        *line = eline;
    BIO_snprintf(btmp, sizeof(btmp), "%ld", eline);
@@ -555,6 +636,86 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
    return 0;
}

#ifndef OPENSSL_NO_POSIX_IO
/*
 * Check whether included path is a directory.
 * Returns next BIO to process and in case of a directory
 * also an opened directory context and the include path.
 */
static BIO *process_include(char *include, OPENSSL_DIR_CTX **dirctx,
                            char **dirpath)
{
    struct stat st = { 0 };
    BIO *next;

    if (stat(include, &st) < 0) {
        SYSerr(SYS_F_STAT, errno);
        ERR_add_error_data(1, include);
        /* missing include file is not fatal error */
        return NULL;
    }

    if ((st.st_mode & S_IFDIR) == S_IFDIR) {
        if (*dirctx != NULL) {
            CONFerr(CONF_F_PROCESS_INCLUDE,
                    CONF_R_RECURSIVE_DIRECTORY_INCLUDE);
            ERR_add_error_data(1, include);
            return NULL;
        }
        /* a directory, load its contents */
        if ((next = get_next_file(include, dirctx)) != NULL)
            *dirpath = include;
        return next;
    }

    next = BIO_new_file(include, "r");
    return next;
}

/*
 * Get next file from the directory path.
 * Returns BIO of the next file to read and updates dirctx.
 */
static BIO *get_next_file(const char *path, OPENSSL_DIR_CTX **dirctx)
{
    const char *filename;

    while ((filename = OPENSSL_DIR_read(dirctx, path)) != NULL) {
        size_t namelen;

        namelen = strlen(filename);

        if ((namelen > 5 && strcasecmp(filename + namelen - 5, ".conf") == 0)
            || (namelen > 4 && strcasecmp(filename + namelen - 4, ".cnf") == 0)) {
            size_t newlen;
            char *newpath;
            BIO *bio;

            newlen = strlen(path) + namelen + 2;
            newpath = OPENSSL_zalloc(newlen);
            if (newpath == NULL) {
                CONFerr(CONF_F_GET_NEXT_FILE, ERR_R_MALLOC_FAILURE);
                break;
            }
            OPENSSL_strlcat(newpath, path, newlen);
#ifndef OPENSSL_SYS_VMS
            OPENSSL_strlcat(newpath, "/", newlen);
#endif
            OPENSSL_strlcat(newpath, filename, newlen);

            bio = BIO_new_file(newpath, "r");
            OPENSSL_free(newpath);
            /* Errors when opening files are non-fatal. */
            if (bio != NULL)
                return bio;
        }
    }
    OPENSSL_DIR_end(dirctx);
    *dirctx = NULL;
    return NULL;
}
#endif

static char *eat_ws(CONF *conf, char *p)
{
    while (IS_WS(conf, *p) && (!IS_EOF(conf, *p)))
@@ -562,6 +723,19 @@ static char *eat_ws(CONF *conf, char *p)
    return p;
}

static void trim_ws(CONF *conf, char *start)
{
    char *p = start;

    while (!IS_EOF(conf, *p))
        p++;
    p--;
    while ((p >= start) && IS_WS(conf, *p))
        p--;
    p++;
    *p = '\0';
}

static char *eat_alpha_numeric(CONF *conf, char *p)
{
    for (;;) {
+5 −1
Original line number Diff line number Diff line
/*
 * Generated by util/mkerr.pl DO NOT EDIT
 * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
@@ -20,6 +20,7 @@ static const ERR_STRING_DATA CONF_str_functs[] = {
    {ERR_PACK(ERR_LIB_CONF, CONF_F_CONF_PARSE_LIST, 0), "CONF_parse_list"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_DEF_LOAD, 0), "def_load"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_DEF_LOAD_BIO, 0), "def_load_bio"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_GET_NEXT_FILE, 0), "get_next_file"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_MODULE_INIT, 0), "module_init"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_MODULE_LOAD_DSO, 0), "module_load_dso"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_MODULE_RUN, 0), "module_run"},
@@ -33,6 +34,7 @@ static const ERR_STRING_DATA CONF_str_functs[] = {
    {ERR_PACK(ERR_LIB_CONF, CONF_F_NCONF_LOAD_BIO, 0), "NCONF_load_bio"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_NCONF_LOAD_FP, 0), "NCONF_load_fp"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_NCONF_NEW, 0), "NCONF_new"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_PROCESS_INCLUDE, 0), "process_include"},
    {ERR_PACK(ERR_LIB_CONF, CONF_F_STR_COPY, 0), "str_copy"},
    {0, NULL}
};
@@ -56,6 +58,8 @@ static const ERR_STRING_DATA CONF_str_reasons[] = {
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_NO_SECTION), "no section"},
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_NO_SUCH_FILE), "no such file"},
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_NO_VALUE), "no value"},
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_RECURSIVE_DIRECTORY_INCLUDE),
    "recursive directory include"},
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_UNABLE_TO_CREATE_NEW_SECTION),
    "unable to create new section"},
    {ERR_PACK(ERR_LIB_CONF, 0, CONF_R_UNKNOWN_MODULE_NAME),
+3 −0
Original line number Diff line number Diff line
@@ -299,6 +299,7 @@ CONF_F_CONF_LOAD_FP:103:CONF_load_fp
CONF_F_CONF_PARSE_LIST:119:CONF_parse_list
CONF_F_DEF_LOAD:120:def_load
CONF_F_DEF_LOAD_BIO:121:def_load_bio
CONF_F_GET_NEXT_FILE:107:get_next_file
CONF_F_MODULE_INIT:115:module_init
CONF_F_MODULE_LOAD_DSO:117:module_load_dso
CONF_F_MODULE_RUN:118:module_run
@@ -311,6 +312,7 @@ CONF_F_NCONF_LOAD:113:NCONF_load
CONF_F_NCONF_LOAD_BIO:110:NCONF_load_bio
CONF_F_NCONF_LOAD_FP:114:NCONF_load_fp
CONF_F_NCONF_NEW:111:NCONF_new
CONF_F_PROCESS_INCLUDE:116:process_include
CONF_F_STR_COPY:101:str_copy
CRYPTO_F_CRYPTO_DUP_EX_DATA:110:CRYPTO_dup_ex_data
CRYPTO_F_CRYPTO_FREE_EX_DATA:111:CRYPTO_free_ex_data
@@ -1872,6 +1874,7 @@ CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE:106:no conf or environment variable
CONF_R_NO_SECTION:107:no section
CONF_R_NO_SUCH_FILE:114:no such file
CONF_R_NO_VALUE:108:no value
CONF_R_RECURSIVE_DIRECTORY_INCLUDE:111:recursive directory include
CONF_R_UNABLE_TO_CREATE_NEW_SECTION:103:unable to create new section
CONF_R_UNKNOWN_MODULE_NAME:113:unknown module name
CONF_R_VARIABLE_EXPANSION_TOO_LONG:116:variable expansion too long
Loading