ts.c 32.2 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
 *
 * 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).
 *
 */

R
Richard Levitte 已提交
59
#include <openssl/opensslconf.h>
M
Matt Caswell 已提交
60 61 62
#ifdef OPENSSL_NO_TS
NON_EMPTY_TRANSLATION_UNIT
#else
R
Richard Levitte 已提交
63 64 65 66 67 68 69 70 71 72
# 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>
# include <openssl/bn.h>
73

74
/* Request nonce length, in bits (must be a multiple of 8). */
R
Richard Levitte 已提交
75
# define NONCE_LENGTH            64
76

77
/* Name of config entry that defines the OID file. */
R
Richard Levitte 已提交
78
# define ENV_OID_FILE            "oid_file"
79

80
/* Is |EXACTLY_ONE| of three pointers set? */
R
Richard Levitte 已提交
81
# define EXACTLY_ONE(a, b, c) \
82 83 84
        (( a && !b && !c) || \
         ( b && !a && !c) || \
         ( c && !a && !b))
85 86 87 88 89 90

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,
91 92
                         const EVP_MD *md, const char *policy, int no_nonce,
                         int cert, const char *in, const char *out, int text);
93
static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md,
94
                            const char *policy, int no_nonce, int cert);
95
static int create_digest(BIO *input, char *digest,
96
                         const EVP_MD *md, unsigned char **md_value);
97 98 99
static ASN1_INTEGER *create_nonce(int bits);

/* Reply related functions. */
100 101
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
102 103 104
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
                         char *out, int token_out, int text);
105 106
static TS_RESP *read_PKCS7(BIO *in_bio);
static TS_RESP *create_response(CONF *conf, const char *section, char *engine,
107
                                char *queryfile, char *passin,
108 109
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy);
110
static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data);
111 112 113 114 115
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,
116
                          char *in, int token_in,
F
fbroda 已提交
117 118
                          char *CApath, char *CAfile, char *untrusted,
                          X509_VERIFY_PARAM *vpm);
119 120
static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
                                        char *queryfile,
121
                                        char *CApath, char *CAfile,
F
fbroda 已提交
122 123 124 125
                                        char *untrusted,
                                        X509_VERIFY_PARAM *vpm);
static X509_STORE *create_cert_store(char *CApath, char *CAfile,
                                     X509_VERIFY_PARAM *vpm);
R
Rich Salz 已提交
126
static int verify_cb(int ok, X509_STORE_CTX *ctx);
127

128 129 130
typedef enum OPTION_choice {
    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
    OPT_ENGINE, OPT_CONFIG, OPT_SECTION, OPT_QUERY, OPT_DATA,
F
fbroda 已提交
131
    OPT_DIGEST, OPT_RAND, OPT_TSPOLICY, OPT_NO_NONCE, OPT_CERT,
132 133 134
    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,
F
fbroda 已提交
135
    OPT_MD, OPT_V_ENUM
136 137 138 139 140 141 142 143 144 145 146
} 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"},
F
fbroda 已提交
147
    {"tspolicy", OPT_TSPOLICY, 's', "Policy OID to use"},
148 149 150 151 152 153 154 155 156
    {"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"},
157
    {"passin", OPT_PASSIN, 's', "Input file pass phrase source"},
158 159 160 161 162 163 164
    {"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"},
165
    {"", OPT_MD, '-', "Any supported digest"},
R
Richard Levitte 已提交
166
# ifndef OPENSSL_NO_ENGINE
167
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
R
Richard Levitte 已提交
168
# endif
F
fbroda 已提交
169 170 171
    {OPT_HELP_STR, 1, '-', "\nOptions specific to 'ts -verify': \n"},
    OPT_V_OPTIONS,
    {OPT_HELP_STR, 1, '-', "\n"},
172 173
    {NULL}
};
174

175 176 177 178 179 180
/*
 * This comand is so complex, special help is needed.
 */
static char* opt_helplist[] = {
    "Typical uses:",
    "ts -query [-rand file...] [-config file] [-data file]",
F
fbroda 已提交
181
    "          [-digest hexstring] [-tspolicy oid] [-no_nonce] [-cert]",
182 183 184 185 186
    "          [-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]",
F
fbroda 已提交
187
    "          [-chain certs_file.pem] [-tspolicy oid]",
188
    "          [-in file] [-token_in] [-out file] [-token_out]",
R
Richard Levitte 已提交
189
# ifndef OPENSSL_NO_ENGINE
190
    "          [-text]",
R
Richard Levitte 已提交
191
# else
192
    "          [-text] [-engine id]",
R
Richard Levitte 已提交
193
# endif
194 195 196 197
    "  or",
    "ts -verify -CApath dir -CAfile file.pem -untrusted file.pem",
    "           [-data file] [-digest hexstring]",
    "           [-queryfile file] -in file [-token_in]",
F
fbroda 已提交
198
    "           [[options specific to 'ts -verify']]",
199 200 201 202
    NULL,
};

