Update copyright year
[openssl.git] / crypto / conf / conf_def.c
index ee71f482001d5134f8dc7a93f84bb2291da94a94..8e3f42a0caca2928824d5e468e098aabd88abbc5 100644 (file)
@@ -1,81 +1,56 @@
-/* crypto/conf/conf.c */
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
+/*
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
  *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
+ * 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
  */
 
 /* Part of the code in here was originally in conf.c, which is now removed */
 
 #include <stdio.h>
 #include <string.h>
-#include "cryptlib.h"
-#include <openssl/stack.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
 
+#ifndef S_ISDIR
+# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+#endif
+
+/*
+ * The maximum length we can grow a value to after variable expansion. 64k
+ * should be more than enough for all reasonable uses.
+ */
+#define MAX_CONF_VALUE_LENGTH       65536
+
+static int is_keytype(const CONF *conf, char c, unsigned short type);
 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);
@@ -88,8 +63,6 @@ static int def_dump(const CONF *conf, BIO *bp);
 static int def_is_number(const CONF *conf, char c);
 static int def_to_int(const CONF *conf, char c);
 
-const char CONF_def_version[] = "CONF_def" OPENSSL_VERSION_PTEXT;
-
 static CONF_METHOD default_method = {
     "OpenSSL default",
     def_create,
@@ -116,12 +89,12 @@ static CONF_METHOD WIN32_method = {
     def_load
 };
 
-CONF_METHOD *NCONF_default()
+CONF_METHOD *NCONF_default(void)
 {
     return &default_method;
 }
 
-CONF_METHOD *NCONF_WIN32()
+CONF_METHOD *NCONF_WIN32(void)
 {
     return &WIN32_method;
 }
@@ -131,7 +104,7 @@ static CONF *def_create(CONF_METHOD *meth)
     CONF *ret;
 
     ret = OPENSSL_malloc(sizeof(*ret));
-    if (ret)
+    if (ret != NULL)
         if (meth->init(ret) == 0) {
             OPENSSL_free(ret);
             ret = NULL;
@@ -219,18 +192,22 @@ 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);
         goto err;
     }
 
-    section = OPENSSL_malloc(10);
+    section = OPENSSL_strdup("default");
     if (section == NULL) {
         CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
         goto err;
     }
-    BUF_strlcpy(section, "default", 10);
 
     if (_CONF_new_data(conf) == 0) {
         CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
@@ -252,11 +229,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'))
@@ -332,7 +337,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';
@@ -340,36 +344,68 @@ 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 || *p == '=')) {
+                char *include = NULL;
+                BIO *next;
+
+                if (*p == '=') {
+                    p++;
+                    p = eat_ws(conf, p);
+                }
+                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_malloc(strlen(pname) + 1);
+            v->name = OPENSSL_strdup(pname);
             v->value = NULL;
             if (v->name == NULL) {
                 CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE);
                 goto err;
             }
-            BUF_strlcpy(v->name, pname, strlen(pname) + 1);
             if (!str_copy(conf, psection, &(v->value), start))
                 goto err;
 
@@ -393,13 +429,34 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
     }
     BUF_MEM_free(buff);
     OPENSSL_free(section);
-    return (1);
+    /*
+     * No need to pop, since we only get here if the stack is empty.
+     * If this causes a BIO leak, THE ISSUE IS SOMEWHERE ELSE!
+     */
+    sk_BIO_free(biosk);
+    return 1;
  err:
     BUF_MEM_free(buff);
     OPENSSL_free(section);
+    /*
+     * Since |in| is the first element of the stack and should NOT be freed
+     * here, we cannot use sk_BIO_pop_free().  Instead, we pop and free one
+     * BIO at a time, making sure that the last one popped isn't.
+     */
+    while (sk_BIO_num(biosk) > 0) {
+        BIO *popped = sk_BIO_pop(biosk);
+        BIO_vfree(in);
+        in = popped;
+    }
+    sk_BIO_free(biosk);
+#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);
+    BIO_snprintf(btmp, sizeof(btmp), "%ld", eline);
     ERR_add_error_data(2, "line ", btmp);
     if (h != conf->data) {
         CONF_free(conf->data);
@@ -410,7 +467,7 @@ static int def_load_bio(CONF *conf, BIO *in, long *line)
         OPENSSL_free(v->value);
         OPENSSL_free(v);
     }
-    return (0);
+    return 0;
 }
 
 static void clear_comments(CONF *conf, char *p)
@@ -457,7 +514,7 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
     BUF_MEM *buf;
 
     if ((buf = BUF_MEM_new()) == NULL)
-        return (0);
+        return 0;
 
     len = strlen(from) + 1;
     if (!BUF_MEM_grow(buf, len))
