| /* |
| * Copyright 2025-2026 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 <openssl/conf.h> |
| #include <openssl/err.h> |
| #include <openssl/safestack.h> |
| |
| #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_puts(out, "\\"); |
| BIO_write(out, p, 1); |
| break; |
| case '\n': |
| BIO_puts(out, "\\n"); |
| break; |
| case '\r': |
| BIO_puts(out, "\\r"); |
| break; |
| case '\b': |
| BIO_puts(out, "\\b"); |
| break; |
| case '\t': |
| BIO_puts(out, "\\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_puts(out, "\" \""); |
| 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_puts(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; |
| 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_HELP: |
| opt_help(configutl_options); |
| ret = 0; |
| goto end; |
| break; |
| case OPT_NOHEADER: |
| no_header = 1; |
| break; |
| case OPT_CONFIG: |
| /* |
| * In case multiple OPT_CONFIG options are passed, we need to free |
| * the previous one before assigning the new one. |
| */ |
| OPENSSL_free(configfile); |
| configfile = OPENSSL_strdup(opt_arg()); |
| break; |
| case OPT_OUT: |
| outfile = opt_arg(); |
| break; |
| case OPT_ERR: |
| /* |
| * default needed for OPT_EOF which might never happen. |
| */ |
| default: |
| BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); |
| goto end; |
| } |
| } |
| |
| 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; |
| } |