int ts_main(int argc, char **argv)
203 204
{
    CONF *conf = NULL;
205
    char *CAfile = NULL, *untrusted = NULL, *engine = NULL, *prog, **helpp;
R
Rich Salz 已提交
206 207
    char *configfile = default_config_file;
    char *section = NULL, *password = NULL;
208 209 210
    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;
211
    const EVP_MD *md = NULL;
212 213
    OPTION_CHOICE o, mode = OPT_ERR;
    int ret = 1, no_nonce = 0, cert = 0, text = 0;
F
fbroda 已提交
214 215
    int vpmtouched = 0;
    X509_VERIFY_PARAM *vpm = NULL;
216 217 218 219 220
    /* Input is ContentInfo instead of TimeStampResp. */
    int token_in = 0;
    /* Output is ContentInfo instead of TimeStampResp. */
    int token_out = 0;

F
fbroda 已提交
221 222 223
    if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
        goto end;

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
    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;
F
fbroda 已提交
260
        case OPT_TSPOLICY:
261 262 263
            policy = opt_arg();
            break;
        case OPT_NO_NONCE:
264
            no_nonce = 1;
265 266
            break;
        case OPT_CERT:
267
            cert = 1;
268 269 270 271 272
            break;
        case OPT_IN:
            in = opt_arg();
            break;
        case OPT_TOKEN_IN:
273
            token_in = 1;
274 275 276 277 278
            break;
        case OPT_OUT:
            out = opt_arg();
            break;
        case OPT_TOKEN_OUT:
279
            token_out = 1;
280 281
            break;
        case OPT_TEXT:
282
            text = 1;
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
            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;
F
fbroda 已提交
315 316 317 318 319
        case OPT_V_CASES:
            if (!opt_verify(o, vpm))
                goto end;
            vpmtouched++;
            break;
320
        }
321
    }
322
    if (mode == OPT_ERR || opt_num_rest() != 0)
323
        goto opthelp;
324 325

    /* Seed the random number generator if it is going to be used. */
326 327
    if (mode == OPT_QUERY && !no_nonce) {
        if (!app_RAND_load_file(NULL, 1) && rnd == NULL)
328 329 330 331 332 333 334
            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));
    }

335 336
    if (mode == OPT_REPLY && passin &&
        !app_passwd(passin, NULL, &password, NULL)) {
337
        BIO_printf(bio_err, "Error getting password.\n");
338
        goto end;
339 340
    }

R
Richard Levitte 已提交
341 342 343 344
    conf = load_config_file(configfile);
    if (!app_load_modules(conf))
        goto end;

345
    /* Check parameter consistency and execute the appropriate function. */
346
    switch (mode) {
347 348 349 350
    default:
    case OPT_ERR:
        goto opthelp;
    case OPT_QUERY:
F
fbroda 已提交
351 352
        if (vpmtouched)
            goto opthelp;
353
        if ((data != NULL) && (digest != NULL))
354
            goto opthelp;
355 356 357
        ret = !query_command(data, digest, md, policy, no_nonce, cert,
                             in, out, text);
        break;
358
    case OPT_REPLY:
F
fbroda 已提交
359 360
        if (vpmtouched)
            goto opthelp;
361 362
        if ((in != NULL) && (queryfile != NULL))
            goto opthelp;
363
        if (in == NULL) {
364
            if ((conf == NULL) || (token_in != 0))
365
                goto opthelp;
366 367
        }
        ret = !reply_command(conf, section, engine, queryfile,
368
                             password, inkey, md, signer, chain, policy,
369 370
                             in, token_in, out, token_out, text);
        break;
371
    case OPT_VERIFY:
372
        if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
373
            goto opthelp;
374
        ret = !verify_command(data, digest, queryfile, in, token_in,
F
fbroda 已提交
375 376
                              CApath, CAfile, untrusted, 
                              vpmtouched ? vpm : NULL);
377 378
    }

379
 end:
F
fbroda 已提交
380
    X509_VERIFY_PARAM_free(vpm);
381
    app_RAND_write_file(NULL);
382 383
    NCONF_free(conf);
    OPENSSL_free(password);
384
    return (ret);
