1 /* $OpenBSD: crl.c,v 1.13 2019/07/14 03:30:45 guenther 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; 227 X509_LOOKUP *lookup = NULL; 228 X509_OBJECT xobj; 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 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 285 if (lookup == NULL) 286 goto end; 287 if (!X509_LOOKUP_load_file(lookup, crl_config.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, crl_config.capath, 296 X509_FILETYPE_PEM)) 297 X509_LOOKUP_add_dir(lookup, NULL, 298 X509_FILETYPE_DEFAULT); 299 ERR_clear_error(); 300 301 if (!X509_STORE_CTX_init(&ctx, store, NULL, NULL)) { 302 BIO_printf(bio_err, 303 "Error initialising X509 store\n"); 304 goto end; 305 } 306 i = X509_STORE_get_by_subject(&ctx, X509_LU_X509, 307 X509_CRL_get_issuer(x), &xobj); 308 if (i <= 0) { 309 BIO_printf(bio_err, 310 "Error getting CRL issuer certificate\n"); 311 goto end; 312 } 313 pkey = X509_get_pubkey(xobj.data.x509); 314 X509_OBJECT_free_contents(&xobj); 315 if (!pkey) { 316 BIO_printf(bio_err, 317 "Error getting CRL issuer public key\n"); 318 goto end; 319 } 320 i = X509_CRL_verify(x, pkey); 321 EVP_PKEY_free(pkey); 322 if (i < 0) 323 goto end; 324 if (i == 0) 325 BIO_printf(bio_err, "verify failure\n"); 326 else 327 BIO_printf(bio_err, "verify OK\n"); 328 } 329 330 /* Print requested information the order that the flags were given. */ 331 for (i = 1; i <= argc; i++) { 332 if (crl_config.issuer == i) { 333 print_name(bio_out, "issuer=", 334 X509_CRL_get_issuer(x), nmflag); 335 } 336 if (crl_config.crlnumber == i) { 337 ASN1_INTEGER *crlnum; 338 crlnum = X509_CRL_get_ext_d2i(x, 339 NID_crl_number, NULL, NULL); 340 BIO_printf(bio_out, "crlNumber="); 341 if (crlnum) { 342 i2a_ASN1_INTEGER(bio_out, crlnum); 343 ASN1_INTEGER_free(crlnum); 344 } else 345 BIO_puts(bio_out, "<NONE>"); 346 BIO_printf(bio_out, "\n"); 347 } 348 if (crl_config.hash == i) { 349 BIO_printf(bio_out, "%08lx\n", 350 X509_NAME_hash(X509_CRL_get_issuer(x))); 351 } 352 #ifndef OPENSSL_NO_MD5 353 if (crl_config.hash_old == i) { 354 BIO_printf(bio_out, "%08lx\n", 355 X509_NAME_hash_old(X509_CRL_get_issuer(x))); 356 } 357 #endif 358 if (crl_config.lastupdate == i) { 359 BIO_printf(bio_out, "lastUpdate="); 360 ASN1_TIME_print(bio_out, 361 X509_CRL_get_lastUpdate(x)); 362 BIO_printf(bio_out, "\n"); 363 } 364 if (crl_config.nextupdate == i) { 365 BIO_printf(bio_out, "nextUpdate="); 366 if (X509_CRL_get_nextUpdate(x)) 367 ASN1_TIME_print(bio_out, 368 X509_CRL_get_nextUpdate(x)); 369 else 370 BIO_printf(bio_out, "NONE"); 371 BIO_printf(bio_out, "\n"); 372 } 373 if (crl_config.fingerprint == i) { 374 int j; 375 unsigned int n; 376 unsigned char md[EVP_MAX_MD_SIZE]; 377 378 if (!X509_CRL_digest(x, digest, md, &n)) { 379 BIO_printf(bio_err, "out of memory\n"); 380 goto end; 381 } 382 BIO_printf(bio_out, "%s Fingerprint=", 383 OBJ_nid2sn(EVP_MD_type(digest))); 384 for (j = 0; j < (int) n; j++) { 385 BIO_printf(bio_out, "%02X%c", md[j], 386 (j + 1 == (int)n) ? '\n' : ':'); 387 } 388 } 389 } 390 391 out = BIO_new(BIO_s_file()); 392 if (out == NULL) { 393 ERR_print_errors(bio_err); 394 goto end; 395 } 396 if (crl_config.outfile == NULL) { 397 BIO_set_fp(out, stdout, BIO_NOCLOSE); 398 } else { 399 if (BIO_write_filename(out, crl_config.outfile) <= 0) { 400 perror(crl_config.outfile); 401 goto end; 402 } 403 } 404 405 if (crl_config.text) 406 X509_CRL_print(out, x); 407 408 if (crl_config.noout) { 409 ret = 0; 410 goto end; 411 } 412 if (crl_config.outformat == FORMAT_ASN1) 413 i = (int) i2d_X509_CRL_bio(out, x); 414 else if (crl_config.outformat == FORMAT_PEM) 415 i = PEM_write_bio_X509_CRL(out, x); 416 else { 417 BIO_printf(bio_err, 418 "bad output format specified for outfile\n"); 419 goto end; 420 } 421 if (!i) { 422 BIO_printf(bio_err, "unable to write CRL\n"); 423 goto end; 424 } 425 ret = 0; 426 427 end: 428 BIO_free_all(out); 429 BIO_free_all(bio_out); 430 bio_out = NULL; 431 X509_CRL_free(x); 432 if (store) { 433 X509_STORE_CTX_cleanup(&ctx); 434 X509_STORE_free(store); 435 } 436 437 return (ret); 438 } 439 440 static X509_CRL * 441 load_crl(char *infile, int format) 442 { 443 X509_CRL *x = NULL; 444 BIO *in = NULL; 445 446 in = BIO_new(BIO_s_file()); 447 if (in == NULL) { 448 ERR_print_errors(bio_err); 449 goto end; 450 } 451 if (infile == NULL) 452 BIO_set_fp(in, stdin, BIO_NOCLOSE); 453 else { 454 if (BIO_read_filename(in, infile) <= 0) { 455 perror(infile); 456 goto end; 457 } 458 } 459 if (format == FORMAT_ASN1) 460 x = d2i_X509_CRL_bio(in, NULL); 461 else if (format == FORMAT_PEM) 462 x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); 463 else { 464 BIO_printf(bio_err, 465 "bad input format specified for input crl\n"); 466 goto end; 467 } 468 if (x == NULL) { 469 BIO_printf(bio_err, "unable to load CRL\n"); 470 ERR_print_errors(bio_err); 471 goto end; 472 } 473 474 end: 475 BIO_free(in); 476 return (x); 477 } 478