1 /* apps/ts.c */ 2 /* 3 * Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL project 4 * 2002. 5 */ 6 /* ==================================================================== 7 * Copyright (c) 2001 The OpenSSL Project. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. All advertising materials mentioning features or use of this 22 * software must display the following acknowledgment: 23 * "This product includes software developed by the OpenSSL Project 24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25 * 26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For written permission, please contact 29 * licensing@OpenSSL.org. 30 * 31 * 5. Products derived from this software may not be called "OpenSSL" 32 * nor may "OpenSSL" appear in their names without prior written 33 * permission of the OpenSSL Project. 34 * 35 * 6. Redistributions of any form whatsoever must retain the following 36 * acknowledgment: 37 * "This product includes software developed by the OpenSSL Project 38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51 * OF THE POSSIBILITY OF SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This product includes cryptographic software written by Eric Young 55 * (eay@cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh@cryptsoft.com). 57 * 58 */ 59 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include "apps.h" 64 #include <openssl/bio.h> 65 #include <openssl/err.h> 66 #include <openssl/pem.h> 67 #include <openssl/rand.h> 68 #include <openssl/ts.h> 69 #include <openssl/bn.h> 70 71 #undef PROG 72 #define PROG ts_main 73 74 /* Length of the nonce of the request in bits (must be a multiple of 8). */ 75 #define NONCE_LENGTH 64 76 77 /* Macro definitions for the configuration file. */ 78 #define ENV_OID_FILE "oid_file" 79 80 /* Local function declarations. */ 81 82 static ASN1_OBJECT *txt2obj(const char *oid); 83 static CONF *load_config_file(const char *configfile); 84 85 /* Query related functions. */ 86 static int query_command(const char *data, char *digest, 87 const EVP_MD *md, const char *policy, int no_nonce, 88 int cert, const char *in, const char *out, int text); 89 static BIO *BIO_open_with_default(const char *file, const char *mode, 90 FILE *default_fp); 91 static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md, 92 const char *policy, int no_nonce, int cert); 93 static int create_digest(BIO *input, char *digest, 94 const EVP_MD *md, unsigned char **md_value); 95 static ASN1_INTEGER *create_nonce(int bits); 96 97 /* Reply related functions. */ 98 static int reply_command(CONF *conf, char *section, char *engine, 99 char *queryfile, char *passin, char *inkey, 100 char *signer, char *chain, const char *policy, 101 char *in, int token_in, char *out, int token_out, 102 int text); 103 static TS_RESP *read_PKCS7(BIO *in_bio); 104 static TS_RESP *create_response(CONF *conf, const char *section, char *engine, 105 char *queryfile, char *passin, char *inkey, 106 char *signer, char *chain, 107 const char *policy); 108 static ASN1_INTEGER *MS_CALLBACK serial_cb(TS_RESP_CTX *ctx, void *data); 109 static ASN1_INTEGER *next_serial(const char *serialfile); 110 static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial); 111 112 /* Verify related functions. */ 113 static int verify_command(char *data, char *digest, char *queryfile, 114 char *in, int token_in, 115 char *ca_path, char *ca_file, char *untrusted); 116 static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest, 117 char *queryfile, 118 char *ca_path, char *ca_file, 119 char *untrusted); 120 static X509_STORE *create_cert_store(char *ca_path, char *ca_file); 121 static int MS_CALLBACK verify_cb(int ok, X509_STORE_CTX *ctx); 122 123 /* Main function definition. */ 124 int MAIN(int, char **); 125 126 int MAIN(int argc, char **argv) 127 { 128 int ret = 1; 129 char *configfile = NULL; 130 char *section = NULL; 131 CONF *conf = NULL; 132 enum mode { 133 CMD_NONE, CMD_QUERY, CMD_REPLY, CMD_VERIFY 134 } mode = CMD_NONE; 135 char *data = NULL; 136 char *digest = NULL; 137 const EVP_MD *md = NULL; 138 char *rnd = NULL; 139 char *policy = NULL; 140 int no_nonce = 0; 141 int cert = 0; 142 char *in = NULL; 143 char *out = NULL; 144 int text = 0; 145 char *queryfile = NULL; 146 char *passin = NULL; /* Password source. */ 147 char *password = NULL; /* Password itself. */ 148 char *inkey = NULL; 149 char *signer = NULL; 150 char *chain = NULL; 151 char *ca_path = NULL; 152 char *ca_file = NULL; 153 char *untrusted = NULL; 154 char *engine = NULL; 155 /* Input is ContentInfo instead of TimeStampResp. */ 156 int token_in = 0; 157 /* Output is ContentInfo instead of TimeStampResp. */ 158 int token_out = 0; 159 int free_bio_err = 0; 160 161 ERR_load_crypto_strings(); 162 apps_startup(); 163 164 if (bio_err == NULL && (bio_err = BIO_new(BIO_s_file())) != NULL) { 165 free_bio_err = 1; 166 BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT); 167 } 168 169 if (!load_config(bio_err, NULL)) 170 goto cleanup; 171 172 for (argc--, argv++; argc > 0; argc--, argv++) { 173 if (strcmp(*argv, "-config") == 0) { 174 if (argc-- < 1) 175 goto usage; 176 configfile = *++argv; 177 } else if (strcmp(*argv, "-section") == 0) { 178 if (argc-- < 1) 179 goto usage; 180 section = *++argv; 181 } else if (strcmp(*argv, "-query") == 0) { 182 if (mode != CMD_NONE) 183 goto usage; 184 mode = CMD_QUERY; 185 } else if (strcmp(*argv, "-data") == 0) { 186 if (argc-- < 1) 187 goto usage; 188 data = *++argv; 189 } else if (strcmp(*argv, "-digest") == 0) { 190 if (argc-- < 1) 191 goto usage; 192 digest = *++argv; 193 } else if (strcmp(*argv, "-rand") == 0) { 194 if (argc-- < 1) 195 goto usage; 196 rnd = *++argv; 197 } else if (strcmp(*argv, "-policy") == 0) { 198 if (argc-- < 1) 199 goto usage; 200 policy = *++argv; 201 } else if (strcmp(*argv, "-no_nonce") == 0) { 202 no_nonce = 1; 203 } else if (strcmp(*argv, "-cert") == 0) { 204 cert = 1; 205 } else if (strcmp(*argv, "-in") == 0) { 206 if (argc-- < 1) 207 goto usage; 208 in = *++argv; 209 } else if (strcmp(*argv, "-token_in") == 0) { 210 token_in = 1; 211 } else if (strcmp(*argv, "-out") == 0) { 212 if (argc-- < 1) 213 goto usage; 214 out = *++argv; 215 } else if (strcmp(*argv, "-token_out") == 0) { 216 token_out = 1; 217 } else if (strcmp(*argv, "-text") == 0) { 218 text = 1; 219 } else if (strcmp(*argv, "-reply") == 0) { 220 if (mode != CMD_NONE) 221 goto usage; 222 mode = CMD_REPLY; 223 } else if (strcmp(*argv, "-queryfile") == 0) { 224 if (argc-- < 1) 225 goto usage; 226 queryfile = *++argv; 227 } else if (strcmp(*argv, "-passin") == 0) { 228 if (argc-- < 1) 229 goto usage; 230 passin = *++argv; 231 } else if (strcmp(*argv, "-inkey") == 0) { 232 if (argc-- < 1) 233 goto usage; 234 inkey = *++argv; 235 } else if (strcmp(*argv, "-signer") == 0) { 236 if (argc-- < 1) 237 goto usage; 238 signer = *++argv; 239 } else if (strcmp(*argv, "-chain") == 0) { 240 if (argc-- < 1) 241 goto usage; 242 chain = *++argv; 243 } else if (strcmp(*argv, "-verify") == 0) { 244 if (mode != CMD_NONE) 245 goto usage; 246 mode = CMD_VERIFY; 247 } else if (strcmp(*argv, "-CApath") == 0) { 248 if (argc-- < 1) 249 goto usage; 250 ca_path = *++argv; 251 } else if (strcmp(*argv, "-CAfile") == 0) { 252 if (argc-- < 1) 253 goto usage; 254 ca_file = *++argv; 255 } else if (strcmp(*argv, "-untrusted") == 0) { 256 if (argc-- < 1) 257 goto usage; 258 untrusted = *++argv; 259 } else if (strcmp(*argv, "-engine") == 0) { 260 if (argc-- < 1) 261 goto usage; 262 engine = *++argv; 263 } else if ((md = EVP_get_digestbyname(*argv + 1)) != NULL) { 264 /* empty. */ 265 } else 266 goto usage; 267 } 268 269 /* Seed the random number generator if it is going to be used. */ 270 if (mode == CMD_QUERY && !no_nonce) { 271 if (!app_RAND_load_file(NULL, bio_err, 1) && rnd == NULL) 272 BIO_printf(bio_err, "warning, not much extra random " 273 "data, consider using the -rand option\n"); 274 if (rnd != NULL) 275 BIO_printf(bio_err, "%ld semi-random bytes loaded\n", 276 app_RAND_load_files(rnd)); 277 } 278 279 /* Get the password if required. */ 280 if (mode == CMD_REPLY && passin && 281 !app_passwd(bio_err, passin, NULL, &password, NULL)) { 282 BIO_printf(bio_err, "Error getting password.\n"); 283 goto cleanup; 284 } 285 286 /* 287 * Check consistency of parameters and execute the appropriate function. 288 */ 289 switch (mode) { 290 case CMD_NONE: 291 goto usage; 292 case CMD_QUERY: 293 /* 294 * Data file and message imprint cannot be specified at the same 295 * time. 296 */ 297 ret = data != NULL && digest != NULL; 298 if (ret) 299 goto usage; 300 /* Load the config file for possible policy OIDs. */ 301 conf = load_config_file(configfile); 302 ret = !query_command(data, digest, md, policy, no_nonce, cert, 303 in, out, text); 304 break; 305 case CMD_REPLY: 306 conf = load_config_file(configfile); 307 if (in == NULL) { 308 ret = !(queryfile != NULL && conf != NULL && !token_in); 309 if (ret) 310 goto usage; 311 } else { 312 /* 'in' and 'queryfile' are exclusive. */ 313 ret = !(queryfile == NULL); 314 if (ret) 315 goto usage; 316 } 317 318 ret = !reply_command(conf, section, engine, queryfile, 319 password, inkey, signer, chain, policy, 320 in, token_in, out, token_out, text); 321 break; 322 case CMD_VERIFY: 323 ret = !(((queryfile && !data && !digest) 324 || (!queryfile && data && !digest) 325 || (!queryfile && !data && digest)) 326 && in != NULL); 327 if (ret) 328 goto usage; 329 330 ret = !verify_command(data, digest, queryfile, in, token_in, 331 ca_path, ca_file, untrusted); 332 } 333 334 goto cleanup; 335 336 usage: 337 BIO_printf(bio_err, "usage:\n" 338 "ts -query [-rand file%cfile%c...] [-config configfile] " 339 "[-data file_to_hash] [-digest digest_bytes]" 340 "[-md2|-md4|-md5|-sha|-sha1|-mdc2|-ripemd160] " 341 "[-policy object_id] [-no_nonce] [-cert] " 342 "[-in request.tsq] [-out request.tsq] [-text]\n", 343 LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR); 344 BIO_printf(bio_err, "or\n" 345 "ts -reply [-config configfile] [-section tsa_section] " 346 "[-queryfile request.tsq] [-passin password] " 347 "[-signer tsa_cert.pem] [-inkey private_key.pem] " 348 "[-chain certs_file.pem] [-policy object_id] " 349 "[-in response.tsr] [-token_in] " 350 "[-out response.tsr] [-token_out] [-text] [-engine id]\n"); 351 BIO_printf(bio_err, "or\n" 352 "ts -verify [-data file_to_hash] [-digest digest_bytes] " 353 "[-queryfile request.tsq] " 354 "-in response.tsr [-token_in] " 355 "-CApath ca_path -CAfile ca_file.pem " 356 "-untrusted cert_file.pem\n"); 357 cleanup: 358 /* Clean up. */ 359 app_RAND_write_file(NULL, bio_err); 360 NCONF_free(conf); 361 OPENSSL_free(password); 362 OBJ_cleanup(); 363 if (free_bio_err) { 364 BIO_free_all(bio_err); 365 bio_err = NULL; 366 } 367 368 OPENSSL_EXIT(ret); 369 } 370 371 /* 372 * Configuration file-related function definitions. 373 */ 374 375 static ASN1_OBJECT *txt2obj(const char *oid) 376 { 377 ASN1_OBJECT *oid_obj = NULL; 378 379 if (!(oid_obj = OBJ_txt2obj(oid, 0))) 380 BIO_printf(bio_err, "cannot convert %s to OID\n", oid); 381 382 return oid_obj; 383 } 384 385 static CONF *load_config_file(const char *configfile) 386 { 387 CONF *conf = NULL; 388 long errorline = -1; 389 390 if (!configfile) 391 configfile = getenv("OPENSSL_CONF"); 392 if (!configfile) 393 configfile = getenv("SSLEAY_CONF"); 394 395 if (configfile && 396 (!(conf = NCONF_new(NULL)) || 397 NCONF_load(conf, configfile, &errorline) <= 0)) { 398 if (errorline <= 0) 399 BIO_printf(bio_err, "error loading the config file " 400 "'%s'\n", configfile); 401 else 402 BIO_printf(bio_err, "error on line %ld of config file " 403 "'%s'\n", errorline, configfile); 404 } 405 406 if (conf != NULL) { 407 const char *p; 408 409 BIO_printf(bio_err, "Using configuration from %s\n", configfile); 410 p = NCONF_get_string(conf, NULL, ENV_OID_FILE); 411 if (p != NULL) { 412 BIO *oid_bio = BIO_new_file(p, "r"); 413 if (!oid_bio) 414 ERR_print_errors(bio_err); 415 else { 416 OBJ_create_objects(oid_bio); 417 BIO_free_all(oid_bio); 418 } 419 } else 420 ERR_clear_error(); 421 if (!add_oid_section(bio_err, conf)) 422 ERR_print_errors(bio_err); 423 } 424 return conf; 425 } 426 427 /* 428 * Query-related method definitions. 429 */ 430 431 static int query_command(const char *data, char *digest, const EVP_MD *md, 432 const char *policy, int no_nonce, 433 int cert, const char *in, const char *out, int text) 434 { 435 int ret = 0; 436 TS_REQ *query = NULL; 437 BIO *in_bio = NULL; 438 BIO *data_bio = NULL; 439 BIO *out_bio = NULL; 440 441 /* Build query object either from file or from scratch. */ 442 if (in != NULL) { 443 if ((in_bio = BIO_new_file(in, "rb")) == NULL) 444 goto end; 445 query = d2i_TS_REQ_bio(in_bio, NULL); 446 } else { 447 /* 448 * Open the file if no explicit digest bytes were specified. 449 */ 450 if (!digest && !(data_bio = BIO_open_with_default(data, "rb", stdin))) 451 goto end; 452 /* Creating the query object. */ 453 query = create_query(data_bio, digest, md, policy, no_nonce, cert); 454 /* Saving the random number generator state. */ 455 } 456 if (query == NULL) 457 goto end; 458 459 /* Write query either in ASN.1 or in text format. */ 460 if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) 461 goto end; 462 if (text) { 463 /* Text output. */ 464 if (!TS_REQ_print_bio(out_bio, query)) 465 goto end; 466 } else { 467 /* ASN.1 output. */ 468 if (!i2d_TS_REQ_bio(out_bio, query)) 469 goto end; 470 } 471 472 ret = 1; 473 474 end: 475 ERR_print_errors(bio_err); 476 477 /* Clean up. */ 478 BIO_free_all(in_bio); 479 BIO_free_all(data_bio); 480 BIO_free_all(out_bio); 481 TS_REQ_free(query); 482 483 return ret; 484 } 485 486 static BIO *BIO_open_with_default(const char *file, const char *mode, 487 FILE *default_fp) 488 { 489 return file == NULL ? BIO_new_fp(default_fp, BIO_NOCLOSE) 490 : BIO_new_file(file, mode); 491 } 492 493 static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md, 494 const char *policy, int no_nonce, int cert) 495 { 496 int ret = 0; 497 TS_REQ *ts_req = NULL; 498 int len; 499 TS_MSG_IMPRINT *msg_imprint = NULL; 500 X509_ALGOR *algo = NULL; 501 unsigned char *data = NULL; 502 ASN1_OBJECT *policy_obj = NULL; 503 ASN1_INTEGER *nonce_asn1 = NULL; 504 505 /* Setting default message digest. */ 506 if (!md && !(md = EVP_get_digestbyname("sha1"))) 507 goto err; 508 509 /* Creating request object. */ 510 if (!(ts_req = TS_REQ_new())) 511 goto err; 512 513 /* Setting version. */ 514 if (!TS_REQ_set_version(ts_req, 1)) 515 goto err; 516 517 /* Creating and adding MSG_IMPRINT object. */ 518 if (!(msg_imprint = TS_MSG_IMPRINT_new())) 519 goto err; 520 521 /* Adding algorithm. */ 522 if (!(algo = X509_ALGOR_new())) 523 goto err; 524 if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md)))) 525 goto err; 526 if (!(algo->parameter = ASN1_TYPE_new())) 527 goto err; 528 algo->parameter->type = V_ASN1_NULL; 529 if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo)) 530 goto err; 531 532 /* Adding message digest. */ 533 if ((len = create_digest(data_bio, digest, md, &data)) == 0) 534 goto err; 535 if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len)) 536 goto err; 537 538 if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint)) 539 goto err; 540 541 /* Setting policy if requested. */ 542 if (policy && !(policy_obj = txt2obj(policy))) 543 goto err; 544 if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj)) 545 goto err; 546 547 /* Setting nonce if requested. */ 548 if (!no_nonce && !(nonce_asn1 = create_nonce(NONCE_LENGTH))) 549 goto err; 550 if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1)) 551 goto err; 552 553 /* Setting certificate request flag if requested. */ 554 if (!TS_REQ_set_cert_req(ts_req, cert)) 555 goto err; 556 557 ret = 1; 558 err: 559 if (!ret) { 560 TS_REQ_free(ts_req); 561 ts_req = NULL; 562 BIO_printf(bio_err, "could not create query\n"); 563 } 564 TS_MSG_IMPRINT_free(msg_imprint); 565 X509_ALGOR_free(algo); 566 OPENSSL_free(data); 567 ASN1_OBJECT_free(policy_obj); 568 ASN1_INTEGER_free(nonce_asn1); 569 return ts_req; 570 } 571 572 static int create_digest(BIO *input, char *digest, const EVP_MD *md, 573 unsigned char **md_value) 574 { 575 int md_value_len; 576 577 md_value_len = EVP_MD_size(md); 578 if (md_value_len < 0) 579 goto err; 580 if (input) { 581 /* Digest must be computed from an input file. */ 582 EVP_MD_CTX md_ctx; 583 unsigned char buffer[4096]; 584 int length; 585 586 *md_value = OPENSSL_malloc(md_value_len); 587 if (*md_value == 0) 588 goto err; 589 590 EVP_DigestInit(&md_ctx, md); 591 while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) { 592 EVP_DigestUpdate(&md_ctx, buffer, length); 593 } 594 EVP_DigestFinal(&md_ctx, *md_value, NULL); 595 } else { 596 /* Digest bytes are specified with digest. */ 597 long digest_len; 598 *md_value = string_to_hex(digest, &digest_len); 599 if (!*md_value || md_value_len != digest_len) { 600 OPENSSL_free(*md_value); 601 *md_value = NULL; 602 BIO_printf(bio_err, "bad digest, %d bytes " 603 "must be specified\n", md_value_len); 604 goto err; 605 } 606 } 607 608 return md_value_len; 609 err: 610 return 0; 611 } 612 613 static ASN1_INTEGER *create_nonce(int bits) 614 { 615 unsigned char buf[20]; 616 ASN1_INTEGER *nonce = NULL; 617 int len = (bits - 1) / 8 + 1; 618 int i; 619 620 /* Generating random byte sequence. */ 621 if (len > (int)sizeof(buf)) 622 goto err; 623 if (RAND_bytes(buf, len) <= 0) 624 goto err; 625 626 /* Find the first non-zero byte and creating ASN1_INTEGER object. */ 627 for (i = 0; i < len && !buf[i]; ++i) ; 628 if (!(nonce = ASN1_INTEGER_new())) 629 goto err; 630 OPENSSL_free(nonce->data); 631 /* Allocate at least one byte. */ 632 nonce->length = len - i; 633 if (!(nonce->data = OPENSSL_malloc(nonce->length + 1))) 634 goto err; 635 memcpy(nonce->data, buf + i, nonce->length); 636 637 return nonce; 638 err: 639 BIO_printf(bio_err, "could not create nonce\n"); 640 ASN1_INTEGER_free(nonce); 641 return NULL; 642 } 643 644 /* 645 * Reply-related method definitions. 646 */ 647 648 static int reply_command(CONF *conf, char *section, char *engine, 649 char *queryfile, char *passin, char *inkey, 650 char *signer, char *chain, const char *policy, 651 char *in, int token_in, 652 char *out, int token_out, int text) 653 { 654 int ret = 0; 655 TS_RESP *response = NULL; 656 BIO *in_bio = NULL; 657 BIO *query_bio = NULL; 658 BIO *inkey_bio = NULL; 659 BIO *signer_bio = NULL; 660 BIO *out_bio = NULL; 661 662 /* Build response object either from response or query. */ 663 if (in != NULL) { 664 if ((in_bio = BIO_new_file(in, "rb")) == NULL) 665 goto end; 666 if (token_in) { 667 /* 668 * We have a ContentInfo (PKCS7) object, add 'granted' status 669 * info around it. 670 */ 671 response = read_PKCS7(in_bio); 672 } else { 673 /* We have a ready-made TS_RESP object. */ 674 response = d2i_TS_RESP_bio(in_bio, NULL); 675 } 676 } else { 677 response = create_response(conf, section, engine, queryfile, 678 passin, inkey, signer, chain, policy); 679 if (response) 680 BIO_printf(bio_err, "Response has been generated.\n"); 681 else 682 BIO_printf(bio_err, "Response is not generated.\n"); 683 } 684 if (response == NULL) 685 goto end; 686 687 /* Write response either in ASN.1 or text format. */ 688 if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) 689 goto end; 690 if (text) { 691 /* Text output. */ 692 if (token_out) { 693 TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response); 694 if (!TS_TST_INFO_print_bio(out_bio, tst_info)) 695 goto end; 696 } else { 697 if (!TS_RESP_print_bio(out_bio, response)) 698 goto end; 699 } 700 } else { 701 /* ASN.1 DER output. */ 702 if (token_out) { 703 PKCS7 *token = TS_RESP_get_token(response); 704 if (!i2d_PKCS7_bio(out_bio, token)) 705 goto end; 706 } else { 707 if (!i2d_TS_RESP_bio(out_bio, response)) 708 goto end; 709 } 710 } 711 712 ret = 1; 713 714 end: 715 ERR_print_errors(bio_err); 716 717 /* Clean up. */ 718 BIO_free_all(in_bio); 719 BIO_free_all(query_bio); 720 BIO_free_all(inkey_bio); 721 BIO_free_all(signer_bio); 722 BIO_free_all(out_bio); 723 TS_RESP_free(response); 724 725 return ret; 726 } 727 728 /* Reads a PKCS7 token and adds default 'granted' status info to it. */ 729 static TS_RESP *read_PKCS7(BIO *in_bio) 730 { 731 int ret = 0; 732 PKCS7 *token = NULL; 733 TS_TST_INFO *tst_info = NULL; 734 TS_RESP *resp = NULL; 735 TS_STATUS_INFO *si = NULL; 736 737 /* Read PKCS7 object and extract the signed time stamp info. */ 738 if (!(token = d2i_PKCS7_bio(in_bio, NULL))) 739 goto end; 740 if (!(tst_info = PKCS7_to_TS_TST_INFO(token))) 741 goto end; 742 743 /* Creating response object. */ 744 if (!(resp = TS_RESP_new())) 745 goto end; 746 747 /* Create granted status info. */ 748 if (!(si = TS_STATUS_INFO_new())) 749 goto end; 750 if (!(ASN1_INTEGER_set(si->status, TS_STATUS_GRANTED))) 751 goto end; 752 if (!TS_RESP_set_status_info(resp, si)) 753 goto end; 754 755 /* Setting encapsulated token. */ 756 TS_RESP_set_tst_info(resp, token, tst_info); 757 token = NULL; /* Ownership is lost. */ 758 tst_info = NULL; /* Ownership is lost. */ 759 760 ret = 1; 761 end: 762 PKCS7_free(token); 763 TS_TST_INFO_free(tst_info); 764 if (!ret) { 765 TS_RESP_free(resp); 766 resp = NULL; 767 } 768 TS_STATUS_INFO_free(si); 769 return resp; 770 } 771 772 static TS_RESP *create_response(CONF *conf, const char *section, char *engine, 773 char *queryfile, char *passin, char *inkey, 774 char *signer, char *chain, const char *policy) 775 { 776 int ret = 0; 777 TS_RESP *response = NULL; 778 BIO *query_bio = NULL; 779 TS_RESP_CTX *resp_ctx = NULL; 780 781 if (!(query_bio = BIO_new_file(queryfile, "rb"))) 782 goto end; 783 784 /* Getting TSA configuration section. */ 785 if (!(section = TS_CONF_get_tsa_section(conf, section))) 786 goto end; 787 788 /* Setting up response generation context. */ 789 if (!(resp_ctx = TS_RESP_CTX_new())) 790 goto end; 791 792 /* Setting serial number provider callback. */ 793 if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx)) 794 goto end; 795 #ifndef OPENSSL_NO_ENGINE 796 /* Setting default OpenSSL engine. */ 797 if (!TS_CONF_set_crypto_device(conf, section, engine)) 798 goto end; 799 #endif 800 801 /* Setting TSA signer certificate. */ 802 if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx)) 803 goto end; 804 805 /* Setting TSA signer certificate chain. */ 806 if (!TS_CONF_set_certs(conf, section, chain, resp_ctx)) 807 goto end; 808 809 /* Setting TSA signer private key. */ 810 if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx)) 811 goto end; 812 813 /* Setting default policy OID. */ 814 if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx)) 815 goto end; 816 817 /* Setting acceptable policy OIDs. */ 818 if (!TS_CONF_set_policies(conf, section, resp_ctx)) 819 goto end; 820 821 /* Setting the acceptable one-way hash algorithms. */ 822 if (!TS_CONF_set_digests(conf, section, resp_ctx)) 823 goto end; 824 825 /* Setting guaranteed time stamp accuracy. */ 826 if (!TS_CONF_set_accuracy(conf, section, resp_ctx)) 827 goto end; 828 829 /* Setting the precision of the time. */ 830 if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx)) 831 goto end; 832 833 /* Setting the ordering flaf if requested. */ 834 if (!TS_CONF_set_ordering(conf, section, resp_ctx)) 835 goto end; 836 837 /* Setting the TSA name required flag if requested. */ 838 if (!TS_CONF_set_tsa_name(conf, section, resp_ctx)) 839 goto end; 840 841 /* Setting the ESS cert id chain flag if requested. */ 842 if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx)) 843 goto end; 844 845 /* Creating the response. */ 846 if (!(response = TS_RESP_create_response(resp_ctx, query_bio))) 847 goto end; 848 849 ret = 1; 850 end: 851 if (!ret) { 852 TS_RESP_free(response); 853 response = NULL; 854 } 855 TS_RESP_CTX_free(resp_ctx); 856 BIO_free_all(query_bio); 857 858 return response; 859 } 860 861 static ASN1_INTEGER *MS_CALLBACK serial_cb(TS_RESP_CTX *ctx, void *data) 862 { 863 const char *serial_file = (const char *)data; 864 ASN1_INTEGER *serial = next_serial(serial_file); 865 866 if (!serial) { 867 TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, 868 "Error during serial number " 869 "generation."); 870 TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE); 871 } else 872 save_ts_serial(serial_file, serial); 873 874 return serial; 875 } 876 877 static ASN1_INTEGER *next_serial(const char *serialfile) 878 { 879 int ret = 0; 880 BIO *in = NULL; 881 ASN1_INTEGER *serial = NULL; 882 BIGNUM *bn = NULL; 883 884 if (!(serial = ASN1_INTEGER_new())) 885 goto err; 886 887 if (!(in = BIO_new_file(serialfile, "r"))) { 888 ERR_clear_error(); 889 BIO_printf(bio_err, "Warning: could not open file %s for " 890 "reading, using serial number: 1\n", serialfile); 891 if (!ASN1_INTEGER_set(serial, 1)) 892 goto err; 893 } else { 894 char buf[1024]; 895 if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) { 896 BIO_printf(bio_err, "unable to load number from %s\n", 897 serialfile); 898 goto err; 899 } 900 if (!(bn = ASN1_INTEGER_to_BN(serial, NULL))) 901 goto err; 902 ASN1_INTEGER_free(serial); 903 serial = NULL; 904 if (!BN_add_word(bn, 1)) 905 goto err; 906 if (!(serial = BN_to_ASN1_INTEGER(bn, NULL))) 907 goto err; 908 } 909 ret = 1; 910 err: 911 if (!ret) { 912 ASN1_INTEGER_free(serial); 913 serial = NULL; 914 } 915 BIO_free_all(in); 916 BN_free(bn); 917 return serial; 918 } 919 920 static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial) 921 { 922 int ret = 0; 923 BIO *out = NULL; 924 925 if (!(out = BIO_new_file(serialfile, "w"))) 926 goto err; 927 if (i2a_ASN1_INTEGER(out, serial) <= 0) 928 goto err; 929 if (BIO_puts(out, "\n") <= 0) 930 goto err; 931 ret = 1; 932 err: 933 if (!ret) 934 BIO_printf(bio_err, "could not save serial number to %s\n", 935 serialfile); 936 BIO_free_all(out); 937 return ret; 938 } 939 940 /* 941 * Verify-related method definitions. 942 */ 943 944 static int verify_command(char *data, char *digest, char *queryfile, 945 char *in, int token_in, 946 char *ca_path, char *ca_file, char *untrusted) 947 { 948 BIO *in_bio = NULL; 949 PKCS7 *token = NULL; 950 TS_RESP *response = NULL; 951 TS_VERIFY_CTX *verify_ctx = NULL; 952 int ret = 0; 953 954 /* Decode the token (PKCS7) or response (TS_RESP) files. */ 955 if (!(in_bio = BIO_new_file(in, "rb"))) 956 goto end; 957 if (token_in) { 958 if (!(token = d2i_PKCS7_bio(in_bio, NULL))) 959 goto end; 960 } else { 961 if (!(response = d2i_TS_RESP_bio(in_bio, NULL))) 962 goto end; 963 } 964 965 if (!(verify_ctx = create_verify_ctx(data, digest, queryfile, 966 ca_path, ca_file, untrusted))) 967 goto end; 968 969 /* Checking the token or response against the request. */ 970 ret = token_in ? 971 TS_RESP_verify_token(verify_ctx, token) : 972 TS_RESP_verify_response(verify_ctx, response); 973 974 end: 975 printf("Verification: "); 976 if (ret) 977 printf("OK\n"); 978 else { 979 printf("FAILED\n"); 980 /* Print errors, if there are any. */ 981 ERR_print_errors(bio_err); 982 } 983 984 /* Clean up. */ 985 BIO_free_all(in_bio); 986 PKCS7_free(token); 987 TS_RESP_free(response); 988 TS_VERIFY_CTX_free(verify_ctx); 989 return ret; 990 } 991 992 static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest, 993 char *queryfile, 994 char *ca_path, char *ca_file, 995 char *untrusted) 996 { 997 TS_VERIFY_CTX *ctx = NULL; 998 BIO *input = NULL; 999 TS_REQ *request = NULL; 1000 int ret = 0; 1001 1002 if (data != NULL || digest != NULL) { 1003 if (!(ctx = TS_VERIFY_CTX_new())) 1004 goto err; 1005 ctx->flags = TS_VFY_VERSION | TS_VFY_SIGNER; 1006 if (data != NULL) { 1007 ctx->flags |= TS_VFY_DATA; 1008 if (!(ctx->data = BIO_new_file(data, "rb"))) 1009 goto err; 1010 } else if (digest != NULL) { 1011 long imprint_len; 1012 ctx->flags |= TS_VFY_IMPRINT; 1013 if (!(ctx->imprint = string_to_hex(digest, &imprint_len))) { 1014 BIO_printf(bio_err, "invalid digest string\n"); 1015 goto err; 1016 } 1017 ctx->imprint_len = imprint_len; 1018 } 1019 1020 } else if (queryfile != NULL) { 1021 /* 1022 * The request has just to be read, decoded and converted to a verify 1023 * context object. 1024 */ 1025 if (!(input = BIO_new_file(queryfile, "rb"))) 1026 goto err; 1027 if (!(request = d2i_TS_REQ_bio(input, NULL))) 1028 goto err; 1029 if (!(ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL))) 1030 goto err; 1031 } else 1032 return NULL; 1033 1034 /* Add the signature verification flag and arguments. */ 1035 ctx->flags |= TS_VFY_SIGNATURE; 1036 1037 /* Initialising the X509_STORE object. */ 1038 if (!(ctx->store = create_cert_store(ca_path, ca_file))) 1039 goto err; 1040 1041 /* Loading untrusted certificates. */ 1042 if (untrusted && !(ctx->certs = TS_CONF_load_certs(untrusted))) 1043 goto err; 1044 1045 ret = 1; 1046 err: 1047 if (!ret) { 1048 TS_VERIFY_CTX_free(ctx); 1049 ctx = NULL; 1050 } 1051 BIO_free_all(input); 1052 TS_REQ_free(request); 1053 return ctx; 1054 } 1055 1056 static X509_STORE *create_cert_store(char *ca_path, char *ca_file) 1057 { 1058 X509_STORE *cert_ctx = NULL; 1059 X509_LOOKUP *lookup = NULL; 1060 int i; 1061 1062 /* Creating the X509_STORE object. */ 1063 cert_ctx = X509_STORE_new(); 1064 1065 /* Setting the callback for certificate chain verification. */ 1066 X509_STORE_set_verify_cb(cert_ctx, verify_cb); 1067 1068 /* Adding a trusted certificate directory source. */ 1069 if (ca_path) { 1070 lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir()); 1071 if (lookup == NULL) { 1072 BIO_printf(bio_err, "memory allocation failure\n"); 1073 goto err; 1074 } 1075 i = X509_LOOKUP_add_dir(lookup, ca_path, X509_FILETYPE_PEM); 1076 if (!i) { 1077 BIO_printf(bio_err, "Error loading directory %s\n", ca_path); 1078 goto err; 1079 } 1080 } 1081 1082 /* Adding a trusted certificate file source. */ 1083 if (ca_file) { 1084 lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file()); 1085 if (lookup == NULL) { 1086 BIO_printf(bio_err, "memory allocation failure\n"); 1087 goto err; 1088 } 1089 i = X509_LOOKUP_load_file(lookup, ca_file, X509_FILETYPE_PEM); 1090 if (!i) { 1091 BIO_printf(bio_err, "Error loading file %s\n", ca_file); 1092 goto err; 1093 } 1094 } 1095 1096 return cert_ctx; 1097 err: 1098 X509_STORE_free(cert_ctx); 1099 return NULL; 1100 } 1101 1102 static int MS_CALLBACK verify_cb(int ok, X509_STORE_CTX *ctx) 1103 { 1104 /*- 1105 char buf[256]; 1106 1107 if (!ok) 1108 { 1109 X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), 1110 buf, sizeof(buf)); 1111 printf("%s\n", buf); 1112 printf("error %d at %d depth lookup: %s\n", 1113 ctx->error, ctx->error_depth, 1114 X509_verify_cert_error_string(ctx->error)); 1115 } 1116 */ 1117 1118 return ok; 1119 } 1120