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