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