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