summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Mraz <tmraz@fedoraproject.org>2018-02-27 13:55:35 +0100
committerMatt Caswell <matt@openssl.org>2018-03-05 13:32:40 +0000
commitb524b808a1d1ba204dbdcbb42de4e3bddb3472ac (patch)
tree4b183c1e0737609272adea1a608f4c3c0064f65b
parentaa2ed504ea9d55be6e75a9647d874709dc71823b (diff)
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: Andy Polyakov <appro@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/5351)
-rw-r--r--apps/openssl-vms.cnf4
-rw-r--r--apps/openssl.cnf4
-rw-r--r--crypto/conf/conf_def.c202
-rw-r--r--crypto/conf/conf_err.c6
-rw-r--r--crypto/err/openssl.txt3
-rw-r--r--doc/man5/config.pod18
-rw-r--r--include/openssl/conferr.h3
-rw-r--r--test/build.info5
-rw-r--r--test/conf_include_test.c161
-rw-r--r--test/recipes/90-test_includes.t17
-rw-r--r--test/recipes/90-test_includes_data/conf-includes/includes1.cnf36
-rw-r--r--test/recipes/90-test_includes_data/conf-includes/includes2.cnf53
-rw-r--r--test/recipes/90-test_includes_data/includes-broken.cnf5
-rw-r--r--test/recipes/90-test_includes_data/includes-file.cnf5
-rw-r--r--test/recipes/90-test_includes_data/includes.cnf5
15 files changed, 512 insertions, 15 deletions
diff --git a/apps/openssl-vms.cnf b/apps/openssl-vms.cnf
index a3ca393018..f4a25112dc 100644
--- a/apps/openssl-vms.cnf
+++ b/apps/openssl-vms.cnf
@@ -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 = .
diff --git a/apps/openssl.cnf b/apps/openssl.cnf
index 32ee9e9fbb..7d1a8bb6e7 100644
--- a/apps/openssl.cnf
+++ b/apps/openssl.cnf
@@ -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 = .
diff --git a/crypto/conf/conf_def.c b/crypto/conf/conf_def.c
index 82e191aef3..d689502e31 100644
--- a/crypto/conf/conf_def.c
+++ b/crypto/conf/conf_def.c
@@ -1,5 +1,5 @@
/*
- * 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)
- break;
+ 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 (;;) {
diff --git a/crypto/conf/conf_err.c b/crypto/conf/conf_err.c
index 8ba243ca69..dd20a1aafa 100644
--- a/crypto/conf/conf_err.c
+++ b/crypto/conf/conf_err.c
@@ -1,6 +1,6 @@
/*
* 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),
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 318b400e31..9a41ea8e92 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -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
diff --git a/doc/man5/config.pod b/doc/man5/config.pod
index a7cc16b2c0..485ec0870b 100644
--- a/doc/man5/config.pod
+++ b/doc/man5/config.pod
@@ -27,6 +27,21 @@ The environment is mapped onto a section called B<ENV>.
Comments can be included by preceding them with the B<#> character
+Other files can be included using the B<.include> directive followed
+by a path. If the path points to a directory all files with
+names ending with B<.cnf> or B<.conf> are included from the directory.
+Recursive inclusion of directories from files in such directory is not
+supported. That means the files in the included directory can also contain
+B<.include> directives but only inclusion of regular files is supported
+there. The inclusion of directories is not supported on systems without
+POSIX IO support.
+
+It is strongly recommended to use absolute paths with the B<.include>
+directive. Relative paths are evaluated based on the application current
+working directory so unless the configuration file containing the
+B<.include> directive is application specific the inclusion will not
+work as expected.
+
Each section in a configuration file consists of a number of name and
value pairs of the form B<name=value>
@@ -52,6 +67,9 @@ or the B<\> character. By making the last character of a line a B<\>
a B<value> string can be spread across multiple lines. In addition
the sequences B<\n>, B<\r>, B<\b> and B<\t> are recognized.
+All expansion and escape rules as described above that apply to B<value>
+also apply to the path of the B<.include> directive.
+
=head1 OPENSSL LIBRARY CONFIGURATION
Applications can automatically configure certain
diff --git a/include/openssl/conferr.h b/include/openssl/conferr.h
index d623cea6d5..d2d0c7bace 100644
--- a/include/openssl/conferr.h
+++ b/include/openssl/conferr.h
@@ -25,6 +25,7 @@ int ERR_load_CONF_strings(void);
# define CONF_F_CONF_PARSE_LIST 119
# define CONF_F_DEF_LOAD 120
# define CONF_F_DEF_LOAD_BIO 121
+# define CONF_F_GET_NEXT_FILE 107
# define CONF_F_MODULE_INIT 115
# define CONF_F_MODULE_LOAD_DSO 117
# define CONF_F_MODULE_RUN 118
@@ -37,6 +38,7 @@ int ERR_load_CONF_strings(void);
# define CONF_F_NCONF_LOAD_BIO 110
# define CONF_F_NCONF_LOAD_FP 114
# define CONF_F_NCONF_NEW 111
+# define CONF_F_PROCESS_INCLUDE 116
# define CONF_F_STR_COPY 101
/*
@@ -54,6 +56,7 @@ int ERR_load_CONF_strings(void);
# define CONF_R_NO_SECTION 107
# define CONF_R_NO_SUCH_FILE 114
# define CONF_R_NO_VALUE 108
+# define CONF_R_RECURSIVE_DIRECTORY_INCLUDE 111
# define CONF_R_UNABLE_TO_CREATE_NEW_SECTION 103
# define CONF_R_UNKNOWN_MODULE_NAME 113
# define CONF_R_VARIABLE_EXPANSION_TOO_LONG 116
diff --git a/test/build.info b/test/build.info
index 38fcf842b8..30424dc4cf 100644
--- a/test/build.info
+++ b/test/build.info
@@ -35,6 +35,7 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
bftest ssltest_old dsatest exptest rsa_test \
evp_test evp_extra_test igetest v3nametest v3ext \
crltest danetest bad_dtls_test lhash_test \
+ conf_include_test \
constant_time_test verify_extra_test clienthellotest \
packettest asynctest secmemtest srptest memleaktest stack_test \
dtlsv1listentest ct_test threadstest afalgtest d2i_test \
@@ -390,6 +391,10 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
INCLUDE[time_offset_test]=../include
DEPEND[time_offset_test]=../libcrypto libtestutil.a
+ SOURCE[conf_include_test]=conf_include_test.c
+ INCLUDE[conf_include_test]=../include
+ DEPEND[conf_include_test]=../libcrypto libtestutil.a
+
# Internal test programs. These are essentially a collection of internal
# test routines. Some of them need to reach internal symbols that aren't
# available through the shared library (at least on Linux, Solaris, Windows
diff --git a/test/conf_include_test.c b/test/conf_include_test.c
new file mode 100644
index 0000000000..41e4da04eb
--- /dev/null
+++ b/test/conf_include_test.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2016-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include "testutil.h"
+
+#ifdef _WIN32
+# include <direct.h>
+# define DIRSEP "/\\"
+# define chdir _chdir
+# define DIRSEP_PRESERVE 0
+#elif !defined(OPENSSL_NO_POSIX_IO)
+# include <unistd.h>
+# ifndef OPENSSL_SYS_VMS
+# define DIRSEP "/"
+# define DIRSEP_PRESERVE 0
+# else
+# define DIRSEP "/]:"
+# define DIRSEP_PRESERVE 1
+# endif
+#else
+/* the test does not work without chdir() */
+# define chdir(x) (-1);
+# define DIRSEP ""
+# define DIRSEP_PRESERVE 0
+#endif
+
+/* changes path to that of the filename */
+static int change_path(const char *file)
+{
+ char *s = OPENSSL_strdup(file);
+ char *p = s;
+ char *last = NULL;
+ int ret;
+
+ if (s == NULL)
+ return -1;
+
+ while ((p = strpbrk(p, DIRSEP)) != NULL) {
+ last = p++;
+ }
+ if (last == NULL)
+ return 0;
+ last[DIRSEP_PRESERVE] = 0;
+
+ TEST_note("changing path to %s", s);
+ ret = chdir(s);
+ OPENSSL_free(s);
+ return ret;
+}
+
+/*
+ * This test program checks the operation of the .include directive.
+ */
+
+static CONF *conf;
+static BIO *in;
+static int expect_failure = 0;
+
+static int test_load_config(void)
+{
+ long errline;
+ long val;
+ char *str;
+ long err;
+
+ if (!TEST_int_gt(NCONF_load_bio(conf, in, &errline), 0)
+ || !TEST_int_eq(err = ERR_peek_error(), 0)) {
+ if (expect_failure)
+ return 1;
+ TEST_note("Failure loading the configuration at line %ld", errline);
+ return 0;
+ }
+ if (expect_failure) {
+ TEST_note("Failure expected but did not happen");
+ return 0;
+ }
+
+ if (!TEST_int_gt(CONF_modules_load(conf, NULL, 0), 0)) {
+ TEST_note("Failed in CONF_modules_load");
+ return 0;
+ }
+
+ /* verify whether RANDFILE is set correctly */
+ str = NCONF_get_string(conf, "", "RANDFILE");
+ if (!TEST_ptr(str) || !TEST_str_eq(str, "./.rnd")) {
+ TEST_note("RANDFILE incorrect");
+ return 0;
+ }
+
+ /* verify whether CA_default/default_days is set */
+ val = 0;
+ if (!TEST_int_eq(NCONF_get_number(conf, "CA_default", "default_days", &val), 1)
+ || !TEST_int_eq(val, 365)) {
+ TEST_note("default_days incorrect");
+ return 0;
+ }
+
+ /* verify whether req/default_bits is set */
+ val = 0;
+ if (!TEST_int_eq(NCONF_get_number(conf, "req", "default_bits", &val), 1)
+ || !TEST_int_eq(val, 2048)) {
+ TEST_note("default_bits incorrect");
+ return 0;
+ }
+
+ /* verify whether countryName_default is set correctly */
+ str = NCONF_get_string(conf, "req_distinguished_name", "countryName_default");
+ if (!TEST_ptr(str) || !TEST_str_eq(str, "AU")) {
+ TEST_note("countryName_default incorrect");
+ return 0;
+ }
+
+ return 1;
+}
+
+int setup_tests(void)
+{
+ const char *conf_file;
+ const char *arg2;
+
+ if (!TEST_ptr(conf = NCONF_new(NULL)))
+ return 0;
+
+ conf_file = test_get_argument(0);
+
+ if (!TEST_ptr(conf_file)
+ || !TEST_ptr(in = BIO_new_file(conf_file, "r"))) {
+ TEST_note("Unable to open the file argument");
+ return 0;
+ }
+
+ if ((arg2 = test_get_argument(1)) != NULL && *arg2 == 'f') {
+ expect_failure = 1;
+ }
+
+ /*
+ * For this test we need to chdir as we use relative
+ * path names in the config files.
+ */
+ change_path(conf_file);
+
+ ADD_TEST(test_load_config);
+ return 1;
+}
+
+void cleanup_tests(void)
+{
+ BIO_vfree(in);
+ NCONF_free(conf);
+ CONF_modules_unload(1);
+}
diff --git a/test/recipes/90-test_includes.t b/test/recipes/90-test_includes.t
new file mode 100644
index 0000000000..9cf75a1c43
--- /dev/null
+++ b/test/recipes/90-test_includes.t
@@ -0,0 +1,17 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use OpenSSL::Test qw/:DEFAULT data_file/;
+use OpenSSL::Test::Utils;
+
+setup("test_includes");
+
+plan skip_all => "test_includes doesn't work without posix-io"
+ if disabled("posix-io");
+
+plan tests => 3; # The number of tests being performed
+
+ok(run(test(["conf_include_test", data_file("includes.cnf")])), "test directory includes");
+ok(run(test(["conf_include_test", data_file("includes-file.cnf")])), "test file includes");
+ok(run(test(["conf_include_test", data_file("includes-broken.cnf"), "f"])), "test broken includes");
diff --git a/test/recipes/90-test_includes_data/conf-includes/includes1.cnf b/test/recipes/90-test_includes_data/conf-includes/includes1.cnf
new file mode 100644
index 0000000000..66c89006d0
--- /dev/null
+++ b/test/recipes/90-test_includes_data/conf-includes/includes1.cnf
@@ -0,0 +1,36 @@
+[ default ]
+#
+# SSLeay example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+RANDFILE = ./.rnd
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir/new_certs # default place for new certs.
+
+certificate = $dir/CAcert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/CAkey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
diff --git a/test/recipes/90-test_includes_data/conf-includes/includes2.cnf b/test/recipes/90-test_includes_data/conf-includes/includes2.cnf
new file mode 100644
index 0000000000..aa5e67c060
--- /dev/null
+++ b/test/recipes/90-test_includes_data/conf-includes/includes2.cnf
@@ -0,0 +1,53 @@
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = testkey.pem
+distinguished_name = req_distinguished_name
+encrypt_rsa_key = no
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_value = AU
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Queensland
+stateOrProvinceName_value =
+
+localityName = Locality Name (eg, city)
+localityName_value = Brisbane
+
+organizationName = Organization Name (eg, company)
+organizationName_default =
+organizationName_value = CryptSoft Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+organizationalUnitName_default =
+organizationalUnitName_value = .
+
+commonName = Common Name (eg, YOUR name)
+commonName_value = Eric Young
+
+emailAddress = Email Address
+emailAddress_value = eay@mincom.oz.au
diff --git a/test/recipes/90-test_includes_data/includes-broken.cnf b/test/recipes/90-test_includes_data/includes-broken.cnf
new file mode 100644
index 0000000000..d38e18c5e5
--- /dev/null
+++ b/test/recipes/90-test_includes_data/includes-broken.cnf
@@ -0,0 +1,5 @@
+#
+# Example configuration file using includes.
+#
+
+.include includes-broken3.cnf
diff --git a/test/recipes/90-test_includes_data/includes-file.cnf b/test/recipes/90-test_includes_data/includes-file.cnf
new file mode 100644
index 0000000000..1737b70d18
--- /dev/null
+++ b/test/recipes/90-test_includes_data/includes-file.cnf
@@ -0,0 +1,5 @@
+#
+# Example configuration file using includes.
+#
+
+.include includes.cnf
diff --git a/test/recipes/90-test_includes_data/includes.cnf b/test/recipes/90-test_includes_data/includes.cnf
new file mode 100644
index 0000000000..345eeb9884
--- /dev/null
+++ b/test/recipes/90-test_includes_data/includes.cnf
@@ -0,0 +1,5 @@
+#
+# Example configuration file using includes.
+#
+
+.include conf-includes