@@ -509,6 +566,8 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
         } else if (IS_EOF(conf, *from))
             break;
         else if (*from == '$') {
+            size_t newsize;
+
             /* try to expand it */
             rrp = NULL;
             s = &(from[1]);
@@ -523,7 +582,7 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
                 s++;
             cp = section;
             e = np = s;
-            while (IS_ALPHA_NUMERIC(conf, *e))
+            while (IS_ALNUM(conf, *e))
                 e++;
             if ((e[0] == ':') && (e[1] == ':')) {
                 cp = np;
@@ -532,7 +591,7 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
                 *rrp = '\0';
                 e += 2;
                 np = e;
-                while (IS_ALPHA_NUMERIC(conf, *e))
+                while (IS_ALNUM(conf, *e))
                     e++;
             }
             r = *e;
@@ -563,8 +622,12 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
                 CONFerr(CONF_F_STR_COPY, CONF_R_VARIABLE_HAS_NO_VALUE);
                 goto err;
             }
-            if (!BUF_MEM_grow_clean(buf,
-                        (strlen(p) + buf->length - (e - from)))) {
+            newsize = strlen(p) + buf->length - (e - from);
+            if (newsize > MAX_CONF_VALUE_LENGTH) {
+                CONFerr(CONF_F_STR_COPY, CONF_R_VARIABLE_EXPANSION_TOO_LONG);
+                goto err;
+            }
+            if (!BUF_MEM_grow_clean(buf, newsize)) {
                 CONFerr(CONF_F_STR_COPY, ERR_R_MALLOC_FAILURE);
                 goto err;
             }
@@ -591,17 +654,150 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from)
     OPENSSL_free(*pto);
     *pto = buf->data;
     OPENSSL_free(buf);
-    return (1);
+    return 1;
  err:
     BUF_MEM_free(buf);
-    return (0);
+    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 (S_ISDIR(st.st_mode)) {
+        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;
+            }
+#ifdef OPENSSL_SYS_VMS
+            /*
+             * If the given path isn't clear VMS syntax,
+             * we treat it as on Unix.
+             */
+            {
+                size_t pathlen = strlen(path);
+
+                if (path[pathlen - 1] == ']' || path[pathlen - 1] == '>'
+                    || path[pathlen - 1] == ':') {
+                    /* Clear VMS directory syntax, just copy as is */
+                    OPENSSL_strlcpy(newpath, path, newlen);
+                }
+            }
+#endif
+            if (newpath[0] == '\0') {
+                OPENSSL_strlcpy(newpath, path, newlen);
+                OPENSSL_strlcat(newpath, "/", newlen);
+            }
+            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 int is_keytype(const CONF *conf, char c, unsigned short type)
+{
+    const unsigned short * keytypes = (const unsigned short *) conf->meth_data;
+    unsigned char key = (unsigned char)c;
+
+#ifdef CHARSET_EBCDIC
+# if CHAR_BIT > 8
+    if (key > 255) {
+        /* key is out of range for os_toascii table */
+        return 0;
+    }
+# endif
+    /* convert key from ebcdic to ascii */
+    key = os_toascii[key];
+#endif
+
+    if (key > 127) {
+        /* key is not a seven bit ascii character */
+        return 0;
+    }
+
+    return (keytypes[key] & type) ? 1 : 0;
 }
 
 static char *eat_ws(CONF *conf, char *p)
 {
     while (IS_WS(conf, *p) && (!IS_EOF(conf, *p)))
         p++;
-    return (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)
@@ -611,8 +807,8 @@ static char *eat_alpha_numeric(CONF *conf, char *p)
             p = scan_esc(conf, p);
             continue;
         }
-        if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p))
-            return (p);
+        if (!IS_ALNUM_PUNCT(conf, *p))
+            return p;
         p++;
     }
 }
@@ -626,13 +822,13 @@ static char *scan_quote(CONF *conf, char *p)
         if (IS_ESC(conf, *p)) {
             p++;
             if (IS_EOF(conf, *p))
-                return (p);
+                return p;
         }
         p++;
     }
     if (*p == q)
         p++;
-    return (p);
+    return p;
 }
 
 static char *scan_dquote(CONF *conf, char *p)
@@ -652,10 +848,10 @@ static char *scan_dquote(CONF *conf, char *p)
     }
     if (*p == q)
         p++;
-    return (p);
+    return p;
 }
 
-static void dump_value_doall_arg(CONF_VALUE *a, BIO *out)
+static void dump_value_doall_arg(const CONF_VALUE *a, BIO *out)
 {
     if (a->name)
         BIO_printf(out, "[%s] %s=%s\n", a->section, a->name, a->value);
@@ -663,12 +859,11 @@ static void dump_value_doall_arg(CONF_VALUE *a, BIO *out)
         BIO_printf(out, "[[%s]]\n", a->section);
 }
 
-static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_value, CONF_VALUE, BIO)
+IMPLEMENT_LHASH_DOALL_ARG_CONST(CONF_VALUE, BIO);
 
 static int def_dump(const CONF *conf, BIO *out)
 {
-    lh_CONF_VALUE_doall_arg(conf->data, LHASH_DOALL_ARG_FN(dump_value),
-                            BIO, out);
+    lh_CONF_VALUE_doall_BIO(conf->data, dump_value_doall_arg, out);
     return 1;
 }