ts.c 31.0 KB
Newer Older
1 2 3
/*
 * Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL project
 * 2002.
4 5 6 7 8 9 10 11 12
 */
/* ====================================================================
 * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
 *
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    licensing@OpenSSL.org.
 *
 * 5. Products derived from this software may not be called "OpenSSL"
 *    nor may "OpenSSL" appear in their names without prior written
 *    permission of the OpenSSL Project.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
 *
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This product includes cryptographic software written by Eric Young
 * (eay@cryptsoft.com).  This product includes software written by Tim
 * Hudson (tjh@cryptsoft.com).
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "apps.h"
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/ts.h>
68
#include <openssl/bn.h>
69

70
/* Request nonce length, in bits (must be a multiple of 8). */
71
#define NONCE_LENGTH            64
72

73
/* Name of config entry that defines the OID file. */
74
#define ENV_OID_FILE            "oid_file"
75

76 77 78 79 80
/* Is |EXACTLY_ONE| of three pointers set? */
#define EXACTLY_ONE(a, b, c) \
        (( a && !b && !c) || \
         ( b && !a && !c) || \
         ( c && !a && !b))
81 82 83 84 85 86

static ASN1_OBJECT *txt2obj(const char *oid);
static CONF *load_config_file(const char *configfile);

/* Query related functions. */
static int query_command(const char *data, char *digest,
87 88
                         const EVP_MD *md, const char *policy, int no_nonce,
                         int cert, const char *in, const char *out, int text);
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 94 95
static ASN1_INTEGER *create_nonce(int bits);

/* Reply related functions. */
96 97
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
98 99 100
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
                         char *out, int token_out, int text);
101 102
static TS_RESP *read_PKCS7(BIO *in_bio);
static TS_RESP *create_response(CONF *conf, const char *section, char *engine,
103
                                char *queryfile, char *passin,
104 105
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy);
106
static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data);
107 108 109 110 111
static ASN1_INTEGER *next_serial(const char *serialfile);
static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial);

/* Verify related functions. */
static int verify_command(char *data, char *digest, char *queryfile,
112
                          char *in, int token_in,
113
                          char *CApath, char *CAfile, char *untrusted);
114 115
static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
                                        char *queryfile,
116
                                        char *CApath, char *CAfile,
117
                                        char *untrusted);
118
static X509_STORE *create_cert_store(char *CApath, char *CAfile);
R
Rich Salz 已提交
119
static int verify_cb(int ok, X509_STORE_CTX *ctx);
120

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
typedef enum OPTION_choice {
    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
    OPT_ENGINE, OPT_CONFIG, OPT_SECTION, OPT_QUERY, OPT_DATA,
    OPT_DIGEST, OPT_RAND, OPT_POLICY, OPT_NO_NONCE, OPT_CERT,
    OPT_IN, OPT_TOKEN_IN, OPT_OUT, OPT_TOKEN_OUT, OPT_TEXT,
    OPT_REPLY, OPT_QUERYFILE, OPT_PASSIN, OPT_INKEY, OPT_SIGNER,
    OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_UNTRUSTED,
    OPT_MD
} OPTION_CHOICE;

