Add support for .include directive in config files
authorTomas Mraz <tmraz@fedoraproject.org>
Tue, 27 Feb 2018 12:55:35 +0000 (13:55 +0100)
committerMatt Caswell <matt@openssl.org>
Mon, 5 Mar 2018 13:32:40 +0000 (13:32 +0000)
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)

15 files changed:
apps/openssl-vms.cnf
apps/openssl.cnf
crypto/conf/conf_def.c
crypto/conf/conf_err.c
crypto/err/openssl.txt
doc/man5/config.pod
include/openssl/conferr.h
test/build.info
test/conf_include_test.c [new file with mode: 0644]
test/recipes/90-test_includes.t [new file with mode: 0644]
test/recipes/90-test_includes_data/conf-includes/includes1.cnf [new file with mode: 0644]
test/recipes/90-test_includes_data/conf-includes/includes2.cnf [new file with mode: 0644]
test/recipes/90-test_includes_data/includes-broken.cnf [new file with mode: 0644]
test/recipes/90-test_includes_data/includes-file.cnf [new file with mode: 0644]
test/recipes/90-test_includes_data/includes.cnf [new file with mode: 0644]

index a3ca3930184d49a10b51c1ada98ade97643bb473..f4a25112dc820e169cb32431f77c338b18676e4b 100644 (file)
@@ -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                   = .
index 32ee9e9fbbba56f8cb8590766796fd0c88c520af..7d1a8bb6e7f06b7abf6df53dca4e3170b7fdd2f6 100644 (file)
@@ -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                   = .
index 82e191aef306869b8a1849d0e681f6f406d482cf..d689502e317fa8d24f898e2e52e8f5a290fa8a5e 100644 (file)
@@ -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
 #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
 #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 (;;) {
index 8ba243ca69ac36ac2dc32c7f84f7c4c82a8c71db..dd20a1aafa27afa9a28fdaf5abbb3e45be40959d 100644 (file)
@@ -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),
index 318b400e318431afbf09589d0e01fca4b00dd6ca..9a41ea8e925dc79cf3d86fc9be5aa61836b1fe5f 100644 (file)
@@ -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
index a7cc16b2c0027957b1acee377cbfede8e6f1aab2..485ec0870b684e94b449d0f60271442ed513d750 100644 (file)
@@ -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
index d623cea6d5ae599d04ba1da79ec6c2ac040e3edc..d2d0c7baceba92e0daabf74794ec785f55525b61 100644 (file)
@@ -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
index 38fcf842b8fbcf2c8b796310032ee573273403c3..30424dc4cfb40364ea3a93ac78e01eaf2bc29e7c 100644 (file)
@@ -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 (file)
index 0000000..41e4da0
--- /dev/null
@@ -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 (file)
index 0000000..9cf75a1
--- /dev/null
@@ -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 (file)
index 0000000..66c8900
--- /dev/null
@@ -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 (file)
index 0000000..aa5e67c
--- /dev/null
@@ -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 (file)
index 0000000..d38e18c
--- /dev/null
@@ -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 (file)
index 0000000..1737b70
--- /dev/null
@@ -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 (file)
index 0000000..345eeb9
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Example configuration file using includes.
+#
+
+.include conf-includes