385
}
386 387 388 389 390 391

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

static ASN1_OBJECT *txt2obj(const char *oid)
392 393
{
    ASN1_OBJECT *oid_obj = NULL;
394

395
    if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL)
396
        BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
397

398 399
    return oid_obj;
}
400 401

static CONF *load_config_file(const char *configfile)
402
{
R
Rich Salz 已提交
403
    CONF *conf = app_load_config(configfile);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

    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();
420
        if (!add_oid_section(conf))
421 422 423 424
            ERR_print_errors(bio_err);
    }
    return conf;
}
425 426 427 428 429

/*
 * Query-related method definitions.
 */
static int query_command(const char *data, char *digest, const EVP_MD *md,
430 431 432 433 434 435 436 437 438
                         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;

439
    /* Build query object. */
440
    if (in != NULL) {
441
        if ((in_bio = bio_open_default(in, 'r', FORMAT_ASN1)) == NULL)
442 443 444
            goto end;
        query = d2i_TS_REQ_bio(in_bio, NULL);
    } else {
445
        if (digest == NULL
446
            && (data_bio = bio_open_default(data, 'r', FORMAT_ASN1)) == NULL)
447 448 449 450 451 452 453
            goto end;
        query = create_query(data_bio, digest, md, policy, no_nonce, cert);
    }
    if (query == NULL)
        goto end;

    if (text) {
454 455
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
            goto end;
456 457 458
        if (!TS_REQ_print_bio(out_bio, query))
            goto end;
    } else {
459 460
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
461 462 463 464 465
        if (!i2d_TS_REQ_bio(out_bio, query))
            goto end;
    }

    ret = 1;
466 467

 end:
468 469 470 471 472 473 474
    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;
}
475 476

static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md,
477 478 479 480 481 482 483 484 485 486 487
                            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;

488
    if (md == NULL && (md = EVP_get_digestbyname("sha1")) == NULL)
489
        goto err;
490
    if ((ts_req = TS_REQ_new()) == NULL)
491 492 493
        goto err;
    if (!TS_REQ_set_version(ts_req, 1))
        goto err;
494
    if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
495
        goto err;
496
    if ((algo = X509_ALGOR_new()) == NULL)
497
        goto err;
498
    if ((algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))) == NULL)
499
        goto err;
500
    if ((algo->parameter = ASN1_TYPE_new()) == NULL)
501 502 503 504 505 506 507 508 509 510
        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;
511
    if (policy && (policy_obj = txt2obj(policy)) == NULL)
512 513 514 515 516
        goto err;
    if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
        goto err;

    /* Setting nonce if requested. */
517
    if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL)
518 519 520 521 522 523 524
        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;
525
 err:
526 527 528 529
    if (!ret) {
        TS_REQ_free(ts_req);
        ts_req = NULL;
        BIO_printf(bio_err, "could not create query\n");
530
        ERR_print_errors(bio_err);
531 532 533 534 535 536 537 538
    }
    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;
}
539 540

static int create_digest(BIO *input, char *digest, const EVP_MD *md,
541 542 543 544 545 546
                         unsigned char **md_value)
{
    int md_value_len;

    md_value_len = EVP_MD_size(md);
    if (md_value_len < 0)
547 548
        return 0;

549
    if (input) {
550
        EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
551 552 553
        unsigned char buffer[4096];
        int length;

554 555
        if (md_ctx == NULL)
            return 0;
R
Rich Salz 已提交
556
        *md_value = app_malloc(md_value_len, "digest buffer");
557
        EVP_DigestInit(md_ctx, md);
558
        while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
559
            EVP_DigestUpdate(md_ctx, buffer, length);
560
        }
561
        if (!EVP_DigestFinal(md_ctx, *md_value, NULL)) {
562
            EVP_MD_CTX_free(md_ctx);
563
            return 0;
564
        }
565
        EVP_MD_CTX_free(md_ctx);
566 567
    } else {
        long digest_len;
568
        *md_value = OPENSSL_hexstr2buf(digest, &digest_len);
569 570 571 572 573
        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);
574
            return 0;
575 576 577 578
        }
    }
    return md_value_len;
}
579 580

