1 /*
2 * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <openssl/conf.h>
11 #include <openssl/err.h>
12 #include <openssl/safestack.h>
13
14 #include "apps.h"
15 #include "progs.h"
16
17 /**
18 * Print the given value escaped for the OpenSSL configuration file format.
19 */
print_escaped_value(BIO * out,const char * value)20 static void print_escaped_value(BIO *out, const char *value)
21 {
22 const char *p;
23
24 for (p = value; *p != '\0'; p++) {
25 switch (*p) {
26 case '"':
27 case '\'':
28 case '#':
29 case '\\':
30 case '$':
31 BIO_printf(out, "\\");
32 BIO_write(out, p, 1);
33 break;
34 case '\n':
35 BIO_printf(out, "%s", "\\n");
36 break;
37 case '\r':
38 BIO_printf(out, "%s", "\\r");
39 break;
40 case '\b':
41 BIO_printf(out, "%s", "\\b");
42 break;
43 case '\t':
44 BIO_printf(out, "%s", "\\t");
45 break;
46 case ' ':
47 if (p == value || p[1] == '\0') {
48 /*
49 * Quote spaces if they are the first or last char of the
50 * value. We could quote the entire string (and it would
51 * certainly produce nicer output), but in quoted strings
52 * the escape sequences for \n, \r, \t, and \b do not work.
53 * To make sure we're producing correct results we'd thus
54 * have to selectively not use those in quoted strings and
55 * close and re-open the quotes if they appear, which is
56 * more trouble than adding the quotes just around the
57 * first and last leading and trailing space.
58 */
59 BIO_printf(out, "%s", "\" \"");
60 break;
61 }
62 /* FALLTHROUGH */
63 default:
64 BIO_write(out, p, 1);
65 break;
66 }
67 }
68 }
69
70 /**
71 * Print all values in the configuration section identified by section_name
72 */
print_section(BIO * out,const CONF * cnf,OPENSSL_CSTRING section_name)73 static void print_section(BIO *out, const CONF *cnf, OPENSSL_CSTRING section_name)
74 {
75 STACK_OF(CONF_VALUE) *values = NCONF_get_section(cnf, section_name);
76 int idx;
77
78 for (idx = 0; idx < sk_CONF_VALUE_num(values); idx++) {
79 CONF_VALUE *value = sk_CONF_VALUE_value(values, idx);
80
81 BIO_printf(out, "%s = ", value->name);
82 print_escaped_value(out, value->value);
83 BIO_printf(out, "\n");
84 }
85 }
86
87 typedef enum OPTION_choice {
88 OPT_COMMON,
89 OPT_OUT,
90 OPT_NOHEADER,
91 OPT_CONFIG
92 } OPTION_CHOICE;
93
94 const OPTIONS configutl_options[] = {
95 OPT_SECTION("General"),
96 {"help", OPT_HELP, '-', "Display this summary"},
97 {"config", OPT_CONFIG, 's', "Config file to deal with (the default one if omitted)"},
98 OPT_SECTION("Output"),
99 {"out", OPT_OUT, '>', "Output to filename rather than stdout"},
100 {"noheader", OPT_NOHEADER, '-', "Don't print the information about original config"},
101 {NULL}
102 };
103
104 /**
105 * Parse the passed OpenSSL configuration file (or the default one/specified in the
106 * OPENSSL_CONF environment variable) and write it back in
107 * a canonical format with all includes and variables expanded.
108 */
configutl_main(int argc,char * argv[])109 int configutl_main(int argc, char *argv[])
110 {
111 int ret = 1;
112 char *prog, *configfile = NULL;
113 OPTION_CHOICE o;
114 CONF *cnf = NULL;
115 long eline = 0;
116 int default_section_idx, idx;
117 int no_header = 0;
118 STACK_OF(OPENSSL_CSTRING) *sections = NULL;
119 BIO *out = NULL;
120 const char *outfile = NULL;
121
122 prog = opt_init(argc, argv, configutl_options);
123 while ((o = opt_next()) != OPT_EOF) {
124 switch (o) {
125 case OPT_HELP:
126 opt_help(configutl_options);
127 ret = 0;
128 goto end;
129 break;
130 case OPT_NOHEADER:
131 no_header = 1;
132 break;
133 case OPT_CONFIG:
134 /*
135 * In case multiple OPT_CONFIG options are passed, we need to free
136 * the previous one before assigning the new one.
137 */
138 OPENSSL_free(configfile);
139 configfile = OPENSSL_strdup(opt_arg());
140 break;
141 case OPT_OUT:
142 outfile = opt_arg();
143 break;
144 case OPT_ERR:
145 /*
146 * default needed for OPT_EOF which might never happen.
147 */
148 default:
149 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
150 goto end;
151 }
152 }
153
154 out = bio_open_default(outfile, 'w', FORMAT_TEXT);
155 if (out == NULL)
156 goto end;
157
158 if (configfile == NULL)
159 configfile = CONF_get1_default_config_file();
160
161 if (configfile == NULL)
162 goto end;
163
164 if ((cnf = NCONF_new(NULL)) == NULL)
165 goto end;
166
167 if (NCONF_load(cnf, configfile, &eline) == 0) {
168 BIO_printf(bio_err, "Error on line %ld of configuration file\n", eline + 1);
169 goto end;
170 }
171
172 if ((sections = NCONF_get_section_names(cnf)) == NULL)
173 goto end;
174
175 if (no_header == 0)
176 BIO_printf(out, "# This configuration file was linearized and expanded from %s\n",
177 configfile);
178
179 default_section_idx = sk_OPENSSL_CSTRING_find(sections, "default");
180 if (default_section_idx != -1)
181 print_section(out, cnf, "default");
182
183 for (idx = 0; idx < sk_OPENSSL_CSTRING_num(sections); idx++) {
184 OPENSSL_CSTRING section_name = sk_OPENSSL_CSTRING_value(sections, idx);
185
186 if (idx == default_section_idx)
187 continue;
188
189 BIO_printf(out, "\n[%s]\n", section_name);
190 print_section(out, cnf, section_name);
191 }
192
193 ret = 0;
194
195 end:
196 ERR_print_errors(bio_err);
197 BIO_free(out);
198 OPENSSL_free(configfile);
199 NCONF_free(cnf);
200 sk_OPENSSL_CSTRING_free(sections);
201 return ret;
202 }
203