From: Dmitry Belyavskiy Date: Mon, 7 Apr 2025 16:04:26 +0000 (+0200) Subject: Utility for dumping OpenSSL config file X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=78ca45cef0971394522848f49ae397aad5f9e55d;p=thirdparty%2Fopenssl.git Utility for dumping OpenSSL config file Based on @neverpanic code` Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/27290) --- diff --git a/apps/build.info b/apps/build.info index e9565c45ee6..345f7079584 100644 --- a/apps/build.info +++ b/apps/build.info @@ -12,7 +12,7 @@ ENDIF # Source for the 'openssl' program $OPENSSLSRC=\ openssl.c \ - asn1parse.c ca.c ciphers.c crl.c crl2pkcs7.c dgst.c \ + asn1parse.c ca.c ciphers.c configutl.c crl.c crl2pkcs7.c dgst.c \ enc.c errstr.c \ genpkey.c kdf.c mac.c nseq.c passwd.c pkcs7.c \ pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c \ diff --git a/apps/configutl.c b/apps/configutl.c new file mode 100644 index 00000000000..9c6a2518c2a --- /dev/null +++ b/apps/configutl.c @@ -0,0 +1,199 @@ +/* + * Copyright 2025 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "apps.h" +#include "progs.h" + +/** + * Print the given value escaped for the OpenSSL configuration file format. + */ +static void print_escaped_value(BIO *out, const char *value) +{ + const char *p; + + for (p = value; *p != '\0'; p++) { + switch (*p) { + case '"': + case '\'': + case '#': + case '\\': + case '$': + BIO_printf(out, "\\"); + BIO_write(out, p, 1); + break; + case '\n': + BIO_printf(out, "%s", "\\n"); + break; + case '\r': + BIO_printf(out, "%s", "\\r"); + break; + case '\b': + BIO_printf(out, "%s", "\\b"); + break; + case '\t': + BIO_printf(out, "%s", "\\t"); + break; + case ' ': + if (p == value || p[1] == '\0') { + /* + * Quote spaces if they are the first or last char of the + * value. We could quote the entire string (and it would + * certainly produce nicer output), but in quoted strings + * the escape sequences for \n, \r, \t, and \b do not work. + * To make sure we're producing correct results we'd thus + * have to selectively not use those in quoted strings and + * close and re-open the quotes if they appear, which is + * more trouble than adding the quotes just around the + * first and last leading and trailing space. + */ + BIO_printf(out, "%s", "\" \""); + break; + } + /* FALLTHROUGH */ + default: + BIO_write(out, p, 1); + break; + } + } +} + +/** + * Print all values in the configuration section identified by section_name + */ +static void print_section(BIO *out, const CONF *cnf, OPENSSL_CSTRING section_name) +{ + STACK_OF(CONF_VALUE) *values = NCONF_get_section(cnf, section_name); + int idx; + + for (idx = 0; idx < sk_CONF_VALUE_num(values); idx++) { + CONF_VALUE *value = sk_CONF_VALUE_value(values, idx); + + BIO_printf(out, "%s = ", value->name); + print_escaped_value(out, value->value); + BIO_printf(out, "\n"); + } +} + +typedef enum OPTION_choice { + OPT_COMMON, + OPT_OUT, + OPT_NOHEADER, + OPT_CONFIG +} OPTION_CHOICE; + +const OPTIONS configutl_options[] = { + OPT_SECTION("General"), + {"help", OPT_HELP, '-', "Display this summary"}, + {"config", OPT_CONFIG, 's', "Config file to deal with (the default one if omitted)"}, + OPT_SECTION("Output"), + {"out", OPT_OUT, '>', "Output to filename rather than stdout"}, + {"noheader", OPT_NOHEADER, '-', "Don't print the information about original config"}, + {NULL} +}; + +/** + * Parse the passed OpenSSL configuration file (or the default one/specified in the + * OPENSSL_CONF environment variable) and write it back in + * a canonical format with all includes and variables expanded. + */ +int configutl_main(int argc, char *argv[]) +{ + int ret = 1; + char *prog, *configfile = NULL; + OPTION_CHOICE o; + int dump = 1; + CONF *cnf = NULL; + long eline = 0; + int default_section_idx, idx; + int no_header = 0; + STACK_OF(OPENSSL_CSTRING) *sections = NULL; + BIO *out = NULL; + const char *outfile = NULL; + + prog = opt_init(argc, argv, configutl_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(configutl_options); + ret = 0; + goto end; + break; + case OPT_NOHEADER: + no_header = 1; + break; + case OPT_CONFIG: + configfile = OPENSSL_strdup(opt_arg()); + break; + case OPT_OUT: + outfile = opt_arg(); + break; + } + } + + if (dump == 0) + goto opthelp; + + out = bio_open_default(outfile, 'w', FORMAT_TEXT); + if (out == NULL) + goto end; + + if (configfile == NULL) + configfile = CONF_get1_default_config_file(); + + if (configfile == NULL) + goto end; + + if ((cnf = NCONF_new(NULL)) == NULL) + goto end; + + if (NCONF_load(cnf, configfile, &eline) == 0) { + BIO_printf(bio_err, "Error on line %ld of configuration file\n", eline + 1); + goto end; + } + + if ((sections = NCONF_get_section_names(cnf)) == NULL) + goto end; + + if (no_header == 0) + BIO_printf(out, "# This configuration file was linearized and expanded from %s\n", + configfile); + + default_section_idx = sk_OPENSSL_CSTRING_find(sections, "default"); + if (default_section_idx != -1) + print_section(out, cnf, "default"); + + for (idx = 0; idx < sk_OPENSSL_CSTRING_num(sections); idx++) { + OPENSSL_CSTRING section_name = sk_OPENSSL_CSTRING_value(sections, idx); + + if (idx == default_section_idx) + continue; + + BIO_printf(out, "\n[%s]\n", section_name); + print_section(out, cnf, section_name); + } + + ret = 0; + +end: + ERR_print_errors(bio_err); + BIO_free(out); + OPENSSL_free(configfile); + NCONF_free(cnf); + sk_OPENSSL_CSTRING_free(sections); + return ret; +}