X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fconf%2Fconf_def.c;h=26764dad00421a9b6503c2a8cf82966342a2d0e7;hp=8a34218748c4e0c821c01c9fc709d00fa9975bd2;hb=HEAD;hpb=2044d382323d3471ba475634e267a787286a99b2 diff --git a/crypto/conf/conf_def.c b/crypto/conf/conf_def.c index 8a34218748..e047746f67 100644 --- a/crypto/conf/conf_def.c +++ b/crypto/conf/conf_def.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -11,11 +11,17 @@ #include #include +#include "internal/e_os.h" /* struct stat */ +#ifdef __TANDEM +# include /* needed for stat.h */ +# include /* struct stat */ +#endif #include "internal/cryptlib.h" #include "internal/o_dir.h" #include #include #include +#include "conf_local.h" #include "conf_def.h" #include #include @@ -23,7 +29,6 @@ # include # ifdef _WIN32 # define stat _stat -# define strcasecmp _stricmp # endif #endif @@ -54,7 +59,9 @@ static BIO *get_next_file(const char *path, OPENSSL_DIR_CTX **dirctx); static CONF *def_create(CONF_METHOD *meth); static int def_init_default(CONF *conf); +#ifndef OPENSSL_NO_DEPRECATED_3_0 static int def_init_WIN32(CONF *conf); +#endif static int def_destroy(CONF *conf); static int def_destroy_data(CONF *conf); static int def_load(CONF *conf, const char *name, long *eline); @@ -76,6 +83,12 @@ static CONF_METHOD default_method = { def_load }; +CONF_METHOD *NCONF_default(void) +{ + return &default_method; +} + +#ifndef OPENSSL_NO_DEPRECATED_3_0 static CONF_METHOD WIN32_method = { "WIN32", def_create, @@ -89,15 +102,11 @@ static CONF_METHOD WIN32_method = { def_load }; -CONF_METHOD *NCONF_default(void) -{ - return &default_method; -} - CONF_METHOD *NCONF_WIN32(void) { return &WIN32_method; } +#endif static CONF *def_create(CONF_METHOD *meth) { @@ -117,24 +126,26 @@ static int def_init_default(CONF *conf) if (conf == NULL) return 0; + memset(conf, 0, sizeof(*conf)); conf->meth = &default_method; conf->meth_data = (void *)CONF_type_default; - conf->data = NULL; return 1; } +#ifndef OPENSSL_NO_DEPRECATED_3_0 static int def_init_WIN32(CONF *conf) { if (conf == NULL) return 0; + memset(conf, 0, sizeof(*conf)); conf->meth = &WIN32_method; conf->meth_data = (void *)CONF_type_win32; - conf->data = NULL; return 1; } +#endif static int def_destroy(CONF *conf) { @@ -165,9 +176,9 @@ static int def_load(CONF *conf, const char *name, long *line) #endif if (in == NULL) { if (ERR_GET_REASON(ERR_peek_last_error()) == BIO_R_NO_SUCH_FILE) - CONFerr(CONF_F_DEF_LOAD, CONF_R_NO_SUCH_FILE); + ERR_raise(ERR_LIB_CONF, CONF_R_NO_SUCH_FILE); else - CONFerr(CONF_F_DEF_LOAD, ERR_R_SYS_LIB); + ERR_raise(ERR_LIB_CONF, ERR_R_SYS_LIB); return 0; } @@ -177,6 +188,23 @@ static int def_load(CONF *conf, const char *name, long *line) return ret; } + +/* Parse a boolean value and fill in *flag. Return 0 on error. */ +static int parsebool(const char *pval, int *flag) +{ + if (OPENSSL_strcasecmp(pval, "on") == 0 + || OPENSSL_strcasecmp(pval, "true") == 0) { + *flag = 1; + } else if (OPENSSL_strcasecmp(pval, "off") == 0 + || OPENSSL_strcasecmp(pval, "false") == 0) { + *flag = 0; + } else { + ERR_raise(ERR_LIB_CONF, CONF_R_INVALID_PRAGMA); + return 0; + } + return 1; +} + static int def_load_bio(CONF *conf, BIO *in, long *line) { /* The macro BUFSIZE conflicts with a system macro in VxWorks */ @@ -185,6 +213,7 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) BUF_MEM *buff = NULL; char *s, *p, *end; int again; + int first_call = 1; long eline = 0; char btmp[DECIMAL_SIZE(eline) + 1]; CONF_VALUE *v = NULL, *tv; @@ -197,26 +226,27 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) char *dirpath = NULL; OPENSSL_DIR_CTX *dirctx = NULL; #endif +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + int numincludes = 0; +#endif if ((buff = BUF_MEM_new()) == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_CONF, ERR_R_BUF_LIB); goto err; } section = OPENSSL_strdup("default"); - if (section == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + if (section == NULL) goto err; - } if (_CONF_new_data(conf) == 0) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_CONF, ERR_R_CONF_LIB); goto err; } sv = _CONF_new_section(conf, section); if (sv == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_UNABLE_TO_CREATE_NEW_SECTION); + ERR_raise(ERR_LIB_CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION); goto err; } @@ -224,17 +254,31 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) again = 0; for (;;) { if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB); + ERR_raise(ERR_LIB_CONF, ERR_R_BUF_LIB); goto err; } p = &(buff->data[bufnum]); *p = '\0'; read_retry: - BIO_gets(in, p, CONFBUFSIZE - 1); + if (in != NULL && BIO_gets(in, p, CONFBUFSIZE - 1) < 0) + goto err; p[CONFBUFSIZE - 1] = '\0'; ii = i = strlen(p); + if (first_call) { + /* Other BOMs imply unsupported multibyte encoding, + * so don't strip them and let the error raise */ + const unsigned char utf8_bom[3] = {0xEF, 0xBB, 0xBF}; + + if (i >= 3 && memcmp(p, utf8_bom, 3) == 0) { + memmove(p, p + 3, i - 3); + p[i - 3] = 0; + i -= 3; + ii -= 3; + } + first_call = 0; + } if (i == 0 && !again) { - /* the currently processed BIO is at EOF */ + /* the currently processed BIO is NULL or at EOF */ BIO *parent; #ifndef OPENSSL_NO_POSIX_IO @@ -320,8 +364,7 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) ss = p; goto again; } - CONFerr(CONF_F_DEF_LOAD_BIO, - CONF_R_MISSING_CLOSE_SQUARE_BRACKET); + ERR_raise(ERR_LIB_CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET); goto err; } *end = '\0'; @@ -330,8 +373,7 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) if ((sv = _CONF_get_section(conf, section)) == NULL) sv = _CONF_new_section(conf, section); if (sv == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, - CONF_R_UNABLE_TO_CREATE_NEW_SECTION); + ERR_raise(ERR_LIB_CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION); goto err; } continue; @@ -348,34 +390,131 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) psection = section; } p = eat_ws(conf, end); - if (strncmp(pname, ".include", 8) == 0 && p != pname + 8) { + if (CHECK_AND_SKIP_PREFIX(pname, ".pragma") + && (p != pname || *p == '=')) { + char *pval; + + if (*p == '=') { + p++; + p = eat_ws(conf, p); + } + trim_ws(conf, p); + + /* Pragma values take the form keyword:value */ + pval = strchr(p, ':'); + if (pval == NULL || pval == p || pval[1] == '\0') { + ERR_raise(ERR_LIB_CONF, CONF_R_INVALID_PRAGMA); + goto err; + } + + *pval++ = '\0'; + trim_ws(conf, p); + pval = eat_ws(conf, pval); + + /* + * Known pragmas: + * + * dollarid takes "on", "true or "off", "false" + * abspath takes "on", "true or "off", "false" + * includedir directory prefix + */ + if (strcmp(p, "dollarid") == 0) { + if (!parsebool(pval, &conf->flag_dollarid)) + goto err; + } else if (strcmp(p, "abspath") == 0) { + if (!parsebool(pval, &conf->flag_abspath)) + goto err; + } else if (strcmp(p, "includedir") == 0) { + OPENSSL_free(conf->includedir); + if ((conf->includedir = OPENSSL_strdup(pval)) == NULL) + goto err; + } + + /* + * We *ignore* any unknown pragma. + */ + continue; + } else if (CHECK_AND_SKIP_PREFIX(pname, ".include") + && (p != pname || *p == '=')) { char *include = NULL; BIO *next; + const char *include_dir = ossl_safe_getenv("OPENSSL_CONF_INCLUDE"); + char *include_path = NULL; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* + * The include processing below can cause the "conf" fuzzer to + * timeout due to the fuzzer inserting large and complicated + * includes - with a large amount of time spent in + * OPENSSL_strlcat/OPENSSL_strcpy. This is not a security + * concern because config files should never come from untrusted + * sources. We just set an arbitrary limit on the allowed + * number of includes when fuzzing to prevent this timeout. + */ + if (numincludes++ > 10) + goto err; +#endif + + if (include_dir == NULL) + include_dir = conf->includedir; + if (*p == '=') { + p++; + p = eat_ws(conf, p); + } trim_ws(conf, p); if (!str_copy(conf, psection, &include, p)) goto err; + + if (include_dir != NULL && !ossl_is_absolute_path(include)) { + size_t newlen = strlen(include_dir) + strlen(include) + 2; + + include_path = OPENSSL_malloc(newlen); + if (include_path == NULL) { + OPENSSL_free(include); + goto err; + } + + OPENSSL_strlcpy(include_path, include_dir, newlen); + if (!ossl_ends_with_dirsep(include_path)) + OPENSSL_strlcat(include_path, "/", newlen); + OPENSSL_strlcat(include_path, include, newlen); + OPENSSL_free(include); + } else { + include_path = include; + } + + if (conf->flag_abspath + && !ossl_is_absolute_path(include_path)) { + ERR_raise(ERR_LIB_CONF, CONF_R_RELATIVE_PATH); + OPENSSL_free(include_path); + goto err; + } + /* get the BIO of the included file */ #ifndef OPENSSL_NO_POSIX_IO - next = process_include(include, &dirctx, &dirpath); - if (include != dirpath) { + next = process_include(include_path, &dirctx, &dirpath); + if (include_path != dirpath) { /* dirpath will contain include in case of a directory */ - OPENSSL_free(include); + OPENSSL_free(include_path); } #else - next = BIO_new_file(include, "r"); - OPENSSL_free(include); + next = BIO_new_file(include_path, "r"); + OPENSSL_free(include_path); #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); + ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); + BIO_free(next); goto err; } } if (!sk_BIO_push(biosk, in)) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); + BIO_free(next); goto err; } /* continue with reading from the included BIO */ @@ -383,7 +522,8 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) } continue; } else if (*p != '=') { - CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_MISSING_EQUAL_SIGN); + ERR_raise_data(ERR_LIB_CONF, CONF_R_MISSING_EQUAL_SIGN, + "HERE-->%s", p); goto err; } *end = '\0'; @@ -391,16 +531,12 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) start = eat_ws(conf, p); trim_ws(conf, start); - if ((v = OPENSSL_malloc(sizeof(*v))) == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + if ((v = OPENSSL_malloc(sizeof(*v))) == NULL) goto err; - } v->name = OPENSSL_strdup(pname); v->value = NULL; - if (v->name == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + if (v->name == NULL) goto err; - } if (!str_copy(conf, psection, &(v->value), start)) goto err; @@ -409,14 +545,14 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) == NULL) tv = _CONF_new_section(conf, psection); if (tv == NULL) { - CONFerr(CONF_F_DEF_LOAD_BIO, - CONF_R_UNABLE_TO_CREATE_NEW_SECTION); + ERR_raise(ERR_LIB_CONF, + CONF_R_UNABLE_TO_CREATE_NEW_SECTION); goto err; } } else tv = sv; if (_CONF_add_string(conf, tv, v) == 0) { - CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_CONF, ERR_R_CONF_LIB); goto err; } v = NULL; @@ -430,6 +566,7 @@ static int def_load_bio(CONF *conf, BIO *in, long *line) */ sk_BIO_free(biosk); return 1; + err: BUF_MEM_free(buff); OPENSSL_free(section); @@ -560,7 +697,10 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) buf->data[to++] = v; } else if (IS_EOF(conf, *from)) break; - else if (*from == '$') { + else if (*from == '$' + && (!conf->flag_dollarid + || from[1] == '{' + || from[1] == '(')) { size_t newsize; /* try to expand it */ @@ -577,7 +717,8 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) s++; cp = section; e = np = s; - while (IS_ALNUM(conf, *e)) + while (IS_ALNUM(conf, *e) + || (conf->flag_dollarid && IS_DOLLAR(conf, *e))) e++; if ((e[0] == ':') && (e[1] == ':')) { cp = np; @@ -586,7 +727,8 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) *rrp = '\0'; e += 2; np = e; - while (IS_ALNUM(conf, *e)) + while (IS_ALNUM(conf, *e) + || (conf->flag_dollarid && IS_DOLLAR(conf, *e))) e++; } r = *e; @@ -594,7 +736,7 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) rp = e; if (q) { if (r != q) { - CONFerr(CONF_F_STR_COPY, CONF_R_NO_CLOSE_BRACE); + ERR_raise(ERR_LIB_CONF, CONF_R_NO_CLOSE_BRACE); goto err; } e++; @@ -614,16 +756,16 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) *rrp = rr; *rp = r; if (p == NULL) { - CONFerr(CONF_F_STR_COPY, CONF_R_VARIABLE_HAS_NO_VALUE); + ERR_raise(ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE); goto err; } newsize = strlen(p) + buf->length - (e - from); if (newsize > MAX_CONF_VALUE_LENGTH) { - CONFerr(CONF_F_STR_COPY, CONF_R_VARIABLE_EXPANSION_TOO_LONG); + ERR_raise(ERR_LIB_CONF, CONF_R_VARIABLE_EXPANSION_TOO_LONG); goto err; } if (!BUF_MEM_grow_clean(buf, newsize)) { - CONFerr(CONF_F_STR_COPY, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_CONF, ERR_R_BUF_LIB); goto err; } while (*p) @@ -664,21 +806,19 @@ static int str_copy(CONF *conf, char *section, char **pto, char *from) static BIO *process_include(char *include, OPENSSL_DIR_CTX **dirctx, char **dirpath) { - struct stat st = { 0 }; + struct stat st; BIO *next; if (stat(include, &st) < 0) { - SYSerr(SYS_F_STAT, errno); - ERR_add_error_data(1, include); + ERR_raise_data(ERR_LIB_SYS, errno, "calling stat(%s)", 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); + ERR_raise_data(ERR_LIB_CONF, CONF_R_RECURSIVE_DIRECTORY_INCLUDE, + "%s", include); return NULL; } /* a directory, load its contents */ @@ -698,38 +838,37 @@ static BIO *process_include(char *include, OPENSSL_DIR_CTX **dirctx, static BIO *get_next_file(const char *path, OPENSSL_DIR_CTX **dirctx) { const char *filename; + size_t pathlen; + pathlen = strlen(path); 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)) { + if ((namelen > 5 + && OPENSSL_strcasecmp(filename + namelen - 5, ".conf") == 0) + || (namelen > 4 + && OPENSSL_strcasecmp(filename + namelen - 4, ".cnf") == 0)) { size_t newlen; char *newpath; BIO *bio; - newlen = strlen(path) + namelen + 2; + newlen = pathlen + namelen + 2; newpath = OPENSSL_zalloc(newlen); - if (newpath == NULL) { - CONFerr(CONF_F_GET_NEXT_FILE, ERR_R_MALLOC_FAILURE); + if (newpath == NULL) 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); - } + 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') { @@ -753,7 +892,7 @@ static BIO *get_next_file(const char *path, OPENSSL_DIR_CTX **dirctx) static int is_keytype(const CONF *conf, char c, unsigned short type) { - const unsigned short * keytypes = (const unsigned short *) conf->meth_data; + const unsigned short *keytypes = (const unsigned short *) conf->meth_data; unsigned char key = (unsigned char)c; #ifdef CHARSET_EBCDIC @@ -802,7 +941,8 @@ static char *eat_alpha_numeric(CONF *conf, char *p) p = scan_esc(conf, p); continue; } - if (!IS_ALNUM_PUNCT(conf, *p)) + if (!(IS_ALNUM_PUNCT(conf, *p) + || (conf->flag_dollarid && IS_DOLLAR(conf, *p)))) return p; p++; }