OPTIONS ts_options[] = {
    {"help", OPT_HELP, '-', "Display this summary"},
    {"config", OPT_CONFIG, '<', "Configuration file"},
    {"section", OPT_SECTION, 's', "Section to use within config file"},
    {"query", OPT_QUERY, '-', "Generate a TS query"},
    {"data", OPT_DATA, '<', "File to hash"},
    {"digest", OPT_DIGEST, 's', "Digest (as a hex string)"},
    {"rand", OPT_RAND, 's',
     "Load the file(s) into the random number generator"},
    {"policy", OPT_POLICY, 's', "Policy OID to use"},
    {"no_nonce", OPT_NO_NONCE, '-', "Do not include a nonce"},
    {"cert", OPT_CERT, '-', "Put cert request into query"},
    {"in", OPT_IN, '<', "Input file"},
    {"token_in", OPT_TOKEN_IN, '-', "Input is a PKCS#7 file"},
    {"out", OPT_OUT, '>', "Output file"},
    {"token_out", OPT_TOKEN_OUT, '-', "Output is a PKCS#7 file"},
    {"text", OPT_TEXT, '-', "Output text (not DER)"},
    {"reply", OPT_REPLY, '-', "Generate a TS reply"},
    {"queryfile", OPT_QUERYFILE, '<', "File containing a TS query"},
    {"passin", OPT_PASSIN, 's'},
    {"inkey", OPT_INKEY, '<', "File with private key for reply"},
    {"signer", OPT_SIGNER, 's'},
    {"chain", OPT_CHAIN, '<', "File with signer CA chain"},
    {"verify", OPT_VERIFY, '-', "Verify a TS response"},
    {"CApath", OPT_CAPATH, '/', "Path to trusted CA files"},
    {"CAfile", OPT_CAFILE, '<', "File with trusted CA certs"},
    {"untrusted", OPT_UNTRUSTED, '<', "File with untrusted certs"},
158
    {"", OPT_MD, '-', "Any supported digest"},
159 160 161 162 163
#ifndef OPENSSL_NO_ENGINE
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
#endif
    {NULL}
};
164

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
/*
 * This comand is so complex, special help is needed.
 */
static char* opt_helplist[] = {
    "Typical uses:",
    "ts -query [-rand file...] [-config file] [-data file]",
    "          [-digest hexstring] [-policy oid] [-no_nonce] [-cert]",
    "          [-in file] [-out file] [-text]",
    "  or",
    "ts -reply [-config file] [-section tsa_section]",
    "          [-queryfile file] [-passin password]",
    "          [-signer tsa_cert.pem] [-inkey private_key.pem]",
    "          [-chain certs_file.pem] [-policy oid]",
    "          [-in file] [-token_in] [-out file] [-token_out]",
#ifndef OPENSSL_NO_ENGINE
    "          [-text]",
#else
    "          [-text] [-engine id]",
#endif
    "  or",
    "ts -verify -CApath dir -CAfile file.pem -untrusted file.pem",
    "           [-data file] [-digest hexstring]",
    "           [-queryfile file] -in file [-token_in]",
    NULL,
};

