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