Make sure that apps/openssl prefixes its output with '# ' during tests
[openssl.git] / apps / storeutl.c
1 /*
2  * Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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/opensslconf.h>
11
12 #include "apps.h"
13 #include "progs.h"
14 #include <openssl/err.h>
15 #include <openssl/pem.h>
16 #include <openssl/store.h>
17
18 static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
19                    int text, int noout, int recursive, int indent, BIO *out);
20
21 typedef enum OPTION_choice {
22     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ENGINE, OPT_OUT, OPT_PASSIN,
23     OPT_NOOUT, OPT_TEXT, OPT_RECURSIVE
24 } OPTION_CHOICE;
25
26 const OPTIONS storeutl_options[] = {
27     {OPT_HELP_STR, 1, '-', "Usage: %s [options] uri\nValid options are:\n"},
28     {"help", OPT_HELP, '-', "Display this summary"},
29     {"out", OPT_OUT, '>', "Output file - default stdout"},
30     {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
31     {"text", OPT_TEXT, '-', "Print a text form of the objects"},
32     {"noout", OPT_NOOUT, '-', "No PEM output, just status"},
33 #ifndef OPENSSL_NO_ENGINE
34     {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
35 #endif
36     {"r", OPT_RECURSIVE, '-', "Recurse through names"},
37     {NULL}
38 };
39
40 int storeutl_main(int argc, char *argv[])
41 {
42     int ret = 1, noout = 0, text = 0, recursive = 0;
43     char *outfile = NULL, *passin = NULL, *passinarg = NULL;
44     BIO *out = NULL;
45     ENGINE *e = NULL;
46     OPTION_CHOICE o;
47     char *prog = opt_init(argc, argv, storeutl_options);
48     PW_CB_DATA pw_cb_data;
49
50     while ((o = opt_next()) != OPT_EOF) {
51         switch (o) {
52         case OPT_EOF:
53         case OPT_ERR:
54  opthelp:
55             BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
56             goto end;
57         case OPT_HELP:
58             opt_help(storeutl_options);
59             ret = 0;
60             goto end;
61         case OPT_OUT:
62             outfile = opt_arg();
63             break;
64         case OPT_PASSIN:
65             passinarg = opt_arg();
66             break;
67         case OPT_NOOUT:
68             noout = 1;
69             break;
70         case OPT_TEXT:
71             text = 1;
72             break;
73         case OPT_RECURSIVE:
74             recursive = 1;
75             break;
76         case OPT_ENGINE:
77             e = setup_engine(opt_arg(), 0);
78             break;
79         }
80     }
81     argc = opt_num_rest();
82     argv = opt_rest();
83
84     if (argc == 0) {
85         BIO_printf(bio_err, "%s: No URI given, nothing to do...\n", prog);
86         goto opthelp;
87     }
88     if (argc > 1) {
89         BIO_printf(bio_err, "%s: Unknown extra parameters after URI\n", prog);
90         goto opthelp;
91     }
92
93     if (!app_passwd(passinarg, NULL, &passin, NULL)) {
94         BIO_printf(bio_err, "Error getting passwords\n");
95         goto end;
96     }
97     pw_cb_data.password = passin;
98     pw_cb_data.prompt_info = argv[0];
99
100     out = bio_open_default(outfile, 'w', FORMAT_TEXT);
101     if (out == NULL)
102         goto end;
103
104     ret = process(argv[0], get_ui_method(), &pw_cb_data, text, noout, recursive,
105                   0, out);
106
107  end:
108     BIO_free_all(out);
109     OPENSSL_free(passin);
110     release_engine(e);
111     return ret;
112 }
113
114 static int indent_printf(int indent, BIO *bio, const char *format, ...)
115 {
116     va_list args;
117     int ret;
118
119     va_start(args, format);
120
121     ret = BIO_printf(bio, "%*s", indent, "") + BIO_vprintf(bio, format, args);
122
123     va_end(args);
124     return ret;
125 }
126
127 static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
128                    int text, int noout, int recursive, int indent, BIO *out)
129 {
130     OSSL_STORE_CTX *store_ctx = NULL;
131     int ret = 1, items = 0;
132
133     if ((store_ctx = OSSL_STORE_open(uri, uimeth, uidata, NULL, NULL))
134         == NULL) {
135         BIO_printf(bio_err, "Couldn't open file or uri %s\n", uri);
136         ERR_print_errors(bio_err);
137         return ret;
138     }
139
140     /* From here on, we count errors, and we'll return the count at the end */
141     ret = 0;
142
143     for (;;) {
144         OSSL_STORE_INFO *info = OSSL_STORE_load(store_ctx);
145         int type = info == NULL ? 0 : OSSL_STORE_INFO_get_type(info);
146         const char *infostr =
147             info == NULL ? NULL : OSSL_STORE_INFO_type_string(type);
148
149         if (info == NULL) {
150             if (OSSL_STORE_eof(store_ctx))
151                 break;
152
153             if (OSSL_STORE_error(store_ctx)) {
154                 if (recursive)
155                     ERR_clear_error();
156                 else
157                     ERR_print_errors(bio_err);
158                 ret++;
159                 continue;
160             }
161
162             BIO_printf(bio_err,
163                        "ERROR: OSSL_STORE_load() returned NULL without "
164                        "eof or error indications\n");
165             BIO_printf(bio_err, "       This is an error in the loader\n");
166             ERR_print_errors(bio_err);
167             ret++;
168             break;
169         }
170
171         if (type == OSSL_STORE_INFO_NAME) {
172             const char *name = OSSL_STORE_INFO_get0_NAME(info);
173             const char *desc = OSSL_STORE_INFO_get0_NAME_description(info);
174             indent_printf(indent, bio_out, "%d: %s: %s\n", items, infostr,
175                           name);
176             if (desc != NULL)
177                 indent_printf(indent, bio_out, "%s\n", desc);
178         } else {
179             indent_printf(indent, bio_out, "%d: %s\n", items, infostr);
180         }
181
182         /*
183          * Unfortunately, PEM_X509_INFO_write_bio() is sorely lacking in
184          * functionality, so we must figure out how exactly to write things
185          * ourselves...
186          */
187         switch (type) {
188         case OSSL_STORE_INFO_NAME:
189             if (recursive) {
190                 const char *suburi = OSSL_STORE_INFO_get0_NAME(info);
191                 ret += process(suburi, uimeth, uidata, text, noout, recursive,
192                                indent + 2, out);
193             }
194             break;
195         case OSSL_STORE_INFO_PARAMS:
196             if (text)
197                 EVP_PKEY_print_params(out, OSSL_STORE_INFO_get0_PARAMS(info),
198                                       0, NULL);
199             if (!noout)
200                 PEM_write_bio_Parameters(out,
201                                          OSSL_STORE_INFO_get0_PARAMS(info));
202             break;
203         case OSSL_STORE_INFO_PKEY:
204             if (text)
205                 EVP_PKEY_print_private(out, OSSL_STORE_INFO_get0_PKEY(info),
206                                        0, NULL);
207             if (!noout)
208                 PEM_write_bio_PrivateKey(out, OSSL_STORE_INFO_get0_PKEY(info),
209                                          NULL, NULL, 0, NULL, NULL);
210             break;
211         case OSSL_STORE_INFO_CERT:
212             if (text)
213                 X509_print(out, OSSL_STORE_INFO_get0_CERT(info));
214             if (!noout)
215                 PEM_write_bio_X509(out, OSSL_STORE_INFO_get0_CERT(info));
216             break;
217         case OSSL_STORE_INFO_CRL:
218             if (text)
219                 X509_CRL_print(out, OSSL_STORE_INFO_get0_CRL(info));
220             if (!noout)
221                 PEM_write_bio_X509_CRL(out, OSSL_STORE_INFO_get0_CRL(info));
222             break;
223         default:
224             BIO_printf(bio_err, "!!! Unknown code\n");
225             ret++;
226             break;
227         }
228         items++;
229         OSSL_STORE_INFO_free(info);
230     }
231     indent_printf(indent, out, "Total found: %d\n", items);
232
233     if (!OSSL_STORE_close(store_ctx)) {
234         ERR_print_errors(bio_err);
235         ret++;
236     }
237
238     return ret;
239 }