Home | History | Annotate | Line # | Download | only in apps
      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