xref: /freebsd/crypto/openssl/apps/storeutl.c (revision 6be3386466ab79a84b48429ae66244f21526d3df)
1 /*
2  * Copyright 2016-2019 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 #include <openssl/x509v3.h>      /* s2i_ASN1_INTEGER */
18 
19 static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
20                    int expected, int criterion, OSSL_STORE_SEARCH *search,
21                    int text, int noout, int recursive, int indent, BIO *out,
22                    const char *prog);
23 
24 typedef enum OPTION_choice {
25     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ENGINE, OPT_OUT, OPT_PASSIN,
26     OPT_NOOUT, OPT_TEXT, OPT_RECURSIVE,
27     OPT_SEARCHFOR_CERTS, OPT_SEARCHFOR_KEYS, OPT_SEARCHFOR_CRLS,
28     OPT_CRITERION_SUBJECT, OPT_CRITERION_ISSUER, OPT_CRITERION_SERIAL,
29     OPT_CRITERION_FINGERPRINT, OPT_CRITERION_ALIAS,
30     OPT_MD
31 } OPTION_CHOICE;
32 
33 const OPTIONS storeutl_options[] = {
34     {OPT_HELP_STR, 1, '-', "Usage: %s [options] uri\nValid options are:\n"},
35     {"help", OPT_HELP, '-', "Display this summary"},
36     {"out", OPT_OUT, '>', "Output file - default stdout"},
37     {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
38     {"text", OPT_TEXT, '-', "Print a text form of the objects"},
39     {"noout", OPT_NOOUT, '-', "No PEM output, just status"},
40     {"certs", OPT_SEARCHFOR_CERTS, '-', "Search for certificates only"},
41     {"keys", OPT_SEARCHFOR_KEYS, '-', "Search for keys only"},
42     {"crls", OPT_SEARCHFOR_CRLS, '-', "Search for CRLs only"},
43     {"subject", OPT_CRITERION_SUBJECT, 's', "Search by subject"},
44     {"issuer", OPT_CRITERION_ISSUER, 's', "Search by issuer and serial, issuer name"},
45     {"serial", OPT_CRITERION_SERIAL, 's', "Search by issuer and serial, serial number"},
46     {"fingerprint", OPT_CRITERION_FINGERPRINT, 's', "Search by public key fingerprint, given in hex"},
47     {"alias", OPT_CRITERION_ALIAS, 's', "Search by alias"},
48     {"", OPT_MD, '-', "Any supported digest"},
49 #ifndef OPENSSL_NO_ENGINE
50     {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
51 #endif
52     {"r", OPT_RECURSIVE, '-', "Recurse through names"},
53     {NULL}
54 };
55 
56 int storeutl_main(int argc, char *argv[])
57 {
58     int ret = 1, noout = 0, text = 0, recursive = 0;
59     char *outfile = NULL, *passin = NULL, *passinarg = NULL;
60     BIO *out = NULL;
61     ENGINE *e = NULL;
62     OPTION_CHOICE o;
63     char *prog = opt_init(argc, argv, storeutl_options);
64     PW_CB_DATA pw_cb_data;
65     int expected = 0;
66     int criterion = 0;
67     X509_NAME *subject = NULL, *issuer = NULL;
68     ASN1_INTEGER *serial = NULL;
69     unsigned char *fingerprint = NULL;
70     size_t fingerprintlen = 0;
71     char *alias = NULL;
72     OSSL_STORE_SEARCH *search = NULL;
73     const EVP_MD *digest = NULL;
74 
75     while ((o = opt_next()) != OPT_EOF) {
76         switch (o) {
77         case OPT_EOF:
78         case OPT_ERR:
79  opthelp:
80             BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
81             goto end;
82         case OPT_HELP:
83             opt_help(storeutl_options);
84             ret = 0;
85             goto end;
86         case OPT_OUT:
87             outfile = opt_arg();
88             break;
89         case OPT_PASSIN:
90             passinarg = opt_arg();
91             break;
92         case OPT_NOOUT:
93             noout = 1;
94             break;
95         case OPT_TEXT:
96             text = 1;
97             break;
98         case OPT_RECURSIVE:
99             recursive = 1;
100             break;
101         case OPT_SEARCHFOR_CERTS:
102         case OPT_SEARCHFOR_KEYS:
103         case OPT_SEARCHFOR_CRLS:
104             if (expected != 0) {
105                 BIO_printf(bio_err, "%s: only one search type can be given.\n",
106                            prog);
107                 goto end;
108             }
109             {
110                 static const struct {
111                     enum OPTION_choice choice;
112                     int type;
113                 } map[] = {
114                     {OPT_SEARCHFOR_CERTS, OSSL_STORE_INFO_CERT},
115                     {OPT_SEARCHFOR_KEYS, OSSL_STORE_INFO_PKEY},
116                     {OPT_SEARCHFOR_CRLS, OSSL_STORE_INFO_CRL},
117                 };
118                 size_t i;
119 
120                 for (i = 0; i < OSSL_NELEM(map); i++) {
121                     if (o == map[i].choice) {
122                         expected = map[i].type;
123                         break;
124                     }
125                 }
126                 /*
127                  * If expected wasn't set at this point, it means the map
128                  * isn't synchronised with the possible options leading here.
129                  */
130                 OPENSSL_assert(expected != 0);
131             }
132             break;
133         case OPT_CRITERION_SUBJECT:
134             if (criterion != 0) {
135                 BIO_printf(bio_err, "%s: criterion already given.\n",
136                            prog);
137                 goto end;
138             }
139             criterion = OSSL_STORE_SEARCH_BY_NAME;
140             if (subject != NULL) {
141                 BIO_printf(bio_err, "%s: subject already given.\n",
142                            prog);
143                 goto end;
144             }
145             if ((subject = parse_name(opt_arg(), MBSTRING_UTF8, 1)) == NULL) {
146                 BIO_printf(bio_err, "%s: can't parse subject argument.\n",
147                            prog);
148                 goto end;
149             }
150             break;
151         case OPT_CRITERION_ISSUER:
152             if (criterion != 0
153                 || (criterion == OSSL_STORE_SEARCH_BY_ISSUER_SERIAL
154                     && issuer != NULL)) {
155                 BIO_printf(bio_err, "%s: criterion already given.\n",
156                            prog);
157                 goto end;
158             }
159             criterion = OSSL_STORE_SEARCH_BY_ISSUER_SERIAL;
160             if (issuer != NULL) {
161                 BIO_printf(bio_err, "%s: issuer already given.\n",
162                            prog);
163                 goto end;
164             }
165             if ((issuer = parse_name(opt_arg(), MBSTRING_UTF8, 1)) == NULL) {
166                 BIO_printf(bio_err, "%s: can't parse issuer argument.\n",
167                            prog);
168                 goto end;
169             }
170             break;
171         case OPT_CRITERION_SERIAL:
172             if (criterion != 0
173                 || (criterion == OSSL_STORE_SEARCH_BY_ISSUER_SERIAL
174                     && serial != NULL)) {
175                 BIO_printf(bio_err, "%s: criterion already given.\n",
176                            prog);
177                 goto end;
178             }
179             criterion = OSSL_STORE_SEARCH_BY_ISSUER_SERIAL;
180             if (serial != NULL) {
181                 BIO_printf(bio_err, "%s: serial number already given.\n",
182                            prog);
183                 goto end;
184             }
185             if ((serial = s2i_ASN1_INTEGER(NULL, opt_arg())) == NULL) {
186                 BIO_printf(bio_err, "%s: can't parse serial number argument.\n",
187                            prog);
188                 goto end;
189             }
190             break;
191         case OPT_CRITERION_FINGERPRINT:
192             if (criterion != 0
193                 || (criterion == OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT
194                     && fingerprint != NULL)) {
195                 BIO_printf(bio_err, "%s: criterion already given.\n",
196                            prog);
197                 goto end;
198             }
199             criterion = OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT;
200             if (fingerprint != NULL) {
201                 BIO_printf(bio_err, "%s: fingerprint already given.\n",
202                            prog);
203                 goto end;
204             }
205             {
206                 long tmplen = 0;
207 
208                 if ((fingerprint = OPENSSL_hexstr2buf(opt_arg(), &tmplen))
209                     == NULL) {
210                     BIO_printf(bio_err,
211                                "%s: can't parse fingerprint argument.\n",
212                                prog);
213                     goto end;
214                 }
215                 fingerprintlen = (size_t)tmplen;
216             }
217             break;
218         case OPT_CRITERION_ALIAS:
219             if (criterion != 0) {
220                 BIO_printf(bio_err, "%s: criterion already given.\n",
221                            prog);
222                 goto end;
223             }
224             criterion = OSSL_STORE_SEARCH_BY_ALIAS;
225             if (alias != NULL) {
226                 BIO_printf(bio_err, "%s: alias already given.\n",
227                            prog);
228                 goto end;
229             }
230             if ((alias = OPENSSL_strdup(opt_arg())) == NULL) {
231                 BIO_printf(bio_err, "%s: can't parse alias argument.\n",
232                            prog);
233                 goto end;
234             }
235             break;
236         case OPT_ENGINE:
237             e = setup_engine(opt_arg(), 0);
238             break;
239         case OPT_MD:
240             if (!opt_md(opt_unknown(), &digest))
241                 goto opthelp;
242         }
243     }
244     argc = opt_num_rest();
245     argv = opt_rest();
246 
247     if (argc == 0) {
248         BIO_printf(bio_err, "%s: No URI given, nothing to do...\n", prog);
249         goto opthelp;
250     }
251     if (argc > 1) {
252         BIO_printf(bio_err, "%s: Unknown extra parameters after URI\n", prog);
253         goto opthelp;
254     }
255 
256     if (criterion != 0) {
257         switch (criterion) {
258         case OSSL_STORE_SEARCH_BY_NAME:
259             if ((search = OSSL_STORE_SEARCH_by_name(subject)) == NULL) {
260                 ERR_print_errors(bio_err);
261                 goto end;
262             }
263             break;
264         case OSSL_STORE_SEARCH_BY_ISSUER_SERIAL:
265             if (issuer == NULL || serial == NULL) {
266                 BIO_printf(bio_err,
267                            "%s: both -issuer and -serial must be given.\n",
268                            prog);
269                 goto end;
270             }
271             if ((search = OSSL_STORE_SEARCH_by_issuer_serial(issuer, serial))
272                 == NULL) {
273                 ERR_print_errors(bio_err);
274                 goto end;
275             }
276             break;
277         case OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT:
278             if ((search = OSSL_STORE_SEARCH_by_key_fingerprint(digest,
279                                                                fingerprint,
280                                                                fingerprintlen))
281                 == NULL) {
282                 ERR_print_errors(bio_err);
283                 goto end;
284             }
285             break;
286         case OSSL_STORE_SEARCH_BY_ALIAS:
287             if ((search = OSSL_STORE_SEARCH_by_alias(alias)) == NULL) {
288                 ERR_print_errors(bio_err);
289                 goto end;
290             }
291             break;
292         }
293     }
294 
295     if (!app_passwd(passinarg, NULL, &passin, NULL)) {
296         BIO_printf(bio_err, "Error getting passwords\n");
297         goto end;
298     }
299     pw_cb_data.password = passin;
300     pw_cb_data.prompt_info = argv[0];
301 
302     out = bio_open_default(outfile, 'w', FORMAT_TEXT);
303     if (out == NULL)
304         goto end;
305 
306     ret = process(argv[0], get_ui_method(), &pw_cb_data,
307                   expected, criterion, search,
308                   text, noout, recursive, 0, out, prog);
309 
310  end:
311     OPENSSL_free(fingerprint);
312     OPENSSL_free(alias);
313     ASN1_INTEGER_free(serial);
314     X509_NAME_free(subject);
315     X509_NAME_free(issuer);
316     OSSL_STORE_SEARCH_free(search);
317     BIO_free_all(out);
318     OPENSSL_free(passin);
319     release_engine(e);
320     return ret;
321 }
322 
323 static int indent_printf(int indent, BIO *bio, const char *format, ...)
324 {
325     va_list args;
326     int ret;
327 
328     va_start(args, format);
329 
330     ret = BIO_printf(bio, "%*s", indent, "") + BIO_vprintf(bio, format, args);
331 
332     va_end(args);
333     return ret;
334 }
335 
336 static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
337                    int expected, int criterion, OSSL_STORE_SEARCH *search,
338                    int text, int noout, int recursive, int indent, BIO *out,
339                    const char *prog)
340 {
341     OSSL_STORE_CTX *store_ctx = NULL;
342     int ret = 1, items = 0;
343 
344     if ((store_ctx = OSSL_STORE_open(uri, uimeth, uidata, NULL, NULL))
345         == NULL) {
346         BIO_printf(bio_err, "Couldn't open file or uri %s\n", uri);
347         ERR_print_errors(bio_err);
348         return ret;
349     }
350 
351     if (expected != 0) {
352         if (!OSSL_STORE_expect(store_ctx, expected)) {
353             ERR_print_errors(bio_err);
354             goto end2;
355         }
356     }
357 
358     if (criterion != 0) {
359         if (!OSSL_STORE_supports_search(store_ctx, criterion)) {
360             BIO_printf(bio_err,
361                        "%s: the store scheme doesn't support the given search criteria.\n",
362                        prog);
363             goto end2;
364         }
365 
366         if (!OSSL_STORE_find(store_ctx, search)) {
367             ERR_print_errors(bio_err);
368             goto end2;
369         }
370     }
371 
372     /* From here on, we count errors, and we'll return the count at the end */
373     ret = 0;
374 
375     for (;;) {
376         OSSL_STORE_INFO *info = OSSL_STORE_load(store_ctx);
377         int type = info == NULL ? 0 : OSSL_STORE_INFO_get_type(info);
378         const char *infostr =
379             info == NULL ? NULL : OSSL_STORE_INFO_type_string(type);
380 
381         if (info == NULL) {
382             if (OSSL_STORE_eof(store_ctx))
383                 break;
384 
385             if (OSSL_STORE_error(store_ctx)) {
386                 if (recursive)
387                     ERR_clear_error();
388                 else
389                     ERR_print_errors(bio_err);
390                 ret++;
391                 continue;
392             }
393 
394             BIO_printf(bio_err,
395                        "ERROR: OSSL_STORE_load() returned NULL without "
396                        "eof or error indications\n");
397             BIO_printf(bio_err, "       This is an error in the loader\n");
398             ERR_print_errors(bio_err);
399             ret++;
400             break;
401         }
402 
403         if (type == OSSL_STORE_INFO_NAME) {
404             const char *name = OSSL_STORE_INFO_get0_NAME(info);
405             const char *desc = OSSL_STORE_INFO_get0_NAME_description(info);
406             indent_printf(indent, bio_out, "%d: %s: %s\n", items, infostr,
407                           name);
408             if (desc != NULL)
409                 indent_printf(indent, bio_out, "%s\n", desc);
410         } else {
411             indent_printf(indent, bio_out, "%d: %s\n", items, infostr);
412         }
413 
414         /*
415          * Unfortunately, PEM_X509_INFO_write_bio() is sorely lacking in
416          * functionality, so we must figure out how exactly to write things
417          * ourselves...
418          */
419         switch (type) {
420         case OSSL_STORE_INFO_NAME:
421             if (recursive) {
422                 const char *suburi = OSSL_STORE_INFO_get0_NAME(info);
423                 ret += process(suburi, uimeth, uidata,
424                                expected, criterion, search,
425                                text, noout, recursive, indent + 2, out, prog);
426             }
427             break;
428         case OSSL_STORE_INFO_PARAMS:
429             if (text)
430                 EVP_PKEY_print_params(out, OSSL_STORE_INFO_get0_PARAMS(info),
431                                       0, NULL);
432             if (!noout)
433                 PEM_write_bio_Parameters(out,
434                                          OSSL_STORE_INFO_get0_PARAMS(info));
435             break;
436         case OSSL_STORE_INFO_PKEY:
437             if (text)
438                 EVP_PKEY_print_private(out, OSSL_STORE_INFO_get0_PKEY(info),
439                                        0, NULL);
440             if (!noout)
441                 PEM_write_bio_PrivateKey(out, OSSL_STORE_INFO_get0_PKEY(info),
442                                          NULL, NULL, 0, NULL, NULL);
443             break;
444         case OSSL_STORE_INFO_CERT:
445             if (text)
446                 X509_print(out, OSSL_STORE_INFO_get0_CERT(info));
447             if (!noout)
448                 PEM_write_bio_X509(out, OSSL_STORE_INFO_get0_CERT(info));
449             break;
450         case OSSL_STORE_INFO_CRL:
451             if (text)
452                 X509_CRL_print(out, OSSL_STORE_INFO_get0_CRL(info));
453             if (!noout)
454                 PEM_write_bio_X509_CRL(out, OSSL_STORE_INFO_get0_CRL(info));
455             break;
456         default:
457             BIO_printf(bio_err, "!!! Unknown code\n");
458             ret++;
459             break;
460         }
461         items++;
462         OSSL_STORE_INFO_free(info);
463     }
464     indent_printf(indent, out, "Total found: %d\n", items);
465 
466  end2:
467     if (!OSSL_STORE_close(store_ctx)) {
468         ERR_print_errors(bio_err);
469         ret++;
470     }
471 
472     return ret;
473 }
474