int ts_main(int argc, char **argv)
192 193
{
    CONF *conf = NULL;
194
    char *CAfile = NULL, *untrusted = NULL, *engine = NULL, *prog, **helpp;
R
Rich Salz 已提交
195 196
    char *configfile = default_config_file;
    char *section = NULL, *password = NULL;
197 198 199
    char *data = NULL, *digest = NULL, *rnd = NULL, *policy = NULL;
    char *in = NULL, *out = NULL, *queryfile = NULL, *passin = NULL;
    char *inkey = NULL, *signer = NULL, *chain = NULL, *CApath = NULL;
200
    const EVP_MD *md = NULL;
201 202
    OPTION_CHOICE o, mode = OPT_ERR;
    int ret = 1, no_nonce = 0, cert = 0, text = 0;
203 204 205 206 207
    /* Input is ContentInfo instead of TimeStampResp. */
    int token_in = 0;
    /* Output is ContentInfo instead of TimeStampResp. */
    int token_out = 0;

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    prog = opt_init(argc, argv, ts_options);
    while ((o = opt_next()) != OPT_EOF) {
        switch (o) {
        case OPT_EOF:
        case OPT_ERR:
 opthelp:
            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
            goto end;
        case OPT_HELP:
            opt_help(ts_options);
            for (helpp = opt_helplist; *helpp; ++helpp)
                BIO_printf(bio_err, "%s\n", *helpp);
            ret = 0;
            goto end;
        case OPT_CONFIG:
            configfile = opt_arg();
            break;
        case OPT_SECTION:
            section = opt_arg();
            break;
        case OPT_QUERY:
        case OPT_REPLY:
        case OPT_VERIFY:
            if (mode != OPT_ERR)
                goto opthelp;
            mode = o;
            break;
        case OPT_DATA:
            data = opt_arg();
            break;
        case OPT_DIGEST:
            digest = opt_arg();
            break;
        case OPT_RAND:
            rnd = opt_arg();
            break;
        case OPT_POLICY:
            policy = opt_arg();
            break;
        case OPT_NO_NONCE:
248
            no_nonce = 1;
249 250
            break;
        case OPT_CERT:
251
            cert = 1;
252 253 254 255 256
            break;
        case OPT_IN:
            in = opt_arg();
            break;
        case OPT_TOKEN_IN:
257
            token_in = 1;
258 259 260 261 262
            break;
        case OPT_OUT:
            out = opt_arg();
            break;
        case OPT_TOKEN_OUT:
263
            token_out = 1;
264 265
            break;
        case OPT_TEXT:
266
            text = 1;
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
            break;
        case OPT_QUERYFILE:
            queryfile = opt_arg();
            break;
        case OPT_PASSIN:
            passin = opt_arg();
            break;
        case OPT_INKEY:
            inkey = opt_arg();
            break;
        case OPT_SIGNER:
            signer = opt_arg();
            break;
        case OPT_CHAIN:
            chain = opt_arg();
            break;
        case OPT_CAPATH:
            CApath = opt_arg();
            break;
        case OPT_CAFILE:
            CAfile = opt_arg();
            break;
        case OPT_UNTRUSTED:
            untrusted = opt_arg();
            break;
        case OPT_ENGINE:
            engine = opt_arg();
            break;
        case OPT_MD:
            if (!opt_md(opt_unknown(), &md))
                goto opthelp;
            break;
        }
300
    }
301 302 303
    argc = opt_num_rest();
    if (mode == OPT_ERR || argc != 0)
        goto opthelp;
304 305

    /* Seed the random number generator if it is going to be used. */
306 307
    if (mode == OPT_QUERY && !no_nonce) {
        if (!app_RAND_load_file(NULL, 1) && rnd == NULL)
308 309 310 311 312 313 314
            BIO_printf(bio_err, "warning, not much extra random "
                       "data, consider using the -rand option\n");
        if (rnd != NULL)
            BIO_printf(bio_err, "%ld semi-random bytes loaded\n",
                       app_RAND_load_files(rnd));
    }

315 316
    if (mode == OPT_REPLY && passin &&
        !app_passwd(passin, NULL, &password, NULL)) {
317
        BIO_printf(bio_err, "Error getting password.\n");
318
        goto end;
319 320
    }

R
Richard Levitte 已提交
321 322 323 324
    conf = load_config_file(configfile);
    if (!app_load_modules(conf))
        goto end;

325
    /* Check parameter consistency and execute the appropriate function. */
326
    switch (mode) {
327 328 329 330
    default:
    case OPT_ERR:
        goto opthelp;
    case OPT_QUERY:
331
        if ((data != NULL) && (digest != NULL))
332
            goto opthelp;
333 334 335
        ret = !query_command(data, digest, md, policy, no_nonce, cert,
                             in, out, text);
        break;
336
    case OPT_REPLY:
337 338
        if ((in != NULL) && (queryfile != NULL))
            goto opthelp;
339
        if (in == NULL) {
340
            if ((conf == NULL) || (token_in != 0))
341
                goto opthelp;
342 343
        }
        ret = !reply_command(conf, section, engine, queryfile,
344
                             password, inkey, md, signer, chain, policy,
345 346
                             in, token_in, out, token_out, text);
        break;
347
    case OPT_VERIFY:
348
        if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
349
            goto opthelp;
350
        ret = !verify_command(data, digest, queryfile, in, token_in,
351
                              CApath, CAfile, untrusted);
352 353
    }

354 355
 end:
    app_RAND_write_file(NULL);
356 357 358
    NCONF_free(conf);
    OPENSSL_free(password);
    OBJ_cleanup();
359
    return (ret);
360
}
361 362 363 364 365 366