static ASN1_INTEGER *create_nonce(int bits)
581 582 583 584 585 586 587 588 589 590 591 592
{
    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. */
593 594 595
    for (i = 0; i < len && !buf[i]; ++i)
        continue;
    if ((nonce = ASN1_INTEGER_new()) == NULL)
596 597 598
        goto err;
    OPENSSL_free(nonce->data);
    nonce->length = len - i;
R
Rich Salz 已提交
599
    nonce->data = app_malloc(nonce->length + 1, "nonce buffer");
600 601
    memcpy(nonce->data, buf + i, nonce->length);
    return nonce;
602

603
 err:
604 605 606 607 608
    BIO_printf(bio_err, "could not create nonce\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
}

609 610 611 612
/*
 * Reply-related method definitions.
 */

613 614
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
615 616
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
                         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,
637
                                   passin, inkey, md, signer, chain, policy);
638 639 640 641 642 643 644 645
        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;

646
    /* Write response. */
647
    if (text) {
648 649
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
        goto end;
650 651 652 653 654 655 656 657 658
        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 {
659 660
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
661 662 663 664 665 666 667 668 669 670 671
        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;
672 673

 end:
674 675 676 677 678 679 680 681 682
    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;
}
683 684 685

/* Reads a PKCS7 token and adds default 'granted' status info to it. */
static TS_RESP *read_PKCS7(BIO *in_bio)
686 687 688 689 690 691 692
{
    int ret = 0;
    PKCS7 *token = NULL;
    TS_TST_INFO *tst_info = NULL;
    TS_RESP *resp = NULL;
    TS_STATUS_INFO *si = NULL;

693
    if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
694
        goto end;
695
    if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL)
696
        goto end;
697
    if ((resp = TS_RESP_new()) == NULL)
698
        goto end;
699
    if ((si = TS_STATUS_INFO_new()) == NULL)
700
        goto end;
R
Rich Salz 已提交
701
    if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED))
702 703 704 705 706 707 708
        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;
709

710
 end:
711 712 713 714 715 716 717 718 719 720 721
    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,
722
                                char *queryfile, char *passin,
723 724
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy)
725 726 727 728 729 730
{
    int ret = 0;
    TS_RESP *response = NULL;
    BIO *query_bio = NULL;
    TS_RESP_CTX *resp_ctx = NULL;

731
    if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL)
732
        goto end;
733
    if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL)
734
        goto end;
735
    if ((resp_ctx = TS_RESP_CTX_new()) == NULL)
736 737 738
        goto end;
    if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
        goto end;
R
Richard Levitte 已提交
739
# ifndef OPENSSL_NO_ENGINE
740 741
    if (!TS_CONF_set_crypto_device(conf, section, engine))
        goto end;
R
Richard Levitte 已提交
742
# endif
743 744 745 746 747 748
    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;
749 750 751 752 753 754 755 756

    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;
    }

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    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;
773
    if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL)
774 775
        goto end;
    ret = 1;
776

777
 end:
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
    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;
}
802 803

static ASN1_INTEGER *next_serial(const char *serialfile)
804 805 806 807 808 809
{
    int ret = 0;
    BIO *in = NULL;
    ASN1_INTEGER *serial = NULL;
    BIGNUM *bn = NULL;

810
    if ((serial = ASN1_INTEGER_new()) == NULL)
811 812
        goto err;

813
    if ((in = BIO_new_file(serialfile, "r")) == NULL) {
814 815 816 817 818 819 820 821 822 823 824 825
        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;
        }
826
        if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL)
827 828 829 830 831
            goto err;
        ASN1_INTEGER_free(serial);
        serial = NULL;
        if (!BN_add_word(bn, 1))
            goto err;
832
        if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL)
833 834 835
            goto err;
    }
    ret = 1;
836

837
 err:
838 839 840 841 842 843 844 845
    if (!ret) {
        ASN1_INTEGER_free(serial);
        serial = NULL;
    }
    BIO_free_all(in);
    BN_free(bn);
    return serial;
}
846 847

static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
848 849 850 851
{
    int ret = 0;
    BIO *out = NULL;

852
    if ((out = BIO_new_file(serialfile, "w")) == NULL)
853 854 855 856 857 858
        goto err;
    if (i2a_ASN1_INTEGER(out, serial) <= 0)
        goto err;
    if (BIO_puts(out, "\n") <= 0)
        goto err;
    ret = 1;
859
 err:
860 861 862 863 864 865
    if (!ret)
        BIO_printf(bio_err, "could not save serial number to %s\n",
                   serialfile);
    BIO_free_all(out);
    return ret;
}
866

867

868 869 870 871 872
/*
 * Verify-related method definitions.
 */

static int verify_command(char *data, char *digest, char *queryfile,
873
                          char *in, int token_in,
F
fbroda 已提交
874 875
                          char *CApath, char *CAfile, char *untrusted,
                          X509_VERIFY_PARAM *vpm)
