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