/*
 * Configuration file-related function definitions.
 */

static ASN1_OBJECT *txt2obj(const char *oid)
367 368
{
    ASN1_OBJECT *oid_obj = NULL;
369

370
    if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL)
371
        BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
372

373 374
    return oid_obj;
}
375 376

static CONF *load_config_file(const char *configfile)
377
{
R
Rich Salz 已提交
378
    CONF *conf = app_load_config(configfile);
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394

    if (conf != NULL) {
        const char *p;

        BIO_printf(bio_err, "Using configuration from %s\n", configfile);
        p = NCONF_get_string(conf, NULL, ENV_OID_FILE);
        if (p != NULL) {
            BIO *oid_bio = BIO_new_file(p, "r");
            if (!oid_bio)
                ERR_print_errors(bio_err);
            else {
                OBJ_create_objects(oid_bio);
                BIO_free_all(oid_bio);
            }
        } else
            ERR_clear_error();
395
        if (!add_oid_section(conf))
396 397 398 399
            ERR_print_errors(bio_err);
    }
    return conf;
}
400 401 402 403 404

/*
 * Query-related method definitions.
 */
static int query_command(const char *data, char *digest, const EVP_MD *md,
405 406 407 408 409 410 411 412 413
                         const char *policy, int no_nonce,
                         int cert, const char *in, const char *out, int text)
{
    int ret = 0;
    TS_REQ *query = NULL;
    BIO *in_bio = NULL;
    BIO *data_bio = NULL;
    BIO *out_bio = NULL;

414
    /* Build query object. */
415
    if (in != NULL) {
416
        if ((in_bio = bio_open_default(in, 'r', FORMAT_ASN1)) == NULL)
417 418 419
            goto end;
        query = d2i_TS_REQ_bio(in_bio, NULL);
    } else {
420
        if (digest == NULL
421
            && (data_bio = bio_open_default(data, 'r', FORMAT_ASN1)) == NULL)
422 423 424 425 426 427 428
            goto end;
        query = create_query(data_bio, digest, md, policy, no_nonce, cert);
    }
    if (query == NULL)
        goto end;

    if (text) {
429 430
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
            goto end;
431 432 433
        if (!TS_REQ_print_bio(out_bio, query))
            goto end;
    } else {
434 435
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
436 437 438 439 440
        if (!i2d_TS_REQ_bio(out_bio, query))
            goto end;
    }

    ret = 1;
441 442

 end:
443 444 445 446 447 448 449
    ERR_print_errors(bio_err);
    BIO_free_all(in_bio);
    BIO_free_all(data_bio);
    BIO_free_all(out_bio);
    TS_REQ_free(query);
    return ret;
}
450 451

static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md,
452 453 454 455 456 457 458 459 460 461 462
                            const char *policy, int no_nonce, int cert)
{
    int ret = 0;
    TS_REQ *ts_req = NULL;
    int len;
    TS_MSG_IMPRINT *msg_imprint = NULL;
    X509_ALGOR *algo = NULL;
    unsigned char *data = NULL;
    ASN1_OBJECT *policy_obj = NULL;
    ASN1_INTEGER *nonce_asn1 = NULL;

463
    if (md == NULL && (md = EVP_get_digestbyname("sha1")) == NULL)
464
        goto err;
465
    if ((ts_req = TS_REQ_new()) == NULL)
466 467 468
        goto err;
    if (!TS_REQ_set_version(ts_req, 1))
        goto err;
469
    if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
470
        goto err;
471
    if ((algo = X509_ALGOR_new()) == NULL)
472
        goto err;
473
    if ((algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))) == NULL)
474
        goto err;
475
    if ((algo->parameter = ASN1_TYPE_new()) == NULL)
476 477 478 479 480 481 482 483 484 485
        goto err;
    algo->parameter->type = V_ASN1_NULL;
    if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
        goto err;
    if ((len = create_digest(data_bio, digest, md, &data)) == 0)
        goto err;
    if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len))
        goto err;
    if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
        goto err;
