From b524b808a1d1ba204dbdcbb42de4e3bddb3472ac Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 27 Feb 2018 13:55:35 +0100 Subject: 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 Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/5351) --- crypto/conf/conf_def.c | 202 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 14 deletions(-) (limited to 'crypto/conf/conf_def.c') 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 #include #include "internal/cryptlib.h" +#include "internal/o_dir.h" #include #include #include #include "conf_def.h" #include #include +#ifndef OPENSSL_NO_POSIX_IO +# include +# 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 (;;) { -- cgit v1.2.3