1 /* $OpenBSD: crl.c,v 1.15 2021/10/31 16:47:27 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 } crl_config; 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 = &crl_config.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 = &crl_config.capath, 105 }, 106 { 107 .name = "crlnumber", 108 .desc = "Print the CRL number", 109 .type = OPTION_FLAG_ORD, 110 .opt.flag = &crl_config.crlnumber, 111 }, 112 { 113 .name = "fingerprint", 114 .desc = "Print the CRL fingerprint", 115 .type = OPTION_FLAG_ORD, 116 .opt.flag = &crl_config.fingerprint, 117 }, 118 { 119 .name = "hash", 120 .desc = "Print the hash of the issuer name", 121 .type = OPTION_FLAG_ORD, 122 .opt.flag = &crl_config.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 = &crl_config.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 = &crl_config.infile, 136 }, 137 { 138 .name = "inform", 139 .argname = "format", 140 .desc = "Input format (DER or PEM)", 141 .type = OPTION_ARG_FORMAT, 142 .opt.value = &crl_config.informat, 143 }, 144 { 145 .name = "issuer", 146 .desc = "Print the issuer name", 147 .type = OPTION_FLAG_ORD, 148 .opt.flag = &crl_config.issuer, 149 }, 150 { 151 .name = "lastupdate", 152 .desc = "Print the lastUpdate field", 153 .type = OPTION_FLAG_ORD, 154 .opt.flag = &crl_config.lastupdate, 155 }, 156 { 157 .name = "nameopt", 158 .argname = "options", 159 .desc = "Specify certificate name options", 160 .type = OPTION_ARG, 161 .opt.arg = &crl_config.nameopt, 162 }, 163 { 164 .name = "nextupdate", 165 .desc = "Print the nextUpdate field", 166 .type = OPTION_FLAG_ORD, 167 .opt.flag = &crl_config.nextupdate, 168 }, 169 { 170 .name = "noout", 171 .desc = "Do not output the encoded version of the CRL", 172 .type = OPTION_FLAG, 173 .opt.flag = &crl_config.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 = &crl_config.outfile, 181 }, 182 { 183 .name = "outform", 184 .argname = "format", 185 .desc = "Output format (DER or PEM)", 186 .type = OPTION_ARG_FORMAT, 187 .opt.value = &crl_config.outformat, 188 }, 189 { 190 .name = "text", 191 .desc = "Print out the CRL in text form", 192 .type = OPTION_FLAG, 193 .opt.flag = &crl_config.text, 194 }, 195 { 196 .name = "verify", 197 .desc = "Verify the signature on the CRL", 198 .type = OPTION_FLAG, 199 .opt.flag = &crl_config.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 (single_execution) { 234 if (pledge("stdio cpath wpath rpath", NULL) == -1) { 235 perror("pledge"); 236 exit(1); 237 } 238 } 239 240 if (bio_out == NULL) { 241 if ((bio_out = BIO_new(BIO_s_file())) != NULL) { 242 BIO_set_fp(bio_out, stdout, BIO_NOCLOSE); 243 } 244 } 245 246 digest = EVP_sha256(); 247 248 memset(&crl_config, 0, sizeof(crl_config)); 249 crl_config.informat = FORMAT_PEM; 250 crl_config.outformat = FORMAT_PEM; 251 252 if (options_parse(argc, argv, crl_options, &digest_name, NULL) != 0) { 253 crl_usage(); 254 goto end; 255 } 256 257 if (crl_config.cafile != NULL || crl_config.capath != NULL) 258 crl_config.verify = 1; 259 260 if (crl_config.nameopt != NULL) { 261 if (set_name_ex(&nmflag, crl_config.nameopt) != 1) { 262 fprintf(stderr, 263 "Invalid -nameopt argument '%s'\n", 264 crl_config.nameopt); 265 goto end; 266 } 267 } 268 269 if (digest_name != NULL) { 270 if ((digest = EVP_get_digestbyname(digest_name)) == NULL) { 271 fprintf(stderr, 272 "Unknown message digest algorithm '%s'\n", 273 digest_name); 274 goto end; 275 } 276 } 277 278 x = load_crl(crl_config.infile, crl_config.informat); 279 if (x == NULL) 280 goto end; 281 282 if (crl_config.verify) { 283 store = X509_STORE_new(); 284 if (store == NULL) 285 goto end; 286 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 287 if (lookup == NULL) 288 goto end; 289 if (!X509_LOOKUP_load_file(lookup, crl_config.cafile, 290 X509_FILETYPE_PEM)) 291 X509_LOOKUP_load_file(lookup, NULL, 292 X509_FILETYPE_DEFAULT); 293 294 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); 295 if (lookup == NULL) 296 goto end; 297 if (!X509_LOOKUP_add_dir(lookup, crl_config.capath, 298 X509_FILETYPE_PEM)) 299 X509_LOOKUP_add_dir(lookup, NULL, 300 X509_FILETYPE_DEFAULT); 301 ERR_clear_error(); 302 303 if ((ctx = X509_STORE_CTX_new()) == NULL) 304 goto end; 305 if ((xobj = X509_OBJECT_new()) == NULL) 306 goto end; 307 308 if (!X509_STORE_CTX_init(ctx, store, NULL, NULL)) { 309 BIO_printf(bio_err, 310 "Error initialising X509 store\n"); 311 goto end; 312 } 313 i = X509_STORE_get_by_subject(ctx, X509_LU_X509, 314 X509_CRL_get_issuer(x), xobj); 315 if (i <= 0) { 316 BIO_printf(bio_err, 317 "Error getting CRL issuer certificate\n"); 318 goto end; 319 } 320 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj)); 321 X509_OBJECT_free(xobj); 322 xobj = NULL; 323 if (!pkey) { 324 BIO_printf(bio_err, 325 "Error getting CRL issuer public key\n"); 326 goto end; 327 } 328 i = X509_CRL_verify(x, pkey); 329 EVP_PKEY_free(pkey); 330 if (i < 0) 331 goto end; 332 if (i == 0) 333 BIO_printf(bio_err, "verify failure\n"); 334 else 335 BIO_printf(bio_err, "verify OK\n"); 336 } 337 338 /* Print requested information the order that the flags were given. */ 339 for (i = 1; i <= argc; i++) { 340 if (crl_config.issuer == i) { 341 print_name(bio_out, "issuer=", 342 X509_CRL_get_issuer(x), nmflag); 343 } 344 if (crl_config.crlnumber == i) { 345 ASN1_INTEGER *crlnum; 346 crlnum = X509_CRL_get_ext_d2i(x, 347 NID_crl_number, NULL, NULL); 348 BIO_printf(bio_out, "crlNumber="); 349 if (crlnum) { 350 i2a_ASN1_INTEGER(bio_out, crlnum); 351 ASN1_INTEGER_free(crlnum); 352 } else 353 BIO_puts(bio_out, "<NONE>"); 354 BIO_printf(bio_out, "\n"); 355 } 356 if (crl_config.hash == i) { 357 BIO_printf(bio_out, "%08lx\n", 358 X509_NAME_hash(X509_CRL_get_issuer(x))); 359 } 360 #ifndef OPENSSL_NO_MD5 361 if (crl_config.hash_old == i) { 362 BIO_printf(bio_out, "%08lx\n", 363 X509_NAME_hash_old(X509_CRL_get_issuer(x))); 364 } 365 #endif 366 if (crl_config.lastupdate == i) { 367 BIO_printf(bio_out, "lastUpdate="); 368 ASN1_TIME_print(bio_out, 369 X509_CRL_get_lastUpdate(x)); 370 BIO_printf(bio_out, "\n"); 371 } 372 if (crl_config.nextupdate == i) { 373 BIO_printf(bio_out, "nextUpdate="); 374 if (X509_CRL_get_nextUpdate(x)) 375 ASN1_TIME_print(bio_out, 376 X509_CRL_get_nextUpdate(x)); 377 else 378 BIO_printf(bio_out, "NONE"); 379 BIO_printf(bio_out, "\n"); 380 } 381 if (crl_config.fingerprint == i) { 382 int j; 383 unsigned int n; 384 unsigned char md[EVP_MAX_MD_SIZE]; 385 386 if (!X509_CRL_digest(x, digest, md, &n)) { 387 BIO_printf(bio_err, "out of memory\n"); 388 goto end; 389 } 390 BIO_printf(bio_out, "%s Fingerprint=", 391 OBJ_nid2sn(EVP_MD_type(digest))); 392 for (j = 0; j < (int) n; j++) { 393 BIO_printf(bio_out, "%02X%c", md[j], 394 (j + 1 == (int)n) ? '\n' : ':'); 395 } 396 } 397 } 398 399 out = BIO_new(BIO_s_file()); 400 if (out == NULL) { 401 ERR_print_errors(bio_err); 402 goto end; 403 } 404 if (crl_config.outfile == NULL) { 405 BIO_set_fp(out, stdout, BIO_NOCLOSE); 406 } else { 407 if (BIO_write_filename(out, crl_config.outfile) <= 0) { 408 perror(crl_config.outfile); 409 goto end; 410 } 411 } 412 413 if (crl_config.text) 414 X509_CRL_print(out, x); 415 416 if (crl_config.noout) { 417 ret = 0; 418 goto end; 419 } 420 if (crl_config.outformat == FORMAT_ASN1) 421 i = (int) i2d_X509_CRL_bio(out, x); 422 else if (crl_config.outformat == FORMAT_PEM) 423 i = PEM_write_bio_X509_CRL(out, x); 424 else { 425 BIO_printf(bio_err, 426 "bad output format specified for outfile\n"); 427 goto end; 428 } 429 if (!i) { 430 BIO_printf(bio_err, "unable to write CRL\n"); 431 goto end; 432 } 433 ret = 0; 434 435 end: 436 BIO_free_all(out); 437 BIO_free_all(bio_out); 438 bio_out = NULL; 439 X509_CRL_free(x); 440 X509_STORE_CTX_free(ctx); 441 X509_STORE_free(store); 442 X509_OBJECT_free(xobj); 443 444 return (ret); 445 } 446 447 static X509_CRL * 448 load_crl(char *infile, int format) 449 { 450 X509_CRL *x = NULL; 451 BIO *in = NULL; 452 453 in = BIO_new(BIO_s_file()); 454 if (in == NULL) { 455 ERR_print_errors(bio_err); 456 goto end; 457 } 458 if (infile == NULL) 459 BIO_set_fp(in, stdin, BIO_NOCLOSE); 460 else { 461 if (BIO_read_filename(in, infile) <= 0) { 462 perror(infile); 463 goto end; 464 } 465 } 466 if (format == FORMAT_ASN1) 467 x = d2i_X509_CRL_bio(in, NULL); 468 else if (format == FORMAT_PEM) 469 x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); 470 else { 471 BIO_printf(bio_err, 472 "bad input format specified for input crl\n"); 473 goto end; 474 } 475 if (x == NULL) { 476 BIO_printf(bio_err, "unable to load CRL\n"); 477 ERR_print_errors(bio_err); 478 goto end; 479 } 480 481 end: 482 BIO_free(in); 483 return (x); 484 } 485