486
    if (policy && (policy_obj = txt2obj(policy)) == NULL)
487 488 489 490 491
        goto err;
    if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
        goto err;

    /* Setting nonce if requested. */
492
    if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL)
493 494 495 496 497 498 499
        goto err;
    if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
        goto err;
    if (!TS_REQ_set_cert_req(ts_req, cert))
        goto err;

    ret = 1;
500
 err:
501 502 503 504
    if (!ret) {
        TS_REQ_free(ts_req);
        ts_req = NULL;
        BIO_printf(bio_err, "could not create query\n");
505
        ERR_print_errors(bio_err);
506 507 508 509 510 511 512 513
    }
    TS_MSG_IMPRINT_free(msg_imprint);
    X509_ALGOR_free(algo);
    OPENSSL_free(data);
    ASN1_OBJECT_free(policy_obj);
    ASN1_INTEGER_free(nonce_asn1);
    return ts_req;
}
514 515

static int create_digest(BIO *input, char *digest, const EVP_MD *md,
516 517 518 519 520 521
                         unsigned char **md_value)
{
    int md_value_len;

    md_value_len = EVP_MD_size(md);
    if (md_value_len < 0)
522 523
        return 0;

524
    if (input) {
525
        EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
526 527 528
        unsigned char buffer[4096];
        int length;

529 530
        if (md_ctx == NULL)
            return 0;
R
Rich Salz 已提交
531
        *md_value = app_malloc(md_value_len, "digest buffer");
532
        EVP_DigestInit(md_ctx, md);
533
        while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
534
            EVP_DigestUpdate(md_ctx, buffer, length);
535
        }
536
        if (!EVP_DigestFinal(md_ctx, *md_value, NULL)) {
537
            EVP_MD_CTX_free(md_ctx);
538
            return 0;
539
        }
540
        EVP_MD_CTX_free(md_ctx);
541 542 543 544 545 546 547 548
    } else {
        long digest_len;
        *md_value = string_to_hex(digest, &digest_len);
        if (!*md_value || md_value_len != digest_len) {
            OPENSSL_free(*md_value);
            *md_value = NULL;
            BIO_printf(bio_err, "bad digest, %d bytes "
                       "must be specified\n", md_value_len);
549
            return 0;
550 551 552 553
        }
    }
    return md_value_len;
}
554 555

static ASN1_INTEGER *create_nonce(int bits)
556 557 558 559 560 561 562 563 564 565 566 567
{
    unsigned char buf[20];
    ASN1_INTEGER *nonce = NULL;
    int len = (bits - 1) / 8 + 1;
    int i;

    if (len > (int)sizeof(buf))
        goto err;
    if (RAND_bytes(buf, len) <= 0)
        goto err;

    /* Find the first non-zero byte and creating ASN1_INTEGER object. */
568 569 570
    for (i = 0; i < len && !buf[i]; ++i)
        continue;
    if ((nonce = ASN1_INTEGER_new()) == NULL)
571 572 573
        goto err;
    OPENSSL_free(nonce->data);
    nonce->length = len - i;
R
Rich Salz 已提交
574
    nonce->data = app_malloc(nonce->length + 1, "nonce buffer");
575 576
    memcpy(nonce->data, buf + i, nonce->length);
    return nonce;
577

578
 err:
579 580 581 582 583
    BIO_printf(bio_err, "could not create nonce\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
}

584 585 586 587
/*
 * Reply-related method definitions.
 */

588 589
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
590 591
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
                         char *out, int token_out, int text)
{
    int ret = 0;
    TS_RESP *response = NULL;
    BIO *in_bio = NULL;
    BIO *query_bio = NULL;
    BIO *inkey_bio = NULL;
    BIO *signer_bio = NULL;
    BIO *out_bio = NULL;

    if (in != NULL) {
        if ((in_bio = BIO_new_file(in, "rb")) == NULL)
            goto end;
        if (token_in) {
            response = read_PKCS7(in_bio);
        } else {
            response = d2i_TS_RESP_bio(in_bio, NULL);
        }
    } else {
        response = create_response(conf, section, engine, queryfile,
612
                                   passin, inkey, md, signer, chain, policy);
613 614 615 616 617 618 619 620
        if (response)
            BIO_printf(bio_err, "Response has been generated.\n");
        else
            BIO_printf(bio_err, "Response is not generated.\n");
    }
    if (response == NULL)
        goto end;

621
    /* Write response. */
622
    if (text) {
623 624
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
        goto end;
625 626 627 628 629 630 631 632 633
        if (token_out) {
            TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response);
            if (!TS_TST_INFO_print_bio(out_bio, tst_info))
                goto end;
        } else {
            if (!TS_RESP_print_bio(out_bio, response))
                goto end;
        }
    } else {
634 635
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
636 637 638 639 640 641 642 643 644 645 646
        if (token_out) {
            PKCS7 *token = TS_RESP_get_token(response);
            if (!i2d_PKCS7_bio(out_bio, token))
                goto end;
        } else {
            if (!i2d_TS_RESP_bio(out_bio, response))
                goto end;
        }
    }

    ret = 1;
