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