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