1 /* $OpenBSD: crl.c,v 1.6 2014/12/28 15:48:52 jsing 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 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 crl_main(int, char **); 219 220 int 221 crl_main(int argc, char **argv) 222 { 223 unsigned long nmflag = 0; 224 X509_CRL *x = NULL; 225 int ret = 1, i; 226 BIO *out = NULL; 227 X509_STORE *store = NULL; 228 X509_STORE_CTX ctx; 229 X509_LOOKUP *lookup = NULL; 230 X509_OBJECT xobj; 231 EVP_PKEY *pkey; 232 const EVP_MD *digest; 233 char *digest_name = NULL; 234 235 if (bio_out == NULL) { 236 if ((bio_out = BIO_new(BIO_s_file())) != NULL) { 237 BIO_set_fp(bio_out, stdout, BIO_NOCLOSE); 238 } 239 } 240 241 digest = EVP_sha1(); 242 243 memset(&crl_config, 0, sizeof(crl_config)); 244 crl_config.informat = FORMAT_PEM; 245 crl_config.outformat = FORMAT_PEM; 246 247 if (options_parse(argc, argv, crl_options, &digest_name, NULL) != 0) { 248 crl_usage(); 249 goto end; 250 } 251 252 if (crl_config.cafile != NULL || crl_config.capath != NULL) 253 crl_config.verify = 1; 254 255 if (crl_config.nameopt != NULL) { 256 if (set_name_ex(&nmflag, crl_config.nameopt) != 1) { 257 fprintf(stderr, 258 "Invalid -nameopt argument '%s'\n", 259 crl_config.nameopt); 260 goto end; 261 } 262 } 263 264 if (digest_name != NULL) { 265 if ((digest = EVP_get_digestbyname(digest_name)) == NULL) { 266 fprintf(stderr, 267 "Unknown message digest algorithm '%s'\n", 268 digest_name); 269 goto end; 270 } 271 } 272 273 x = load_crl(crl_config.infile, crl_config.informat); 274 if (x == NULL) 275 goto end; 276 277 if (crl_config.verify) { 278 store = X509_STORE_new(); 279 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 280 if (lookup == NULL) 281 goto end; 282 if (!X509_LOOKUP_load_file(lookup, crl_config.cafile, 283 X509_FILETYPE_PEM)) 284 X509_LOOKUP_load_file(lookup, NULL, 285 X509_FILETYPE_DEFAULT); 286 287 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); 288 if (lookup == NULL) 289 goto end; 290 if (!X509_LOOKUP_add_dir(lookup, crl_config.capath, 291 X509_FILETYPE_PEM)) 292 X509_LOOKUP_add_dir(lookup, NULL, 293 X509_FILETYPE_DEFAULT); 294 ERR_clear_error(); 295 296 if (!X509_STORE_CTX_init(&ctx, store, NULL, NULL)) { 297 BIO_printf(bio_err, 298 "Error initialising X509 store\n"); 299 goto end; 300 } 301 i = X509_STORE_get_by_subject(&ctx, X509_LU_X509, 302 X509_CRL_get_issuer(x), &xobj); 303 if (i <= 0) { 304 BIO_printf(bio_err, 305 "Error getting CRL issuer certificate\n"); 306 goto end; 307 } 308 pkey = X509_get_pubkey(xobj.data.x509); 309 X509_OBJECT_free_contents(&xobj); 310 if (!pkey) { 311 BIO_printf(bio_err, 312 "Error getting CRL issuer public key\n"); 313 goto end; 314 } 315 i = X509_CRL_verify(x, pkey); 316 EVP_PKEY_free(pkey); 317 if (i < 0) 318 goto end; 319 if (i == 0) 320 BIO_printf(bio_err, "verify failure\n"); 321 else 322 BIO_printf(bio_err, "verify OK\n"); 323 } 324 325 /* Print requested information the order that the flags were given. */ 326 for (i = 1; i <= argc; i++) { 327 if (crl_config.issuer == i) { 328 print_name(bio_out, "issuer=", 329 X509_CRL_get_issuer(x), nmflag); 330 } 331 if (crl_config.crlnumber == i) { 332 ASN1_INTEGER *crlnum; 333 crlnum = X509_CRL_get_ext_d2i(x, 334 NID_crl_number, NULL, NULL); 335 BIO_printf(bio_out, "crlNumber="); 336 if (crlnum) { 337 i2a_ASN1_INTEGER(bio_out, crlnum); 338 ASN1_INTEGER_free(crlnum); 339 } else 340 BIO_puts(bio_out, "<NONE>"); 341 BIO_printf(bio_out, "\n"); 342 } 343 if (crl_config.hash == i) { 344 BIO_printf(bio_out, "%08lx\n", 345 X509_NAME_hash(X509_CRL_get_issuer(x))); 346 } 347 #ifndef OPENSSL_NO_MD5 348 if (crl_config.hash_old == i) { 349 BIO_printf(bio_out, "%08lx\n", 350 X509_NAME_hash_old(X509_CRL_get_issuer(x))); 351 } 352 #endif 353 if (crl_config.lastupdate == i) { 354 BIO_printf(bio_out, "lastUpdate="); 355 ASN1_TIME_print(bio_out, 356 X509_CRL_get_lastUpdate(x)); 357 BIO_printf(bio_out, "\n"); 358 } 359 if (crl_config.nextupdate == i) { 360 BIO_printf(bio_out, "nextUpdate="); 361 if (X509_CRL_get_nextUpdate(x)) 362 ASN1_TIME_print(bio_out, 363 X509_CRL_get_nextUpdate(x)); 364 else 365 BIO_printf(bio_out, "NONE"); 366 BIO_printf(bio_out, "\n"); 367 } 368 if (crl_config.fingerprint == i) { 369 int j; 370 unsigned int n; 371 unsigned char md[EVP_MAX_MD_SIZE]; 372 373 if (!X509_CRL_digest(x, digest, md, &n)) { 374 BIO_printf(bio_err, "out of memory\n"); 375 goto end; 376 } 377 BIO_printf(bio_out, "%s Fingerprint=", 378 OBJ_nid2sn(EVP_MD_type(digest))); 379 for (j = 0; j < (int) n; j++) { 380 BIO_printf(bio_out, "%02X%c", md[j], 381 (j + 1 == (int)n) ? '\n' : ':'); 382 } 383 } 384 } 385 386 out = BIO_new(BIO_s_file()); 387 if (out == NULL) { 388 ERR_print_errors(bio_err); 389 goto end; 390 } 391 if (crl_config.outfile == NULL) { 392 BIO_set_fp(out, stdout, BIO_NOCLOSE); 393 } else { 394 if (BIO_write_filename(out, crl_config.outfile) <= 0) { 395 perror(crl_config.outfile); 396 goto end; 397 } 398 } 399 400 if (crl_config.text) 401 X509_CRL_print(out, x); 402 403 if (crl_config.noout) { 404 ret = 0; 405 goto end; 406 } 407 if (crl_config.outformat == FORMAT_ASN1) 408 i = (int) i2d_X509_CRL_bio(out, x); 409 else if (crl_config.outformat == FORMAT_PEM) 410 i = PEM_write_bio_X509_CRL(out, x); 411 else { 412 BIO_printf(bio_err, 413 "bad output format specified for outfile\n"); 414 goto end; 415 } 416 if (!i) { 417 BIO_printf(bio_err, "unable to write CRL\n"); 418 goto end; 419 } 420 ret = 0; 421 422 end: 423 BIO_free_all(out); 424 BIO_free_all(bio_out); 425 bio_out = NULL; 426 X509_CRL_free(x); 427 if (store) { 428 X509_STORE_CTX_cleanup(&ctx); 429 X509_STORE_free(store); 430 } 431 432 return (ret); 433 } 434 435 static X509_CRL * 436 load_crl(char *infile, int format) 437 { 438 X509_CRL *x = NULL; 439 BIO *in = NULL; 440 441 in = BIO_new(BIO_s_file()); 442 if (in == NULL) { 443 ERR_print_errors(bio_err); 444 goto end; 445 } 446 if (infile == NULL) 447 BIO_set_fp(in, stdin, BIO_NOCLOSE); 448 else { 449 if (BIO_read_filename(in, infile) <= 0) { 450 perror(infile); 451 goto end; 452 } 453 } 454 if (format == FORMAT_ASN1) 455 x = d2i_X509_CRL_bio(in, NULL); 456 else if (format == FORMAT_PEM) 457 x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); 458 else { 459 BIO_printf(bio_err, 460 "bad input format specified for input crl\n"); 461 goto end; 462 } 463 if (x == NULL) { 464 BIO_printf(bio_err, "unable to load CRL\n"); 465 ERR_print_errors(bio_err); 466 goto end; 467 } 468 469 end: 470 BIO_free(in); 471 return (x); 472 } 473