647 648

 end:
649 650 651 652 653 654 655 656 657
    ERR_print_errors(bio_err);
    BIO_free_all(in_bio);
    BIO_free_all(query_bio);
    BIO_free_all(inkey_bio);
    BIO_free_all(signer_bio);
    BIO_free_all(out_bio);
    TS_RESP_free(response);
    return ret;
}
658 659 660

/* Reads a PKCS7 token and adds default 'granted' status info to it. */
static TS_RESP *read_PKCS7(BIO *in_bio)
661 662 663 664 665 666 667
{
    int ret = 0;
    PKCS7 *token = NULL;
    TS_TST_INFO *tst_info = NULL;
    TS_RESP *resp = NULL;
    TS_STATUS_INFO *si = NULL;

668
    if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
669
        goto end;
670
    if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL)
671
        goto end;
672
    if ((resp = TS_RESP_new()) == NULL)
673
        goto end;
674
    if ((si = TS_STATUS_INFO_new()) == NULL)
675
        goto end;
R
Rich Salz 已提交
676
    if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED))
677 678 679 680 681 682 683
        goto end;
    if (!TS_RESP_set_status_info(resp, si))
        goto end;
    TS_RESP_set_tst_info(resp, token, tst_info);
    token = NULL;               /* Ownership is lost. */
    tst_info = NULL;            /* Ownership is lost. */
    ret = 1;
684

685
 end:
686 687 688 689 690 691 692 693 694 695 696
    PKCS7_free(token);
    TS_TST_INFO_free(tst_info);
    if (!ret) {
        TS_RESP_free(resp);
        resp = NULL;
    }
    TS_STATUS_INFO_free(si);
    return resp;
}

static TS_RESP *create_response(CONF *conf, const char *section, char *engine,
697
                                char *queryfile, char *passin,
698 699
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy)
700 701 702 703 704 705
{
    int ret = 0;
    TS_RESP *response = NULL;
    BIO *query_bio = NULL;
    TS_RESP_CTX *resp_ctx = NULL;

706
    if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL)
707
        goto end;
708
    if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL)
709
        goto end;
710
    if ((resp_ctx = TS_RESP_CTX_new()) == NULL)
711 712 713
        goto end;
    if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
        goto end;
D
Dr. Stephen Henson 已提交
714
#ifndef OPENSSL_NO_ENGINE
715 716
    if (!TS_CONF_set_crypto_device(conf, section, engine))
        goto end;
D
Dr. Stephen Henson 已提交
717
#endif
718 719 720 721 722 723
    if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx))
        goto end;
    if (!TS_CONF_set_certs(conf, section, chain, resp_ctx))
        goto end;
    if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx))
        goto end;
