| /* | 
 |  * Copyright 1995-2016 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 | 
 |  */ | 
 |  | 
 | /* Part of the code in here was originally in conf.c, which is now removed */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include "internal/cryptlib.h" | 
 | #include <openssl/stack.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> | 
 |  | 
 | static char *eat_ws(CONF *conf, char *p); | 
 | 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))) | 
 |  | 
 | static CONF *def_create(CONF_METHOD *meth); | 
 | static int def_init_default(CONF *conf); | 
 | static int def_init_WIN32(CONF *conf); | 
 | static int def_destroy(CONF *conf); | 
 | static int def_destroy_data(CONF *conf); | 
 | static int def_load(CONF *conf, const char *name, long *eline); | 
 | static int def_load_bio(CONF *conf, BIO *bp, long *eline); | 
 | 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); | 
 |  | 
 | static CONF_METHOD default_method = { | 
 |     "OpenSSL default", | 
 |     def_create, | 
 |     def_init_default, | 
 |     def_destroy, | 
 |     def_destroy_data, | 
 |     def_load_bio, | 
 |     def_dump, | 
 |     def_is_number, | 
 |     def_to_int, | 
 |     def_load | 
 | }; | 
 |  | 
 | static CONF_METHOD WIN32_method = { | 
 |     "WIN32", | 
 |     def_create, | 
 |     def_init_WIN32, | 
 |     def_destroy, | 
 |     def_destroy_data, | 
 |     def_load_bio, | 
 |     def_dump, | 
 |     def_is_number, | 
 |     def_to_int, | 
 |     def_load | 
 | }; | 
 |  | 
 | CONF_METHOD *NCONF_default() | 
 | { | 
 |     return &default_method; | 
 | } | 
 |  | 
 | CONF_METHOD *NCONF_WIN32() | 
 | { | 
 |     return &WIN32_method; | 
 | } | 
 |  | 
 | static CONF *def_create(CONF_METHOD *meth) | 
 | { | 
 |     CONF *ret; | 
 |  | 
 |     ret = OPENSSL_malloc(sizeof(*ret)); | 
 |     if (ret != NULL) | 
 |         if (meth->init(ret) == 0) { | 
 |             OPENSSL_free(ret); | 
 |             ret = NULL; | 
 |         } | 
 |     return ret; | 
 | } | 
 |  | 
 | static int def_init_default(CONF *conf) | 
 | { | 
 |     if (conf == NULL) | 
 |         return 0; | 
 |  | 
 |     conf->meth = &default_method; | 
 |     conf->meth_data = (void *)CONF_type_default; | 
 |     conf->data = NULL; | 
 |  | 
 |     return 1; | 
 | } | 
 |  | 
 | static int def_init_WIN32(CONF *conf) | 
 | { | 
 |     if (conf == NULL) | 
 |         return 0; | 
 |  | 
 |     conf->meth = &WIN32_method; | 
 |     conf->meth_data = (void *)CONF_type_win32; | 
 |     conf->data = NULL; | 
 |  | 
 |     return 1; | 
 | } | 
 |  | 
 | static int def_destroy(CONF *conf) | 
 | { | 
 |     if (def_destroy_data(conf)) { | 
 |         OPENSSL_free(conf); | 
 |         return 1; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static int def_destroy_data(CONF *conf) | 
 | { | 
 |     if (conf == NULL) | 
 |         return 0; | 
 |     _CONF_free_data(conf); | 
 |     return 1; | 
 | } | 
 |  | 
 | static int def_load(CONF *conf, const char *name, long *line) | 
 | { | 
 |     int ret; | 
 |     BIO *in = NULL; | 
 |  | 
 | #ifdef OPENSSL_SYS_VMS | 
 |     in = BIO_new_file(name, "r"); | 
 | #else | 
 |     in = BIO_new_file(name, "rb"); | 
 | #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); | 
 |         else | 
 |             CONFerr(CONF_F_DEF_LOAD, ERR_R_SYS_LIB); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     ret = def_load_bio(conf, in, line); | 
 |     BIO_free(in); | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | static int def_load_bio(CONF *conf, BIO *in, long *line) | 
 | { | 
 | /* The macro BUFSIZE conflicts with a system macro in VxWorks */ | 
 | #define CONFBUFSIZE     512 | 
 |     int bufnum = 0, i, ii; | 
 |     BUF_MEM *buff = NULL; | 
 |     char *s, *p, *end; | 
 |     int again; | 
 |     long eline = 0; | 
 |     char btmp[DECIMAL_SIZE(eline) + 1]; | 
 |     CONF_VALUE *v = NULL, *tv; | 
 |     CONF_VALUE *sv = NULL; | 
 |     char *section = NULL, *buf; | 
 |     char *start, *psection, *pname; | 
 |     void *h = (void *)(conf->data); | 
 |  | 
 |     if ((buff = BUF_MEM_new()) == NULL) { | 
 |         CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB); | 
 |         goto err; | 
 |     } | 
 |  | 
 |     section = OPENSSL_strdup("default"); | 
 |     if (section == NULL) { | 
 |         CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); | 
 |         goto err; | 
 |     } | 
 |  | 
 |     if (_CONF_new_data(conf) == 0) { | 
 |         CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); | 
 |         goto err; | 
 |     } | 
 |  | 
 |     sv = _CONF_new_section(conf, section); | 
 |     if (sv == NULL) { | 
 |         CONFerr(CONF_F_DEF_LOAD_BIO, CONF_R_UNABLE_TO_CREATE_NEW_SECTION); | 
 |         goto err; | 
 |     } | 
 |  | 
 |     bufnum = 0; | 
 |     again = 0; | 
 |     for (;;) { | 
 |         if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) { | 
 |             CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_BUF_LIB); | 
 |             goto err; | 
 |         } | 
 |         p = &(buff->data[bufnum]); | 
 |         *p = '\0'; | 
 |         BIO_gets(in, p, CONFBUFSIZE - 1); | 
 |         p[CONFBUFSIZE - 1] = '\0'; | 
 |         ii = i = strlen(p); | 
 |         if (i == 0 && !again) | 
 |             break; | 
 |         again = 0; | 
 |         while (i > 0) { | 
 |             if ((p[i - 1] != '\r') && (p[i - 1] != '\n')) | 
 |                 break; | 
 |             else | 
 |                 i--; | 
 |         } | 
 |         /* | 
 |          * we removed some trailing stuff so there is a new line on the end. | 
 |          */ | 
 |         if (ii && i == ii) | 
 |             again = 1;          /* long line */ | 
 |         else { | 
 |             p[i] = '\0'; | 
 |             eline++;            /* another input line */ | 
 |         } | 
 |  | 
 |         /* we now have a line with trailing \r\n removed */ | 
 |  | 
 |         /* i is the number of bytes */ | 
 |         bufnum += i; | 
 |  | 
 |         v = NULL; | 
 |         /* check for line continuation */ | 
 |         if (bufnum >= 1) { | 
 |             /* | 
 |              * If we have bytes and the last char '\\' and second last char | 
 |              * is not '\\' | 
 |              */ | 
 |             p = &(buff->data[bufnum - 1]); | 
 |             if (IS_ESC(conf, p[0]) && ((bufnum <= 1) || !IS_ESC(conf, p[-1]))) { | 
 |                 bufnum--; | 
 |                 again = 1; | 
 |             } | 
 |         } | 
 |         if (again) | 
 |             continue; | 
 |         bufnum = 0; | 
 |         buf = buff->data; | 
 |  | 
 |         clear_comments(conf, buf); | 
 |         s = eat_ws(conf, buf); | 
 |         if (IS_EOF(conf, *s)) | 
 |             continue;           /* blank line */ | 
 |         if (*s == '[') { | 
 |             char *ss; | 
 |  | 
 |             s++; | 
 |             start = eat_ws(conf, s); | 
 |             ss = start; | 
 |  again: | 
 |             end = eat_alpha_numeric(conf, ss); | 
 |             p = eat_ws(conf, end); | 
 |             if (*p != ']') { | 
 |                 if (*p != '\0' && ss != p) { | 
 |                     ss = p; | 
 |                     goto again; | 
 |                 } | 
 |                 CONFerr(CONF_F_DEF_LOAD_BIO, | 
 |                         CONF_R_MISSING_CLOSE_SQUARE_BRACKET); | 
 |                 goto err; | 
 |             } | 
 |             *end = '\0'; | 
 |             if (!str_copy(conf, NULL, §ion, start)) | 
 |                 goto err; | 
 |             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); | 
 |                 goto err; | 
 |             } | 
 |             continue; | 
 |         } else { | 
 |             pname = s; | 
 |             psection = NULL; | 
 |             end = eat_alpha_numeric(conf, s); | 
 |             if ((end[0] == ':') && (end[1] == ':')) { | 
 |                 *end = '\0'; | 
 |                 end += 2; | 
 |                 psection = pname; | 
 |                 pname = end; | 
 |                 end = eat_alpha_numeric(conf, end); | 
 |             } | 
 |             p = eat_ws(conf, end); | 
 |             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'; | 
 |  | 
 |             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->value = NULL; | 
 |             if (v->name == NULL) { | 
 |                 CONFerr(CONF_F_DEF_LOAD_BIO, ERR_R_MALLOC_FAILURE); | 
 |                 goto err; | 
 |             } | 
 |             OPENSSL_strlcpy(v->name, pname, strlen(pname) + 1); | 
 |             if (!str_copy(conf, psection, &(v->value), start)) | 
 |                 goto err; | 
 |  | 
 |             if (strcmp(psection, section) != 0) { | 
 |                 if ((tv = _CONF_get_section(conf, psection)) | 
 |                     == NULL) | 
 |                     tv = _CONF_new_section(conf, psection); | 
 |                 if (tv == NULL) { | 
 |                     CONFerr(CONF_F_DEF_LOAD_BIO, | 
 |                             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); | 
 |                 goto err; | 
 |             } | 
 |             v = NULL; | 
 |         } | 
 |     } | 
 |     BUF_MEM_free(buff); | 
 |     OPENSSL_free(section); | 
 |     return (1); | 
 |  err: | 
 |     BUF_MEM_free(buff); | 
 |     OPENSSL_free(section); | 
 |     if (line != NULL) | 
 |         *line = eline; | 
 |     BIO_snprintf(btmp, sizeof btmp, "%ld", eline); | 
 |     ERR_add_error_data(2, "line ", btmp); | 
 |     if (h != conf->data) { | 
 |         CONF_free(conf->data); | 
 |         conf->data = NULL; | 
 |     } | 
 |     if (v != NULL) { | 
 |         OPENSSL_free(v->name); | 
 |         OPENSSL_free(v->value); | 
 |         OPENSSL_free(v); | 
 |     } | 
 |     return (0); | 
 | } | 
 |  | 
 | static void clear_comments(CONF *conf, char *p) | 
 | { | 
 |     for (;;) { | 
 |         if (IS_FCOMMENT(conf, *p)) { | 
 |             *p = '\0'; | 
 |             return; | 
 |         } | 
 |         if (!IS_WS(conf, *p)) { | 
 |             break; | 
 |         } | 
 |         p++; | 
 |     } | 
 |  | 
 |     for (;;) { | 
 |         if (IS_COMMENT(conf, *p)) { | 
 |             *p = '\0'; | 
 |             return; | 
 |         } | 
 |         if (IS_DQUOTE(conf, *p)) { | 
 |             p = scan_dquote(conf, p); | 
 |             continue; | 
 |         } | 
 |         if (IS_QUOTE(conf, *p)) { | 
 |             p = scan_quote(conf, p); | 
 |             continue; | 
 |         } | 
 |         if (IS_ESC(conf, *p)) { | 
 |             p = scan_esc(conf, p); | 
 |             continue; | 
 |         } | 
 |         if (IS_EOF(conf, *p)) | 
 |             return; | 
 |         else | 
 |             p++; | 
 |     } | 
 | } | 
 |  | 
 | static int str_copy(CONF *conf, char *section, char **pto, char *from) | 
 | { | 
 |     int q, r, rr = 0, to = 0, len = 0; | 
 |     char *s, *e, *rp, *p, *rrp, *np, *cp, v; | 
 |     BUF_MEM *buf; | 
 |  | 
 |     if ((buf = BUF_MEM_new()) == NULL) | 
 |         return (0); | 
 |  | 
 |     len = strlen(from) + 1; | 
 |     if (!BUF_MEM_grow(buf, len)) | 
 |         goto err; | 
 |  | 
 |     for (;;) { | 
 |         if (IS_QUOTE(conf, *from)) { | 
 |             q = *from; | 
 |             from++; | 
 |             while (!IS_EOF(conf, *from) && (*from != q)) { | 
 |                 if (IS_ESC(conf, *from)) { | 
 |                     from++; | 
 |                     if (IS_EOF(conf, *from)) | 
 |                         break; | 
 |                 } | 
 |                 buf->data[to++] = *(from++); | 
 |             } | 
 |             if (*from == q) | 
 |                 from++; | 
 |         } else if (IS_DQUOTE(conf, *from)) { | 
 |             q = *from; | 
 |             from++; | 
 |             while (!IS_EOF(conf, *from)) { | 
 |                 if (*from == q) { | 
 |                     if (*(from + 1) == q) { | 
 |                         from++; | 
 |                     } else { | 
 |                         break; | 
 |                     } | 
 |                 } | 
 |                 buf->data[to++] = *(from++); | 
 |             } | 
 |             if (*from == q) | 
 |                 from++; | 
 |         } else if (IS_ESC(conf, *from)) { | 
 |             from++; | 
 |             v = *(from++); | 
 |             if (IS_EOF(conf, v)) | 
 |                 break; | 
 |             else if (v == 'r') | 
 |                 v = '\r'; | 
 |             else if (v == 'n') | 
 |                 v = '\n'; | 
 |             else if (v == 'b') | 
 |                 v = '\b'; | 
 |             else if (v == 't') | 
 |                 v = '\t'; | 
 |             buf->data[to++] = v; | 
 |         } else if (IS_EOF(conf, *from)) | 
 |             break; | 
 |         else if (*from == '$') { | 
 |             /* try to expand it */ | 
 |             rrp = NULL; | 
 |             s = &(from[1]); | 
 |             if (*s == '{') | 
 |                 q = '}'; | 
 |             else if (*s == '(') | 
 |                 q = ')'; | 
 |             else | 
 |                 q = 0; | 
 |  | 
 |             if (q) | 
 |                 s++; | 
 |             cp = section; | 
 |             e = np = s; | 
 |             while (IS_ALPHA_NUMERIC(conf, *e)) | 
 |                 e++; | 
 |             if ((e[0] == ':') && (e[1] == ':')) { | 
 |                 cp = np; | 
 |                 rrp = e; | 
 |                 rr = *e; | 
 |                 *rrp = '\0'; | 
 |                 e += 2; | 
 |                 np = e; | 
 |                 while (IS_ALPHA_NUMERIC(conf, *e)) | 
 |                     e++; | 
 |             } | 
 |             r = *e; | 
 |             *e = '\0'; | 
 |             rp = e; | 
 |             if (q) { | 
 |                 if (r != q) { | 
 |                     CONFerr(CONF_F_STR_COPY, CONF_R_NO_CLOSE_BRACE); | 
 |                     goto err; | 
 |                 } | 
 |                 e++; | 
 |             } | 
 |             /*- | 
 |              * So at this point we have | 
 |              * np which is the start of the name string which is | 
 |              *   '\0' terminated. | 
 |              * cp which is the start of the section string which is | 
 |              *   '\0' terminated. | 
 |              * e is the 'next point after'. | 
 |              * r and rr are the chars replaced by the '\0' | 
 |              * rp and rrp is where 'r' and 'rr' came from. | 
 |              */ | 
 |             p = _CONF_get_string(conf, cp, np); | 
 |             if (rrp != NULL) | 
 |                 *rrp = rr; | 
 |             *rp = r; | 
 |             if (p == NULL) { | 
 |                 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)))) { | 
 |                 CONFerr(CONF_F_STR_COPY, ERR_R_MALLOC_FAILURE); | 
 |                 goto err; | 
 |             } | 
 |             while (*p) | 
 |                 buf->data[to++] = *(p++); | 
 |  | 
 |             /* | 
 |              * Since we change the pointer 'from', we also have to change the | 
 |              * perceived length of the string it points at.  /RL | 
 |              */ | 
 |             len -= e - from; | 
 |             from = e; | 
 |  | 
 |             /* | 
 |              * In case there were no braces or parenthesis around the | 
 |              * variable reference, we have to put back the character that was | 
 |              * replaced with a '\0'.  /RL | 
 |              */ | 
 |             *rp = r; | 
 |         } else | 
 |             buf->data[to++] = *(from++); | 
 |     } | 
 |     buf->data[to] = '\0'; | 
 |     OPENSSL_free(*pto); | 
 |     *pto = buf->data; | 
 |     OPENSSL_free(buf); | 
 |     return (1); | 
 |  err: | 
 |     BUF_MEM_free(buf); | 
 |     return (0); | 
 | } | 
 |  | 
 | static char *eat_ws(CONF *conf, char *p) | 
 | { | 
 |     while (IS_WS(conf, *p) && (!IS_EOF(conf, *p))) | 
 |         p++; | 
 |     return (p); | 
 | } | 
 |  | 
 | static char *eat_alpha_numeric(CONF *conf, char *p) | 
 | { | 
 |     for (;;) { | 
 |         if (IS_ESC(conf, *p)) { | 
 |             p = scan_esc(conf, p); | 
 |             continue; | 
 |         } | 
 |         if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p)) | 
 |             return (p); | 
 |         p++; | 
 |     } | 
 | } | 
 |  | 
 | static char *scan_quote(CONF *conf, char *p) | 
 | { | 
 |     int q = *p; | 
 |  | 
 |     p++; | 
 |     while (!(IS_EOF(conf, *p)) && (*p != q)) { | 
 |         if (IS_ESC(conf, *p)) { | 
 |             p++; | 
 |             if (IS_EOF(conf, *p)) | 
 |                 return (p); | 
 |         } | 
 |         p++; | 
 |     } | 
 |     if (*p == q) | 
 |         p++; | 
 |     return (p); | 
 | } | 
 |  | 
 | static char *scan_dquote(CONF *conf, char *p) | 
 | { | 
 |     int q = *p; | 
 |  | 
 |     p++; | 
 |     while (!(IS_EOF(conf, *p))) { | 
 |         if (*p == q) { | 
 |             if (*(p + 1) == q) { | 
 |                 p++; | 
 |             } else { | 
 |                 break; | 
 |             } | 
 |         } | 
 |         p++; | 
 |     } | 
 |     if (*p == q) | 
 |         p++; | 
 |     return (p); | 
 | } | 
 |  | 
 | 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); | 
 |     else | 
 |         BIO_printf(out, "[[%s]]\n", a->section); | 
 | } | 
 |  | 
 | IMPLEMENT_LHASH_DOALL_ARG_CONST(CONF_VALUE, BIO); | 
 |  | 
 | static int def_dump(const CONF *conf, BIO *out) | 
 | { | 
 |     lh_CONF_VALUE_doall_BIO(conf->data, dump_value_doall_arg, out); | 
 |     return 1; | 
 | } | 
 |  | 
 | static int def_is_number(const CONF *conf, char c) | 
 | { | 
 |     return IS_NUMBER(conf, c); | 
 | } | 
 |  | 
 | static int def_to_int(const CONF *conf, char c) | 
 | { | 
 |     return c - '0'; | 
 | } |