1 /* $OpenBSD: crl.c,v 1.17 2023/03/06 14:32:05 tb Exp $ */ 2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay@cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay@cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 63 #include "apps.h" 64 65 #include <openssl/bio.h> 66 #include <openssl/err.h> 67 #include <openssl/pem.h> 68 #include <openssl/x509.h> 69 #include <openssl/x509v3.h> 70 71 static struct { 72 char *cafile; 73 char *capath; 74 int crlnumber; 75 int fingerprint; 76 int hash; 77 int hash_old; 78 char *infile; 79 int informat; 80 int issuer; 81 int lastupdate; 82 char *nameopt; 83 int nextupdate; 84 int noout; 85 char *outfile; 86 int outformat; 87 int text; 88 int verify; 89 } cfg; 90 91 static const struct option crl_options[] = { 92 { 93 .name = "CAfile", 94 .argname = "file", 95 .desc = "Verify the CRL using certificates in the given file", 96 .type = OPTION_ARG, 97 .opt.arg = &cfg.cafile, 98 }, 99 { 100 .name = "CApath", 101 .argname = "path", 102 .desc = "Verify the CRL using certificates in the given path", 103 .type = OPTION_ARG, 104 .opt.arg = &cfg.capath, 105 }, 106 { 107 .name = "crlnumber", 108 .desc = "Print the CRL number", 109 .type = OPTION_FLAG_ORD, 110 .opt.flag = &cfg.crlnumber, 111 }, 112 { 113 .name = "fingerprint", 114 .desc = "Print the CRL fingerprint", 115 .type = OPTION_FLAG_ORD, 116 .opt.flag = &cfg.fingerprint, 117 }, 118 { 119 .name = "hash", 120 .desc = "Print the hash of the issuer name", 121 .type = OPTION_FLAG_ORD, 122 .opt.flag = &cfg.hash, 123 }, 124 { 125 .name = "hash_old", 126 .desc = "Print an old-style (MD5) hash of the issuer name", 127 .type = OPTION_FLAG_ORD, 128 .opt.flag = &cfg.hash_old, 129 }, 130 { 131 .name = "in", 132 .argname = "file", 133 .desc = "Input file to read from (stdin if unspecified)", 134 .type = OPTION_ARG, 135 .opt.arg = &cfg.infile, 136 }, 137 { 138 .name = "inform", 139 .argname = "format", 140 .desc = "Input format (DER or PEM)", 141 .type = OPTION_ARG_FORMAT, 142 .opt.value = &cfg.informat, 143 }, 144 { 145 .name = "issuer", 146 .desc = "Print the issuer name", 147 .type = OPTION_FLAG_ORD, 148 .opt.flag = &cfg.issuer, 149 }, 150 { 151 .name = "lastupdate", 152 .desc = "Print the lastUpdate field", 153 .type = OPTION_FLAG_ORD, 154 .opt.flag = &cfg.lastupdate, 155 }, 156 { 157 .name = "nameopt", 158 .argname = "options", 159 .desc = "Specify certificate name options", 160 .type = OPTION_ARG, 161 .opt.arg = &cfg.nameopt, 162 }, 163 { 164 .name = "nextupdate", 165 .desc = "Print the nextUpdate field", 166 .type = OPTION_FLAG_ORD, 167 .opt.flag = &cfg.nextupdate, 168 }, 169 { 170 .name = "noout", 171 .desc = "Do not output the encoded version of the CRL", 172 .type = OPTION_FLAG, 173 .opt.flag = &cfg.noout, 174 }, 175 { 176 .name = "out", 177 .argname = "file", 178 .desc = "Output file to write to (stdout if unspecified)", 179 .type = OPTION_ARG, 180 .opt.arg = &cfg.outfile, 181 }, 182 { 183 .name = "outform", 184 .argname = "format", 185 .desc = "Output format (DER or PEM)", 186 .type = OPTION_ARG_FORMAT, 187 .opt.value = &cfg.outformat, 188 }, 189 { 190 .name = "text", 191 .desc = "Print out the CRL in text form", 192 .type = OPTION_FLAG, 193 .opt.flag = &cfg.text, 194 }, 195 { 196 .name = "verify", 197 .desc = "Verify the signature on the CRL", 198 .type = OPTION_FLAG, 199 .opt.flag = &cfg.verify, 200 }, 201 {NULL}, 202 }; 203 204 static void 205 crl_usage(void) 206 { 207 fprintf(stderr, 208 "usage: crl [-CAfile file] [-CApath dir] [-fingerprint] [-hash]\n" 209 " [-in file] [-inform DER | PEM] [-issuer] [-lastupdate]\n" 210 " [-nextupdate] [-noout] [-out file] [-outform DER | PEM]\n" 211 " [-text]\n\n"); 212 options_usage(crl_options); 213 } 214 215 static X509_CRL *load_crl(char *file, int format); 216 static BIO *bio_out = NULL; 217 218 int 219 crl_main(int argc, char **argv) 220 { 221 unsigned long nmflag = 0; 222 X509_CRL *x = NULL; 223 int ret = 1, i; 224 BIO *out = NULL; 225 X509_STORE *store = NULL; 226 X509_STORE_CTX *ctx = NULL; 227 X509_LOOKUP *lookup = NULL; 228 X509_OBJECT *xobj = NULL; 229 EVP_PKEY *pkey; 230 const EVP_MD *digest; 231 char *digest_name = NULL; 232 233 if (pledge("stdio cpath wpath rpath", NULL) == -1) { 234 perror("pledge"); 235 exit(1); 236 } 237 238 if (bio_out == NULL) { 239 if ((bio_out = BIO_new(BIO_s_file())) != NULL) { 240 BIO_set_fp(bio_out, stdout, BIO_NOCLOSE); 241 } 242 } 243 244 digest = EVP_sha256(); 245 246 memset(&cfg, 0, sizeof(cfg)); 247 cfg.informat = FORMAT_PEM; 248 cfg.outformat = FORMAT_PEM; 249 250 if (options_parse(argc, argv, crl_options, &digest_name, NULL) != 0) { 251 crl_usage(); 252 goto end; 253 } 254 255 if (cfg.cafile != NULL || cfg.capath != NULL) 256 cfg.verify = 1; 257 258 if (cfg.nameopt != NULL) { 259 if (set_name_ex(&nmflag, cfg.nameopt) != 1) { 260 fprintf(stderr, 261 "Invalid -nameopt argument '%s'\n", 262 cfg.nameopt); 263 goto end; 264 } 265 } 266 267 if (digest_name != NULL) { 268 if ((digest = EVP_get_digestbyname(digest_name)) == NULL) { 269 fprintf(stderr, 270 "Unknown message digest algorithm '%s'\n", 271 digest_name); 272 goto end; 273 } 274 } 275 276 x = load_crl(cfg.infile, cfg.informat); 277 if (x == NULL) 278 goto end; 279 280 if (cfg.verify) { 281 store = X509_STORE_new(); 282 if (store == NULL) 283 goto end; 284 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 285 if (lookup == NULL) 286 goto end; 287 if (!X509_LOOKUP_load_file(lookup, cfg.cafile, 288 X509_FILETYPE_PEM)) 289 X509_LOOKUP_load_file(lookup, NULL, 290 X509_FILETYPE_DEFAULT); 291 292 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); 293 if (lookup == NULL) 294 goto end; 295 if (!X509_LOOKUP_add_dir(lookup, cfg.capath, 296 X509_FILETYPE_PEM)) 297 X509_LOOKUP_add_dir(lookup, NULL, 298 X509_FILETYPE_DEFAULT); 299 ERR_clear_error(); 300 301 if ((ctx = X509_STORE_CTX_new()) == NULL) 302 goto end; 303 if ((xobj = X509_OBJECT_new()) == NULL) 304 goto end; 305 306 if (!X509_STORE_CTX_init(ctx, store, NULL, NULL)) { 307 BIO_printf(bio_err, 308 "Error initialising X509 store\n"); 309 goto end; 310 } 311 i = X509_STORE_get_by_subject(ctx, X509_LU_X509, 312 X509_CRL_get_issuer(x), xobj); 313 if (i <= 0) { 314 BIO_printf(bio_err, 315 "Error getting CRL issuer certificate\n"); 316 goto end; 317 } 318 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj)); 319 X509_OBJECT_free(xobj); 320 xobj = NULL; 321 if (!pkey) { 322 BIO_printf(bio_err, 323 "Error getting CRL issuer public key\n"); 324 goto end; 325 } 326 i = X509_CRL_verify(x, pkey); 327 EVP_PKEY_free(pkey); 328 if (i < 0) 329 goto end; 330 if (i == 0) 331 BIO_printf(bio_err, "verify failure\n"); 332 else 333 BIO_printf(bio_err, "verify OK\n"); 334 } 335 336 /* Print requested information the order that the flags were given. */ 337 for (i = 1; i <= argc; i++) { 338 if (cfg.issuer == i) { 339 print_name(bio_out, "issuer=", 340 X509_CRL_get_issuer(x), nmflag); 341 } 342 if (cfg.crlnumber == i) { 343 ASN1_INTEGER *crlnum; 344 crlnum = X509_CRL_get_ext_d2i(x, 345 NID_crl_number, NULL, NULL); 346 BIO_printf(bio_out, "crlNumber="); 347 if (crlnum) { 348 i2a_ASN1_INTEGER(bio_out, crlnum); 349 ASN1_INTEGER_free(crlnum); 350 } else 351 BIO_puts(bio_out, "<NONE>"); 352 BIO_printf(bio_out, "\n"); 353 } 354 if (cfg.hash == i) { 355 BIO_printf(bio_out, "%08lx\n", 356 X509_NAME_hash(X509_CRL_get_issuer(x))); 357 } 358 #ifndef OPENSSL_NO_MD5 359 if (cfg.hash_old == i) { 360 BIO_printf(bio_out, "%08lx\n", 361 X509_NAME_hash_old(X509_CRL_get_issuer(x))); 362 } 363 #endif 364 if (cfg.lastupdate == i) { 365 BIO_printf(bio_out, "lastUpdate="); 366 ASN1_TIME_print(bio_out, 367 X509_CRL_get_lastUpdate(x)); 368 BIO_printf(bio_out, "\n"); 369 } 370 if (cfg.nextupdate == i) { 371 BIO_printf(bio_out, "nextUpdate="); 372 if (X509_CRL_get_nextUpdate(x)) 373 ASN1_TIME_print(bio_out, 374 X509_CRL_get_nextUpdate(x)); 375 else 376 BIO_printf(bio_out, "NONE"); 377 BIO_printf(bio_out, "\n"); 378 } 379 if (cfg.fingerprint == i) { 380 int j; 381 unsigned int n; 382 unsigned char md[EVP_MAX_MD_SIZE]; 383 384 if (!X509_CRL_digest(x, digest, md, &n)) { 385 BIO_printf(bio_err, "out of memory\n"); 386 goto end; 387 } 388 BIO_printf(bio_out, "%s Fingerprint=", 389 OBJ_nid2sn(EVP_MD_type(digest))); 390 for (j = 0; j < (int) n; j++) { 391 BIO_printf(bio_out, "%02X%c", md[j], 392 (j + 1 == (int)n) ? '\n' : ':'); 393 } 394 } 395 } 396 397 out = BIO_new(BIO_s_file()); 398 if (out == NULL) { 399 ERR_print_errors(bio_err); 400 goto end; 401 } 402 if (cfg.outfile == NULL) { 403 BIO_set_fp(out, stdout, BIO_NOCLOSE); 404 } else { 405 if (BIO_write_filename(out, cfg.outfile) <= 0) { 406 perror(cfg.outfile); 407 goto end; 408 } 409 } 410 411 if (cfg.text) 412 X509_CRL_print(out, x); 413 414 if (cfg.noout) { 415 ret = 0; 416 goto end; 417 } 418 if (cfg.outformat == FORMAT_ASN1) 419 i = (int) i2d_X509_CRL_bio(out, x); 420 else if (cfg.outformat == FORMAT_PEM) 421 i = PEM_write_bio_X509_CRL(out, x); 422 else { 423 BIO_printf(bio_err, 424 "bad output format specified for outfile\n"); 425 goto end; 426 } 427 if (!i) { 428 BIO_printf(bio_err, "unable to write CRL\n"); 429 goto end; 430 } 431 ret = 0; 432 433 end: 434 BIO_free_all(out); 435 BIO_free_all(bio_out); 436 bio_out = NULL; 437 X509_CRL_free(x); 438 X509_STORE_CTX_free(ctx); 439 X509_STORE_free(store); 440 X509_OBJECT_free(xobj); 441 442 return (ret); 443 } 444 445 static X509_CRL * 446 load_crl(char *infile, int format) 447 { 448 X509_CRL *x = NULL; 449 BIO *in = NULL; 450 451 in = BIO_new(BIO_s_file()); 452 if (in == NULL) { 453 ERR_print_errors(bio_err); 454 goto end; 455 } 456 if (infile == NULL) 457 BIO_set_fp(in, stdin, BIO_NOCLOSE); 458 else { 459 if (BIO_read_filename(in, infile) <= 0) { 460 perror(infile); 461 goto end; 462 } 463 } 464 if (format == FORMAT_ASN1) 465 x = d2i_X509_CRL_bio(in, NULL); 466 else if (format == FORMAT_PEM) 467 x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); 468 else { 469 BIO_printf(bio_err, 470 "bad input format specified for input crl\n"); 471 goto end; 472 } 473 if (x == NULL) { 474 BIO_printf(bio_err, "unable to load CRL\n"); 475 ERR_print_errors(bio_err); 476 goto end; 477 } 478 479 end: 480 BIO_free(in); 481 return (x); 482 } 483