724 725 726 727 728 729 730 731

    if (md) {
        if (!TS_RESP_CTX_set_signer_digest(resp_ctx, md))
            goto end;
    } else if (!TS_CONF_set_signer_digest(conf, section, NULL, resp_ctx)) {
            goto end;
    }

732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
    if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx))
        goto end;
    if (!TS_CONF_set_policies(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_digests(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_accuracy(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_ordering(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_tsa_name(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx))
        goto end;
748
    if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL)
749 750
        goto end;
    ret = 1;
751

752
 end:
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
    if (!ret) {
        TS_RESP_free(response);
        response = NULL;
    }
    TS_RESP_CTX_free(resp_ctx);
    BIO_free_all(query_bio);
    return response;
}

static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data)
{
    const char *serial_file = (const char *)data;
    ASN1_INTEGER *serial = next_serial(serial_file);

    if (!serial) {
        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
                                    "Error during serial number "
                                    "generation.");
        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE);
    } else
        save_ts_serial(serial_file, serial);

    return serial;
}
777 778

static ASN1_INTEGER *next_serial(const char *serialfile)
779 780 781 782 783 784
{
    int ret = 0;
    BIO *in = NULL;
    ASN1_INTEGER *serial = NULL;
    BIGNUM *bn = NULL;

785
    if ((serial = ASN1_INTEGER_new()) == NULL)
786 787
        goto err;

788
    if ((in = BIO_new_file(serialfile, "r")) == NULL) {
789 790 791 792 793 794 795 796 797 798 799 800
        ERR_clear_error();
        BIO_printf(bio_err, "Warning: could not open file %s for "
                   "reading, using serial number: 1\n", serialfile);
        if (!ASN1_INTEGER_set(serial, 1))
            goto err;
    } else {
        char buf[1024];
        if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) {
            BIO_printf(bio_err, "unable to load number from %s\n",
                       serialfile);
            goto err;
        }
801
        if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL)
802 803 804 805 806
            goto err;
        ASN1_INTEGER_free(serial);
        serial = NULL;
        if (!BN_add_word(bn, 1))
            goto err;
807
        if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL)
808 809 810
            goto err;
    }
    ret = 1;
811

812
 err:
813 814 815 816 817 818 819 820
    if (!ret) {
        ASN1_INTEGER_free(serial);
        serial = NULL;
    }
    BIO_free_all(in);
    BN_free(bn);
    return serial;
}
821 822

static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
823 824 825 826
{
    int ret = 0;
    BIO *out = NULL;

827
    if ((out = BIO_new_file(serialfile, "w")) == NULL)
828 829 830 831 832 833
        goto err;
    if (i2a_ASN1_INTEGER(out, serial) <= 0)
        goto err;
    if (BIO_puts(out, "\n") <= 0)
        goto err;
    ret = 1;
834
 err:
835 836 837 838 839 840
    if (!ret)
        BIO_printf(bio_err, "could not save serial number to %s\n",
                   serialfile);
    BIO_free_all(out);
    return ret;
}
841

842

843 844 845 846 847
/*
 * Verify-related method definitions.
 */

static int verify_command(char *data, char *digest, char *queryfile,
848
                          char *in, int token_in,
849
                          char *CApath, char *CAfile, char *untrusted)
850 851 852 853 854 855 856
{
    BIO *in_bio = NULL;
    PKCS7 *token = NULL;
    TS_RESP *response = NULL;
    TS_VERIFY_CTX *verify_ctx = NULL;
    int ret = 0;

857
    if ((in_bio = BIO_new_file(in, "rb")) == NULL)
858 859
        goto end;
    if (token_in) {
860
        if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
861 862
            goto end;
    } else {
863
        if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL)
864 865 866
            goto end;
    }

867 868
    if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
                                        CApath, CAfile, untrusted)) == NULL)
869 870
        goto end;

871 872 873
    ret = token_in
        ? TS_RESP_verify_token(verify_ctx, token)
        : TS_RESP_verify_response(verify_ctx, response);
