1 /*
2 * Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <openssl/opensslconf.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include "apps.h"
15 #include "progs.h"
16 #include <openssl/bio.h>
17 #include <openssl/err.h>
18 #include <openssl/pem.h>
19 #include <openssl/rand.h>
20 #include <openssl/ts.h>
21 #include <openssl/bn.h>
22
23 /* Request nonce length, in bits (must be a multiple of 8). */
24 #define NONCE_LENGTH 64
25
26 /* Name of config entry that defines the OID file. */
27 #define ENV_OID_FILE "oid_file"
28
29 /* Is |EXACTLY_ONE| of three pointers set? */
30 #define EXACTLY_ONE(a, b, c) \
31 ((a && !b && !c) || (b && !a && !c) || (c && !a && !b))
32
33 static ASN1_OBJECT *txt2obj(const char *oid);
34 static CONF *load_config_file(const char *configfile);
35
36 /* Query related functions. */
37 static int query_command(const char *data, const char *digest,
38 const EVP_MD *md, const char *policy, int no_nonce,
39 int cert, const char *in, const char *out, int text);
40 static TS_REQ *create_query(BIO *data_bio, const char *digest, const EVP_MD *md,
41 const char *policy, int no_nonce, int cert);
42 static int create_digest(BIO *input, const char *digest,
43 const EVP_MD *md, unsigned char **md_value);
44 static ASN1_INTEGER *create_nonce(int bits);
45
46 /* Reply related functions. */
47 static int reply_command(CONF *conf, const char *section, const char *engine,
48 const char *queryfile, const char *passin, const char *inkey,
49 const EVP_MD *md, const char *signer, const char *chain,
50 const char *policy, const char *in, int token_in,
51 const char *out, int token_out, int text);
52 static TS_RESP *read_PKCS7(BIO *in_bio);
53 static TS_RESP *create_response(CONF *conf, const char *section, const char *engine,
54 const char *queryfile, const char *passin,
55 const char *inkey, const EVP_MD *md, const char *signer,
56 const char *chain, const char *policy);
57 static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data);
58 static ASN1_INTEGER *next_serial(const char *serialfile);
59 static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial);
60
61 /* Verify related functions. */
62 static int verify_command(const char *data, const char *digest, const char *queryfile,
63 const char *in, int token_in,
64 const char *CApath, const char *CAfile,
65 const char *CAstore,
66 char *untrusted, X509_VERIFY_PARAM *vpm);
67 static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
68 const char *queryfile,
69 const char *CApath, const char *CAfile,
70 const char *CAstore,
71 char *untrusted,
72 X509_VERIFY_PARAM *vpm);
73 static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
74 const char *CAstore, X509_VERIFY_PARAM *vpm);
75 static int verify_cb(int ok, X509_STORE_CTX *ctx);
76
77 typedef enum OPTION_choice {
78 OPT_COMMON,
79 OPT_ENGINE,
80 OPT_CONFIG,
81 OPT_SECTION,
82 OPT_QUERY,
83 OPT_DATA,
84 OPT_DIGEST,
85 OPT_TSPOLICY,
86 OPT_NO_NONCE,
87 OPT_CERT,
88 OPT_IN,
89 OPT_TOKEN_IN,
90 OPT_OUT,
91 OPT_TOKEN_OUT,
92 OPT_TEXT,
93 OPT_REPLY,
94 OPT_QUERYFILE,
95 OPT_PASSIN,
96 OPT_INKEY,
97 OPT_SIGNER,
98 OPT_CHAIN,
99 OPT_VERIFY,
100 OPT_CAPATH,
101 OPT_CAFILE,
102 OPT_CASTORE,
103 OPT_UNTRUSTED,
104 OPT_MD,
105 OPT_V_ENUM,
106 OPT_R_ENUM,
107 OPT_PROV_ENUM
108 } OPTION_CHOICE;
109
110 const OPTIONS ts_options[] = {
111 OPT_SECTION("General"),
112 { "help", OPT_HELP, '-', "Display this summary" },
113 { "config", OPT_CONFIG, '<', "Configuration file" },
114 { "section", OPT_SECTION, 's', "Section to use within config file" },
115 #ifndef OPENSSL_NO_ENGINE
116 { "engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device" },
117 #endif
118 { "inkey", OPT_INKEY, 's', "File with private key for reply" },
119 { "signer", OPT_SIGNER, 's', "Signer certificate file" },
120 { "chain", OPT_CHAIN, '<', "File with signer CA chain" },
121 { "CAfile", OPT_CAFILE, '<', "File with trusted CA certs" },
122 { "CApath", OPT_CAPATH, '/', "Path to trusted CA files" },
123 { "CAstore", OPT_CASTORE, ':', "URI to trusted CA store" },
124 { "untrusted", OPT_UNTRUSTED, '<', "Extra untrusted certs" },
125 { "token_in", OPT_TOKEN_IN, '-', "Input is a PKCS#7 file" },
126 { "token_out", OPT_TOKEN_OUT, '-', "Output is a PKCS#7 file" },
127 { "passin", OPT_PASSIN, 's', "Input file pass phrase source" },
128 { "", OPT_MD, '-', "Any supported digest" },
129
130 OPT_SECTION("Query"),
131 { "query", OPT_QUERY, '-', "Generate a TS query" },
132 { "data", OPT_DATA, '<', "File to hash" },
133 { "digest", OPT_DIGEST, 's', "Digest (as a hex string)" },
134 { "queryfile", OPT_QUERYFILE, '<', "File containing a TS query" },
135 { "cert", OPT_CERT, '-', "Put cert request into query" },
136 { "in", OPT_IN, '<', "Input file" },
137
138 OPT_SECTION("Verify"),
139 { "verify", OPT_VERIFY, '-', "Verify a TS response" },
140 { "reply", OPT_REPLY, '-', "Generate a TS reply" },
141 { "tspolicy", OPT_TSPOLICY, 's', "Policy OID to use" },
142 { "no_nonce", OPT_NO_NONCE, '-', "Do not include a nonce" },
143 { "out", OPT_OUT, '>', "Output file" },
144 { "text", OPT_TEXT, '-', "Output text (not DER)" },
145
146 OPT_R_OPTIONS,
147 OPT_V_OPTIONS,
148 OPT_PROV_OPTIONS,
149 { NULL }
150 };
151
152 /*
153 * This command is so complex, special help is needed.
154 */
155 static char *opt_helplist[] = {
156 "",
157 "Typical uses:",
158 " openssl ts -query [-rand file...] [-config file] [-data file]",
159 " [-digest hexstring] [-tspolicy oid] [-no_nonce] [-cert]",
160 " [-in file] [-out file] [-text]",
161 "",
162 " openssl ts -reply [-config file] [-section tsa_section]",
163 " [-queryfile file] [-passin password]",
164 " [-signer tsa_cert.pem] [-inkey private_key.pem]",
165 " [-chain certs_file.pem] [-tspolicy oid]",
166 " [-in file] [-token_in] [-out file] [-token_out]",
167 #ifndef OPENSSL_NO_ENGINE
168 " [-text] [-engine id]",
169 #else
170 " [-text]",
171 #endif
172 "",
173 " openssl ts -verify -CApath dir -CAfile root-cert.pem -CAstore uri",
174 " -untrusted extra-certs.pem [-data file] [-digest hexstring]",
175 " [-queryfile request.tsq] -in response.tsr [-token_in] ...",
176 NULL,
177 };
178
ts_main(int argc,char ** argv)179 int ts_main(int argc, char **argv)
180 {
181 CONF *conf = NULL;
182 const char *CAfile = NULL, *prog;
183 char *untrusted = NULL;
184 const char *configfile = default_config_file, *engine = NULL;
185 const char *section = NULL, *digestname = NULL;
186 char **helpp;
187 char *password = NULL;
188 char *data = NULL, *digest = NULL, *policy = NULL;
189 char *in = NULL, *out = NULL, *queryfile = NULL, *passin = NULL;
190 char *inkey = NULL, *signer = NULL, *chain = NULL, *CApath = NULL;
191 char *CAstore = NULL;
192 EVP_MD *md = NULL;
193 OPTION_CHOICE o, mode = OPT_ERR;
194 int ret = 1, no_nonce = 0, cert = 0, text = 0;
195 int vpmtouched = 0;
196 X509_VERIFY_PARAM *vpm = NULL;
197 /* Input is ContentInfo instead of TimeStampResp. */
198 int token_in = 0;
199 /* Output is ContentInfo instead of TimeStampResp. */
200 int token_out = 0;
201
202 if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
203 goto end;
204
205 opt_set_unknown_name("digest");
206 prog = opt_init(argc, argv, ts_options);
207 while ((o = opt_next()) != OPT_EOF) {
208 switch (o) {
209 case OPT_EOF:
210 case OPT_ERR:
211 opthelp:
212 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
213 goto end;
214 case OPT_HELP:
215 opt_help(ts_options);
216 for (helpp = opt_helplist; *helpp; ++helpp)
217 BIO_printf(bio_err, "%s\n", *helpp);
218 ret = 0;
219 goto end;
220 case OPT_CONFIG:
221 configfile = opt_arg();
222 break;
223 case OPT_SECTION:
224 section = opt_arg();
225 break;
226 case OPT_QUERY:
227 case OPT_REPLY:
228 case OPT_VERIFY:
229 if (mode != OPT_ERR) {
230 BIO_printf(bio_err, "%s: Must give only one of -query, -reply, or -verify\n", prog);
231 goto opthelp;
232 }
233 mode = o;
234 break;
235 case OPT_DATA:
236 data = opt_arg();
237 break;
238 case OPT_DIGEST:
239 digest = opt_arg();
240 break;
241 case OPT_R_CASES:
242 if (!opt_rand(o))
243 goto end;
244 break;
245 case OPT_PROV_CASES:
246 if (!opt_provider(o))
247 goto end;
248 break;
249 case OPT_TSPOLICY:
250 policy = opt_arg();
251 break;
252 case OPT_NO_NONCE:
253 no_nonce = 1;
254 break;
255 case OPT_CERT:
256 cert = 1;
257 break;
258 case OPT_IN:
259 in = opt_arg();
260 break;
261 case OPT_TOKEN_IN:
262 token_in = 1;
263 break;
264 case OPT_OUT:
265 out = opt_arg();
266 break;
267 case OPT_TOKEN_OUT:
268 token_out = 1;
269 break;
270 case OPT_TEXT:
271 text = 1;
272 break;
273 case OPT_QUERYFILE:
274 queryfile = opt_arg();
275 break;
276 case OPT_PASSIN:
277 passin = opt_arg();
278 break;
279 case OPT_INKEY:
280 inkey = opt_arg();
281 break;
282 case OPT_SIGNER:
283 signer = opt_arg();
284 break;
285 case OPT_CHAIN:
286 chain = opt_arg();
287 break;
288 case OPT_CAPATH:
289 CApath = opt_arg();
290 break;
291 case OPT_CAFILE:
292 CAfile = opt_arg();
293 break;
294 case OPT_CASTORE:
295 CAstore = opt_arg();
296 break;
297 case OPT_UNTRUSTED:
298 untrusted = opt_arg();
299 break;
300 case OPT_ENGINE:
301 engine = opt_arg();
302 break;
303 case OPT_MD:
304 digestname = opt_unknown();
305 break;
306 case OPT_V_CASES:
307 if (!opt_verify(o, vpm))
308 goto end;
309 vpmtouched++;
310 break;
311 }
312 }
313
314 /* No extra arguments. */
315 if (!opt_check_rest_arg(NULL))
316 goto opthelp;
317 if (mode == OPT_ERR) {
318 BIO_printf(bio_err, "%s: Must give one of -query, -reply, or -verify\n", prog);
319 goto opthelp;
320 }
321
322 if (!app_RAND_load())
323 goto end;
324
325 if (!opt_md(digestname, &md))
326 goto opthelp;
327 if (mode == OPT_REPLY && passin && !app_passwd(passin, NULL, &password, NULL)) {
328 BIO_printf(bio_err, "Error getting password.\n");
329 goto end;
330 }
331
332 if ((conf = load_config_file(configfile)) == NULL)
333 goto end;
334 if (configfile != default_config_file && !app_load_modules(conf))
335 goto end;
336
337 /* Check parameter consistency and execute the appropriate function. */
338 if (mode == OPT_QUERY) {
339 if (vpmtouched)
340 goto opthelp;
341 if ((data != NULL) && (digest != NULL))
342 goto opthelp;
343 ret = !query_command(data, digest, md, policy, no_nonce, cert,
344 in, out, text);
345 } else if (mode == OPT_REPLY) {
346 if (vpmtouched)
347 goto opthelp;
348 if ((in != NULL) && (queryfile != NULL))
349 goto opthelp;
350 if (in == NULL) {
351 if ((conf == NULL) || (token_in != 0))
352 goto opthelp;
353 }
354 ret = !reply_command(conf, section, engine, queryfile,
355 password, inkey, md, signer, chain, policy,
356 in, token_in, out, token_out, text);
357
358 } else if (mode == OPT_VERIFY) {
359 if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
360 goto opthelp;
361 ret = !verify_command(data, digest, queryfile, in, token_in,
362 CApath, CAfile, CAstore, untrusted,
363 vpmtouched ? vpm : NULL);
364 } else {
365 goto opthelp;
366 }
367
368 end:
369 X509_VERIFY_PARAM_free(vpm);
370 EVP_MD_free(md);
371 NCONF_free(conf);
372 OPENSSL_free(password);
373 return ret;
374 }
375
376 /*
377 * Configuration file-related function definitions.
378 */
379
txt2obj(const char * oid)380 static ASN1_OBJECT *txt2obj(const char *oid)
381 {
382 ASN1_OBJECT *oid_obj = NULL;
383
384 if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL)
385 BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
386
387 return oid_obj;
388 }
389
load_config_file(const char * configfile)390 static CONF *load_config_file(const char *configfile)
391 {
392 CONF *conf = app_load_config(configfile);
393
394 if (conf != NULL) {
395 const char *p;
396
397 BIO_printf(bio_err, "Using configuration from %s\n", configfile);
398 p = app_conf_try_string(conf, NULL, ENV_OID_FILE);
399 if (p != NULL) {
400 BIO *oid_bio = BIO_new_file(p, "r");
401 if (!oid_bio)
402 ERR_print_errors(bio_err);
403 else {
404 OBJ_create_objects(oid_bio);
405 BIO_free_all(oid_bio);
406 }
407 }
408 if (!add_oid_section(conf))
409 ERR_print_errors(bio_err);
410 }
411 return conf;
412 }
413
414 /*
415 * Query-related method definitions.
416 */
query_command(const char * data,const char * digest,const EVP_MD * md,const char * policy,int no_nonce,int cert,const char * in,const char * out,int text)417 static int query_command(const char *data, const char *digest, const EVP_MD *md,
418 const char *policy, int no_nonce,
419 int cert, const char *in, const char *out, int text)
420 {
421 int ret = 0;
422 TS_REQ *query = NULL;
423 BIO *in_bio = NULL;
424 BIO *data_bio = NULL;
425 BIO *out_bio = NULL;
426
427 /* Build query object. */
428 if (in != NULL) {
429 if ((in_bio = bio_open_default(in, 'r', FORMAT_ASN1)) == NULL)
430 goto end;
431 query = d2i_TS_REQ_bio(in_bio, NULL);
432 } else {
433 if (digest == NULL
434 && (data_bio = bio_open_default(data, 'r', FORMAT_ASN1)) == NULL)
435 goto end;
436 query = create_query(data_bio, digest, md, policy, no_nonce, cert);
437 }
438 if (query == NULL)
439 goto end;
440
441 if (text) {
442 if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
443 goto end;
444 if (!TS_REQ_print_bio(out_bio, query))
445 goto end;
446 } else {
447 if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
448 goto end;
449 if (!i2d_TS_REQ_bio(out_bio, query))
450 goto end;
451 }
452
453 ret = 1;
454
455 end:
456 ERR_print_errors(bio_err);
457 BIO_free_all(in_bio);
458 BIO_free_all(data_bio);
459 BIO_free_all(out_bio);
460 TS_REQ_free(query);
461 return ret;
462 }
463
create_query(BIO * data_bio,const char * digest,const EVP_MD * md,const char * policy,int no_nonce,int cert)464 static TS_REQ *create_query(BIO *data_bio, const char *digest, const EVP_MD *md,
465 const char *policy, int no_nonce, int cert)
466 {
467 int ret = 0;
468 TS_REQ *ts_req = NULL;
469 int len;
470 TS_MSG_IMPRINT *msg_imprint = NULL;
471 X509_ALGOR *algo = NULL;
472 unsigned char *data = NULL;
473 ASN1_OBJECT *policy_obj = NULL;
474 ASN1_INTEGER *nonce_asn1 = NULL;
475
476 if (md == NULL && (md = EVP_get_digestbyname("sha256")) == NULL)
477 goto err;
478 if ((ts_req = TS_REQ_new()) == NULL)
479 goto err;
480 if (!TS_REQ_set_version(ts_req, 1))
481 goto err;
482 if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
483 goto err;
484 if ((algo = X509_ALGOR_new()) == NULL)
485 goto err;
486 if ((algo->algorithm = OBJ_nid2obj(EVP_MD_get_type(md))) == NULL)
487 goto err;
488 if ((algo->parameter = ASN1_TYPE_new()) == NULL)
489 goto err;
490 algo->parameter->type = V_ASN1_NULL;
491 if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
492 goto err;
493 if ((len = create_digest(data_bio, digest, md, &data)) == 0)
494 goto err;
495 if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len))
496 goto err;
497 if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint))
498 goto err;
499 if (policy && (policy_obj = txt2obj(policy)) == NULL)
500 goto err;
501 if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
502 goto err;
503
504 /* Setting nonce if requested. */
505 if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL)
506 goto err;
507 if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
508 goto err;
509 if (!TS_REQ_set_cert_req(ts_req, cert))
510 goto err;
511
512 ret = 1;
513 err:
514 if (!ret) {
515 TS_REQ_free(ts_req);
516 ts_req = NULL;
517 BIO_printf(bio_err, "could not create query\n");
518 ERR_print_errors(bio_err);
519 }
520 TS_MSG_IMPRINT_free(msg_imprint);
521 X509_ALGOR_free(algo);
522 OPENSSL_free(data);
523 ASN1_OBJECT_free(policy_obj);
524 ASN1_INTEGER_free(nonce_asn1);
525 return ts_req;
526 }
527
create_digest(BIO * input,const char * digest,const EVP_MD * md,unsigned char ** md_value)528 static int create_digest(BIO *input, const char *digest, const EVP_MD *md,
529 unsigned char **md_value)
530 {
531 int md_value_len;
532 int rv = 0;
533 EVP_MD_CTX *md_ctx = NULL;
534
535 md_value_len = EVP_MD_get_size(md);
536 if (md_value_len <= 0)
537 return 0;
538
539 if (input != NULL) {
540 unsigned char buffer[4096];
541 int length;
542
543 md_ctx = EVP_MD_CTX_new();
544 if (md_ctx == NULL)
545 return 0;
546 *md_value = app_malloc(md_value_len, "digest buffer");
547 if (!EVP_DigestInit(md_ctx, md))
548 goto err;
549 while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
550 if (!EVP_DigestUpdate(md_ctx, buffer, length))
551 goto err;
552 }
553 if (!EVP_DigestFinal(md_ctx, *md_value, NULL))
554 goto err;
555 md_value_len = EVP_MD_get_size(md);
556 } else {
557 long digest_len;
558
559 *md_value = OPENSSL_hexstr2buf(digest, &digest_len);
560 if (*md_value == NULL || md_value_len != digest_len) {
561 BIO_printf(bio_err, "bad digest, %d bytes "
562 "must be specified\n",
563 md_value_len);
564 goto err;
565 }
566 }
567 rv = md_value_len;
568 err:
569 if (rv <= 0) {
570 OPENSSL_free(*md_value);
571 *md_value = NULL;
572 rv = 0;
573 }
574 EVP_MD_CTX_free(md_ctx);
575 return rv;
576 }
577
create_nonce(int bits)578 static ASN1_INTEGER *create_nonce(int bits)
579 {
580 unsigned char buf[20];
581 ASN1_INTEGER *nonce = NULL;
582 int len = (bits - 1) / 8 + 1;
583 int i;
584
585 if (len > (int)sizeof(buf))
586 goto err;
587 if (RAND_bytes(buf, len) <= 0)
588 goto err;
589
590 /* Find the first non-zero byte and creating ASN1_INTEGER object. */
591 for (i = 0; i < len && !buf[i]; ++i)
592 continue;
593 if ((nonce = ASN1_INTEGER_new()) == NULL)
594 goto err;
595 OPENSSL_free(nonce->data);
596 nonce->length = len - i;
597 nonce->data = app_malloc(nonce->length + 1, "nonce buffer");
598 memcpy(nonce->data, buf + i, nonce->length);
599 return nonce;
600
601 err:
602 BIO_printf(bio_err, "could not create nonce\n");
603 ASN1_INTEGER_free(nonce);
604 return NULL;
605 }
606
607 /*
608 * Reply-related method definitions.
609 */
610
reply_command(CONF * conf,const char * section,const char * engine,const char * queryfile,const char * passin,const char * inkey,const EVP_MD * md,const char * signer,const char * chain,const char * policy,const char * in,int token_in,const char * out,int token_out,int text)611 static int reply_command(CONF *conf, const char *section, const char *engine,
612 const char *queryfile, const char *passin, const char *inkey,
613 const EVP_MD *md, const char *signer, const char *chain,
614 const char *policy, const char *in, int token_in,
615 const char *out, int token_out, int text)
616 {
617 int ret = 0;
618 TS_RESP *response = NULL;
619 BIO *in_bio = NULL;
620 BIO *query_bio = NULL;
621 BIO *inkey_bio = NULL;
622 BIO *signer_bio = NULL;
623 BIO *out_bio = NULL;
624
625 if (in != NULL) {
626 if ((in_bio = BIO_new_file(in, "rb")) == NULL)
627 goto end;
628 if (token_in) {
629 response = read_PKCS7(in_bio);
630 } else {
631 response = d2i_TS_RESP_bio(in_bio, NULL);
632 }
633 } else {
634 response = create_response(conf, section, engine, queryfile,
635 passin, inkey, md, signer, chain, policy);
636 if (response != NULL)
637 BIO_printf(bio_err, "Response has been generated.\n");
638 else
639 BIO_printf(bio_err, "Response is not generated.\n");
640 }
641 if (response == NULL)
642 goto end;
643
644 /* Write response. */
645 if (text) {
646 if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
647 goto end;
648 if (token_out) {
649 TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response);
650 if (!TS_TST_INFO_print_bio(out_bio, tst_info))
651 goto end;
652 } else {
653 if (!TS_RESP_print_bio(out_bio, response))
654 goto end;
655 }
656 } else {
657 if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
658 goto end;
659 if (token_out) {
660 PKCS7 *token = TS_RESP_get_token(response);
661 if (!i2d_PKCS7_bio(out_bio, token))
662 goto end;
663 } else {
664 if (!i2d_TS_RESP_bio(out_bio, response))
665 goto end;
666 }
667 }
668
669 ret = 1;
670
671 end:
672 ERR_print_errors(bio_err);
673 BIO_free_all(in_bio);
674 BIO_free_all(query_bio);
675 BIO_free_all(inkey_bio);
676 BIO_free_all(signer_bio);
677 BIO_free_all(out_bio);
678 TS_RESP_free(response);
679 return ret;
680 }
681
682 /* Reads a PKCS7 token and adds default 'granted' status info to it. */
read_PKCS7(BIO * in_bio)683 static TS_RESP *read_PKCS7(BIO *in_bio)
684 {
685 int ret = 0;
686 PKCS7 *token = NULL;
687 TS_TST_INFO *tst_info = NULL;
688 TS_RESP *resp = NULL;
689 TS_STATUS_INFO *si = NULL;
690
691 if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
692 goto end;
693 if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL)
694 goto end;
695 if ((resp = TS_RESP_new()) == NULL)
696 goto end;
697 if ((si = TS_STATUS_INFO_new()) == NULL)
698 goto end;
699 if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED))
700 goto end;
701 if (!TS_RESP_set_status_info(resp, si))
702 goto end;
703 TS_RESP_set_tst_info(resp, token, tst_info);
704 token = NULL; /* Ownership is lost. */
705 tst_info = NULL; /* Ownership is lost. */
706 ret = 1;
707
708 end:
709 PKCS7_free(token);
710 TS_TST_INFO_free(tst_info);
711 if (!ret) {
712 TS_RESP_free(resp);
713 resp = NULL;
714 }
715 TS_STATUS_INFO_free(si);
716 return resp;
717 }
718
create_response(CONF * conf,const char * section,const char * engine,const char * queryfile,const char * passin,const char * inkey,const EVP_MD * md,const char * signer,const char * chain,const char * policy)719 static TS_RESP *create_response(CONF *conf, const char *section, const char *engine,
720 const char *queryfile, const char *passin,
721 const char *inkey, const EVP_MD *md, const char *signer,
722 const char *chain, const char *policy)
723 {
724 int ret = 0;
725 TS_RESP *response = NULL;
726 BIO *query_bio = NULL;
727 TS_RESP_CTX *resp_ctx = NULL;
728
729 if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL)
730 goto end;
731 if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL)
732 goto end;
733 if ((resp_ctx = TS_RESP_CTX_new()) == NULL)
734 goto end;
735 if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
736 goto end;
737 #ifndef OPENSSL_NO_ENGINE
738 if (!TS_CONF_set_crypto_device(conf, section, engine))
739 goto end;
740 #endif
741 if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx))
742 goto end;
743 if (!TS_CONF_set_certs(conf, section, chain, resp_ctx))
744 goto end;
745 if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx))
746 goto end;
747
748 if (md) {
749 if (!TS_RESP_CTX_set_signer_digest(resp_ctx, md))
750 goto end;
751 } else if (!TS_CONF_set_signer_digest(conf, section, NULL, resp_ctx)) {
752 goto end;
753 }
754
755 if (!TS_CONF_set_ess_cert_id_digest(conf, section, resp_ctx))
756 goto end;
757 if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx))
758 goto end;
759 if (!TS_CONF_set_policies(conf, section, resp_ctx))
760 goto end;
761 if (!TS_CONF_set_digests(conf, section, resp_ctx))
762 goto end;
763 if (!TS_CONF_set_accuracy(conf, section, resp_ctx))
764 goto end;
765 if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx))
766 goto end;
767 if (!TS_CONF_set_ordering(conf, section, resp_ctx))
768 goto end;
769 if (!TS_CONF_set_tsa_name(conf, section, resp_ctx))
770 goto end;
771 if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx))
772 goto end;
773 if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL)
774 goto end;
775 ret = 1;
776
777 end:
778 if (!ret) {
779 TS_RESP_free(response);
780 response = NULL;
781 }
782 TS_RESP_CTX_free(resp_ctx);
783 BIO_free_all(query_bio);
784 return response;
785 }
786
serial_cb(TS_RESP_CTX * ctx,void * data)787 static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data)
788 {
789 const char *serial_file = (const char *)data;
790 ASN1_INTEGER *serial = next_serial(serial_file);
791
792 if (serial == NULL) {
793 TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
794 "Error during serial number "
795 "generation.");
796 TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE);
797 } else {
798 save_ts_serial(serial_file, serial);
799 }
800
801 return serial;
802 }
803
next_serial(const char * serialfile)804 static ASN1_INTEGER *next_serial(const char *serialfile)
805 {
806 int ret = 0;
807 BIO *in = NULL;
808 ASN1_INTEGER *serial = NULL;
809 BIGNUM *bn = NULL;
810
811 if ((serial = ASN1_INTEGER_new()) == NULL)
812 goto err;
813
814 if ((in = BIO_new_file(serialfile, "r")) == NULL) {
815 ERR_clear_error();
816 BIO_printf(bio_err, "Warning: could not open file %s for "
817 "reading, using serial number: 1\n",
818 serialfile);
819 if (!ASN1_INTEGER_set(serial, 1))
820 goto err;
821 } else {
822 char buf[1024];
823 if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) {
824 BIO_printf(bio_err, "unable to load number from %s\n",
825 serialfile);
826 goto err;
827 }
828 if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL)
829 goto err;
830 ASN1_INTEGER_free(serial);
831 serial = NULL;
832 if (!BN_add_word(bn, 1))
833 goto err;
834 if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL)
835 goto err;
836 }
837 ret = 1;
838
839 err:
840 if (!ret) {
841 ASN1_INTEGER_free(serial);
842 serial = NULL;
843 }
844 BIO_free_all(in);
845 BN_free(bn);
846 return serial;
847 }
848
save_ts_serial(const char * serialfile,ASN1_INTEGER * serial)849 static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
850 {
851 int ret = 0;
852 BIO *out = NULL;
853
854 if ((out = BIO_new_file(serialfile, "w")) == NULL)
855 goto err;
856 if (i2a_ASN1_INTEGER(out, serial) <= 0)
857 goto err;
858 if (BIO_puts(out, "\n") <= 0)
859 goto err;
860 ret = 1;
861 err:
862 if (!ret)
863 BIO_printf(bio_err, "could not save serial number to %s\n",
864 serialfile);
865 BIO_free_all(out);
866 return ret;
867 }
868
869 /*
870 * Verify-related method definitions.
871 */
872
verify_command(const char * data,const char * digest,const char * queryfile,const char * in,int token_in,const char * CApath,const char * CAfile,const char * CAstore,char * untrusted,X509_VERIFY_PARAM * vpm)873 static int verify_command(const char *data, const char *digest, const char *queryfile,
874 const char *in, int token_in,
875 const char *CApath, const char *CAfile,
876 const char *CAstore, char *untrusted,
877 X509_VERIFY_PARAM *vpm)
878 {
879 BIO *in_bio = NULL;
880 PKCS7 *token = NULL;
881 TS_RESP *response = NULL;
882 TS_VERIFY_CTX *verify_ctx = NULL;
883 int ret = 0;
884
885 if ((in_bio = BIO_new_file(in, "rb")) == NULL)
886 goto end;
887 if (token_in) {
888 if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
889 goto end;
890 } else {
891 if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL)
892 goto end;
893 }
894
895 if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
896 CApath, CAfile, CAstore, untrusted,
897 vpm))
898 == NULL)
899 goto end;
900
901 ret = token_in
902 ? TS_RESP_verify_token(verify_ctx, token)
903 : TS_RESP_verify_response(verify_ctx, response);
904
905 end:
906 printf("Verification: ");
907 if (ret)
908 printf("OK\n");
909 else {
910 printf("FAILED\n");
911 ERR_print_errors(bio_err);
912 }
913
914 BIO_free_all(in_bio);
915 PKCS7_free(token);
916 TS_RESP_free(response);
917 TS_VERIFY_CTX_free(verify_ctx);
918 return ret;
919 }
920
create_verify_ctx(const char * data,const char * digest,const char * queryfile,const char * CApath,const char * CAfile,const char * CAstore,char * untrusted,X509_VERIFY_PARAM * vpm)921 static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
922 const char *queryfile,
923 const char *CApath, const char *CAfile,
924 const char *CAstore,
925 char *untrusted,
926 X509_VERIFY_PARAM *vpm)
927 {
928 TS_VERIFY_CTX *ctx = NULL;
929 STACK_OF(X509) *certs;
930 BIO *input = NULL;
931 TS_REQ *request = NULL;
932 int ret = 0;
933 int f = 0;
934
935 if (data != NULL || digest != NULL) {
936 if ((ctx = TS_VERIFY_CTX_new()) == NULL)
937 goto err;
938 f = TS_VFY_VERSION | TS_VFY_SIGNER;
939 if (data != NULL) {
940 BIO *out = NULL;
941
942 f |= TS_VFY_DATA;
943 if ((out = BIO_new_file(data, "rb")) == NULL)
944 goto err;
945 if (!TS_VERIFY_CTX_set0_data(ctx, out)) {
946 BIO_free_all(out);
947 goto err;
948 }
949 } else if (digest != NULL) {
950 long imprint_len;
951 unsigned char *hexstr = OPENSSL_hexstr2buf(digest, &imprint_len);
952 f |= TS_VFY_IMPRINT;
953 if (!TS_VERIFY_CTX_set0_imprint(ctx, hexstr, imprint_len)) {
954 BIO_printf(bio_err, "invalid digest string\n");
955 goto err;
956 }
957 }
958
959 } else if (queryfile != NULL) {
960 if ((input = BIO_new_file(queryfile, "rb")) == NULL)
961 goto err;
962 if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL)
963 goto err;
964 if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL)
965 goto err;
966 } else {
967 return NULL;
968 }
969
970 /* Add the signature verification flag and arguments. */
971 TS_VERIFY_CTX_add_flags(ctx, f | TS_VFY_SIGNATURE);
972
973 /* Initialising the X509_STORE object. */
974 if (!TS_VERIFY_CTX_set0_store(ctx, create_cert_store(CApath, CAfile, CAstore, vpm)))
975 goto err;
976
977 /* Loading any extra untrusted certificates. */
978 if (untrusted != NULL) {
979 certs = load_certs_multifile(untrusted, NULL, "extra untrusted certs",
980 vpm);
981 if (certs == NULL || !TS_VERIFY_CTX_set0_certs(ctx, certs))
982 goto err;
983 }
984 ret = 1;
985
986 err:
987 if (!ret) {
988 TS_VERIFY_CTX_free(ctx);
989 ctx = NULL;
990 }
991 BIO_free_all(input);
992 TS_REQ_free(request);
993 return ctx;
994 }
995
create_cert_store(const char * CApath,const char * CAfile,const char * CAstore,X509_VERIFY_PARAM * vpm)996 static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
997 const char *CAstore, X509_VERIFY_PARAM *vpm)
998 {
999 X509_STORE *cert_ctx = NULL;
1000 X509_LOOKUP *lookup = NULL;
1001 OSSL_LIB_CTX *libctx = app_get0_libctx();
1002 const char *propq = app_get0_propq();
1003
1004 cert_ctx = X509_STORE_new();
1005 if (cert_ctx == NULL) {
1006 BIO_printf(bio_err, "memory allocation failure\n");
1007 return NULL;
1008 }
1009 X509_STORE_set_verify_cb(cert_ctx, verify_cb);
1010 if (CApath != NULL) {
1011 lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
1012 if (lookup == NULL) {
1013 BIO_printf(bio_err, "memory allocation failure\n");
1014 goto err;
1015 }
1016 if (X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM) <= 0) {
1017 BIO_printf(bio_err, "Error loading directory %s\n", CApath);
1018 goto err;
1019 }
1020 }
1021
1022 if (CAfile != NULL) {
1023 lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
1024 if (lookup == NULL) {
1025 BIO_printf(bio_err, "memory allocation failure\n");
1026 goto err;
1027 }
1028 if (X509_LOOKUP_load_file_ex(lookup, CAfile, X509_FILETYPE_PEM, libctx,
1029 propq)
1030 <= 0) {
1031 BIO_printf(bio_err, "Error loading file %s\n", CAfile);
1032 goto err;
1033 }
1034 }
1035
1036 if (CAstore != NULL) {
1037 lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_store());
1038 if (lookup == NULL) {
1039 BIO_printf(bio_err, "memory allocation failure\n");
1040 goto err;
1041 }
1042 if (X509_LOOKUP_add_store_ex(lookup, CAstore, libctx, propq) <= 0) {
1043 BIO_printf(bio_err, "Error loading store URI %s\n", CAstore);
1044 goto err;
1045 }
1046 }
1047
1048 if (vpm != NULL)
1049 X509_STORE_set1_param(cert_ctx, vpm);
1050
1051 return cert_ctx;
1052
1053 err:
1054 X509_STORE_free(cert_ctx);
1055 return NULL;
1056 }
1057
verify_cb(int ok,X509_STORE_CTX * ctx)1058 static int verify_cb(int ok, X509_STORE_CTX *ctx)
1059 {
1060 return ok;
1061 }
1062