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