876 877 878 879 880 881 882
{
    BIO *in_bio = NULL;
    PKCS7 *token = NULL;
    TS_RESP *response = NULL;
    TS_VERIFY_CTX *verify_ctx = NULL;
    int ret = 0;

883
    if ((in_bio = BIO_new_file(in, "rb")) == NULL)
884 885
        goto end;
    if (token_in) {
886
        if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
887 888
            goto end;
    } else {
889
        if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL)
890 891 892
            goto end;
    }

893
    if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
F
fbroda 已提交
894 895
                                        CApath, CAfile, untrusted,
                                        vpm)) == NULL)
896 897
        goto end;

898 899 900
    ret = token_in
        ? TS_RESP_verify_token(verify_ctx, token)
        : TS_RESP_verify_response(verify_ctx, response);
901 902

 end:
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
    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,
920
                                        char *CApath, char *CAfile,
F
fbroda 已提交
921 922
                                        char *untrusted,
                                        X509_VERIFY_PARAM *vpm)
923 924 925 926 927
{
    TS_VERIFY_CTX *ctx = NULL;
    BIO *input = NULL;
    TS_REQ *request = NULL;
    int ret = 0;
R
Rich Salz 已提交
928
    int f = 0;
929 930

    if (data != NULL || digest != NULL) {
931
        if ((ctx = TS_VERIFY_CTX_new()) == NULL)
932
            goto err;
R
Rich Salz 已提交
933
        f = TS_VFY_VERSION | TS_VFY_SIGNER;
934
        if (data != NULL) {
R
Rich Salz 已提交
935 936
            f |= TS_VFY_DATA;
            if (TS_VERIFY_CTX_set_data(ctx, BIO_new_file(data, "rb")) == NULL)
937 938 939
                goto err;
        } else if (digest != NULL) {
            long imprint_len;
940
            unsigned char *hexstr = OPENSSL_hexstr2buf(digest, &imprint_len);
R
Rich Salz 已提交
941 942
            f |= TS_VFY_IMPRINT;
            if (TS_VERIFY_CTX_set_imprint(ctx, hexstr, imprint_len) == NULL) {
943 944 945 946 947 948
                BIO_printf(bio_err, "invalid digest string\n");
                goto err;
            }
        }

    } else if (queryfile != NULL) {
949
        if ((input = BIO_new_file(queryfile, "rb")) == NULL)
950
            goto err;
951
        if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL)
952
            goto err;
953
        if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL)
954 955 956 957 958
            goto err;
    } else
        return NULL;

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

    /* Initialising the X509_STORE object. */
F
fbroda 已提交
962
    if (TS_VERIFY_CTX_set_store(ctx, create_cert_store(CApath, CAfile, vpm))
R
Rich Salz 已提交
963
            == NULL)
964 965 966
        goto err;

    /* Loading untrusted certificates. */
R
Rich Salz 已提交
967 968
    if (untrusted
        && TS_VERIFY_CTS_set_certs(ctx, TS_CONF_load_certs(untrusted)) == NULL)
969 970
        goto err;
    ret = 1;
971

972
 err:
973 974 975 976 977 978 979 980
    if (!ret) {
        TS_VERIFY_CTX_free(ctx);
        ctx = NULL;
    }
    BIO_free_all(input);
    TS_REQ_free(request);
    return ctx;
}
981

F
fbroda 已提交
982
static X509_STORE *create_cert_store(char *CApath, char *CAfile, X509_VERIFY_PARAM *vpm)
983 984 985 986 987 988 989
{
    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);
990
    if (CApath != NULL) {
991 992 993 994 995
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
996
        i = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
997
        if (!i) {
998
            BIO_printf(bio_err, "Error loading directory %s\n", CApath);
999 1000 1001 1002
            goto err;
        }
    }

1003
    if (CAfile != NULL) {
1004 1005 1006 1007 1008
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
1009
        i = X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM);
1010
        if (!i) {
1011
            BIO_printf(bio_err, "Error loading file %s\n", CAfile);
1012 1013 1014
            goto err;
        }
    }
F
fbroda 已提交
1015 1016 1017 1018

    if (vpm != NULL) 
        X509_STORE_set1_param(cert_ctx, vpm);

1019
    return cert_ctx;
1020

1021
 err:
1022 1023 1024
    X509_STORE_free(cert_ctx);
    return NULL;
}
1025

R
Rich Salz 已提交
1026
static int verify_cb(int ok, X509_STORE_CTX *ctx)
1027 1028 1029
{
    return ok;
}
R
Richard Levitte 已提交
1030
#endif