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