874 875

 end:
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
    printf("Verification: ");
    if (ret)
        printf("OK\n");
    else {
        printf("FAILED\n");
        ERR_print_errors(bio_err);
    }

    BIO_free_all(in_bio);
    PKCS7_free(token);
    TS_RESP_free(response);
    TS_VERIFY_CTX_free(verify_ctx);
    return ret;
}

static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
                                        char *queryfile,
893
                                        char *CApath, char *CAfile,
894 895 896 897 898 899
                                        char *untrusted)
{
    TS_VERIFY_CTX *ctx = NULL;
    BIO *input = NULL;
    TS_REQ *request = NULL;
    int ret = 0;
R
Rich Salz 已提交
900
    int f = 0;
901 902

    if (data != NULL || digest != NULL) {
903
        if ((ctx = TS_VERIFY_CTX_new()) == NULL)
904
            goto err;
R
Rich Salz 已提交
905
        f = TS_VFY_VERSION | TS_VFY_SIGNER;
906
        if (data != NULL) {
R
Rich Salz 已提交
907 908
            f |= TS_VFY_DATA;
            if (TS_VERIFY_CTX_set_data(ctx, BIO_new_file(data, "rb")) == NULL)
909 910 911
                goto err;
        } else if (digest != NULL) {
            long imprint_len;
R
Rich Salz 已提交
912 913 914
            unsigned char *hexstr = string_to_hex(digest, &imprint_len);
            f |= TS_VFY_IMPRINT;
            if (TS_VERIFY_CTX_set_imprint(ctx, hexstr, imprint_len) == NULL) {
915 916 917 918 919 920
                BIO_printf(bio_err, "invalid digest string\n");
                goto err;
            }
        }

    } else if (queryfile != NULL) {
921
        if ((input = BIO_new_file(queryfile, "rb")) == NULL)
922
            goto err;
923
        if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL)
924
            goto err;
925
        if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL)
926 927 928 929 930
            goto err;
    } else
        return NULL;

    /* Add the signature verification flag and arguments. */
R
Rich Salz 已提交
931
    TS_VERIFY_CTX_add_flags(ctx, f | TS_VFY_SIGNATURE);
932 933

    /* Initialising the X509_STORE object. */
R
Rich Salz 已提交
934 935
    if (TS_VERIFY_CTX_set_store(ctx, create_cert_store(CApath, CAfile))
            == NULL)
936 937 938
        goto err;

    /* Loading untrusted certificates. */
R
Rich Salz 已提交
939 940
    if (untrusted
        && TS_VERIFY_CTS_set_certs(ctx, TS_CONF_load_certs(untrusted)) == NULL)
941 942
        goto err;
    ret = 1;
943

944
 err:
945 946 947 948 949 950 951 952
    if (!ret) {
        TS_VERIFY_CTX_free(ctx);
        ctx = NULL;
    }
    BIO_free_all(input);
    TS_REQ_free(request);
    return ctx;
}
953

954
static X509_STORE *create_cert_store(char *CApath, char *CAfile)
955 956 957 958 959 960 961
{
    X509_STORE *cert_ctx = NULL;
    X509_LOOKUP *lookup = NULL;
    int i;

    cert_ctx = X509_STORE_new();
    X509_STORE_set_verify_cb(cert_ctx, verify_cb);
962
    if (CApath != NULL) {
963 964 965 966 967
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
968
        i = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
969
        if (!i) {
970
            BIO_printf(bio_err, "Error loading directory %s\n", CApath);
971 972 973 974
            goto err;
        }
    }

975
    if (CAfile != NULL) {
976 977 978 979 980
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
981
        i = X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM);
982
        if (!i) {
983
            BIO_printf(bio_err, "Error loading file %s\n", CAfile);
984 985 986 987
            goto err;
        }
    }
    return cert_ctx;
988

989
 err:
990 991 992
    X509_STORE_free(cert_ctx);
    return NULL;
}
993

R
Rich Salz 已提交
994
static int verify_cb(int ok, X509_STORE_CTX *ctx)
995 996 997
{
    return ok;
}