diff options
author | cvs2svn <admin@example.com> | 2025-04-14 17:32:06 +0000 |
---|---|---|
committer | cvs2svn <admin@example.com> | 2025-04-14 17:32:06 +0000 |
commit | eb8dd9dca1228af0cd132f515509051ecfabf6f6 (patch) | |
tree | edb6da6af7e865d488dc1a29309f1e1ec226e603 /src/usr.bin/openssl/ocsp.c | |
parent | 247f0352e0ed72a4f476db9dc91f4d982bc83eb2 (diff) | |
download | openbsd-tb_20250414.tar.gz openbsd-tb_20250414.tar.bz2 openbsd-tb_20250414.zip |
This commit was manufactured by cvs2git to create tag 'tb_20250414'.tb_20250414
Diffstat (limited to 'src/usr.bin/openssl/ocsp.c')
-rw-r--r-- | src/usr.bin/openssl/ocsp.c | 1584 |
1 files changed, 0 insertions, 1584 deletions
diff --git a/src/usr.bin/openssl/ocsp.c b/src/usr.bin/openssl/ocsp.c deleted file mode 100644 index d35940a7ae..0000000000 --- a/src/usr.bin/openssl/ocsp.c +++ /dev/null | |||
@@ -1,1584 +0,0 @@ | |||
1 | /* $OpenBSD: ocsp.c,v 1.26 2024/08/31 18:39:25 tb Exp $ */ | ||
2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | ||
3 | * project 2000. | ||
4 | */ | ||
5 | /* ==================================================================== | ||
6 | * Copyright (c) 1999 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 | #ifndef OPENSSL_NO_OCSP | ||
59 | |||
60 | #include <sys/types.h> | ||
61 | |||
62 | #include <stdio.h> | ||
63 | #include <stdlib.h> | ||
64 | #include <limits.h> | ||
65 | #include <string.h> | ||
66 | #include <poll.h> | ||
67 | #include <time.h> | ||
68 | |||
69 | /* Needs to be included before the openssl headers! */ | ||
70 | #include "apps.h" | ||
71 | |||
72 | #include <openssl/bn.h> | ||
73 | #include <openssl/conf.h> | ||
74 | #include <openssl/crypto.h> | ||
75 | #include <openssl/err.h> | ||
76 | #include <openssl/evp.h> | ||
77 | #include <openssl/ssl.h> | ||
78 | #include <openssl/x509v3.h> | ||
79 | |||
80 | /* Maximum leeway in validity period: default 5 minutes */ | ||
81 | #define MAX_VALIDITY_PERIOD (5 * 60) | ||
82 | |||
83 | static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, | ||
84 | const EVP_MD *cert_id_md, X509 *issuer, STACK_OF(OCSP_CERTID) *ids); | ||
85 | static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, | ||
86 | const EVP_MD *cert_id_md, X509 *issuer, STACK_OF(OCSP_CERTID) *ids); | ||
87 | static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, | ||
88 | STACK_OF(OPENSSL_STRING) *names, STACK_OF(OCSP_CERTID) *ids, long nsec, | ||
89 | long maxage); | ||
90 | |||
91 | static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, | ||
92 | CA_DB *db, X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, | ||
93 | unsigned long flags, int nmin, int ndays); | ||
94 | |||
95 | static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser); | ||
96 | static BIO *init_responder(char *port); | ||
97 | static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, | ||
98 | char *port); | ||
99 | static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); | ||
100 | static OCSP_RESPONSE *query_responder(BIO *err, BIO *cbio, char *path, | ||
101 | STACK_OF(CONF_VALUE) *headers, const char *host, OCSP_REQUEST *req, | ||
102 | int req_timeout); | ||
103 | |||
104 | static struct { | ||
105 | int accept_count; | ||
106 | int add_nonce; | ||
107 | char *CAfile; | ||
108 | char *CApath; | ||
109 | X509 *cert; | ||
110 | const EVP_MD *cert_id_md; | ||
111 | STACK_OF(CONF_VALUE) *headers; | ||
112 | char *host; | ||
113 | STACK_OF(OCSP_CERTID) *ids; | ||
114 | int ignore_err; | ||
115 | X509 *issuer; | ||
116 | char *keyfile; | ||
117 | long maxage; | ||
118 | int ndays; | ||
119 | int nmin; | ||
120 | int no_usage; | ||
121 | int noverify; | ||
122 | long nsec; | ||
123 | char *outfile; | ||
124 | char *path; | ||
125 | char *port; | ||
126 | char *rca_filename; | ||
127 | char *rcertfile; | ||
128 | OCSP_REQUEST *req; | ||
129 | int req_text; | ||
130 | int req_timeout; | ||
131 | char *reqin; | ||
132 | STACK_OF(OPENSSL_STRING) *reqnames; | ||
133 | char *reqout; | ||
134 | int resp_text; | ||
135 | char *respin; | ||
136 | char *respout; | ||
137 | unsigned long rflags; | ||
138 | char *ridx_filename; | ||
139 | char *rkeyfile; | ||
140 | char *rsignfile; | ||
141 | char *sign_certfile; | ||
142 | unsigned long sign_flags; | ||
143 | char *signfile; | ||
144 | int use_ssl; | ||
145 | char *verify_certfile; | ||
146 | unsigned long verify_flags; | ||
147 | } cfg; | ||
148 | |||
149 | static int | ||
150 | ocsp_opt_cert(char *arg) | ||
151 | { | ||
152 | X509_free(cfg.cert); | ||
153 | cfg.cert = load_cert(bio_err, arg, FORMAT_PEM, NULL, | ||
154 | "certificate"); | ||
155 | if (cfg.cert == NULL) { | ||
156 | cfg.no_usage = 1; | ||
157 | return (1); | ||
158 | } | ||
159 | if (cfg.cert_id_md == NULL) | ||
160 | cfg.cert_id_md = EVP_sha1(); | ||
161 | if (!add_ocsp_cert(&cfg.req, cfg.cert, | ||
162 | cfg.cert_id_md, cfg.issuer, cfg.ids)) { | ||
163 | cfg.no_usage = 1; | ||
164 | return (1); | ||
165 | } | ||
166 | if (!sk_OPENSSL_STRING_push(cfg.reqnames, arg)) { | ||
167 | cfg.no_usage = 1; | ||
168 | return (1); | ||
169 | } | ||
170 | return (0); | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | ocsp_opt_cert_id_md(int argc, char **argv, int *argsused) | ||
175 | { | ||
176 | char *name = argv[0]; | ||
177 | |||
178 | if (*name++ != '-') | ||
179 | return (1); | ||
180 | |||
181 | if ((cfg.cert_id_md = EVP_get_digestbyname(name)) == NULL) | ||
182 | return (1); | ||
183 | |||
184 | *argsused = 1; | ||
185 | return (0); | ||
186 | } | ||
187 | |||
188 | static int | ||
189 | x509v3_add_value(const char *name, const char *value, | ||
190 | STACK_OF(CONF_VALUE) **out_extlist) | ||
191 | { | ||
192 | STACK_OF(CONF_VALUE) *extlist = NULL; | ||
193 | CONF_VALUE *conf_value = NULL; | ||
194 | int ret = 0; | ||
195 | |||
196 | if ((conf_value = calloc(1, sizeof(*conf_value))) == NULL) { | ||
197 | X509V3error(ERR_R_MALLOC_FAILURE); | ||
198 | goto err; | ||
199 | } | ||
200 | if (name != NULL) { | ||
201 | if ((conf_value->name = strdup(name)) == NULL) { | ||
202 | X509V3error(ERR_R_MALLOC_FAILURE); | ||
203 | goto err; | ||
204 | } | ||
205 | } | ||
206 | if (value != NULL) { | ||
207 | if ((conf_value->value = strdup(value)) == NULL) { | ||
208 | X509V3error(ERR_R_MALLOC_FAILURE); | ||
209 | goto err; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | if ((extlist = *out_extlist) == NULL) | ||
214 | extlist = sk_CONF_VALUE_new_null(); | ||
215 | if (extlist == NULL) { | ||
216 | X509V3error(ERR_R_MALLOC_FAILURE); | ||
217 | goto err; | ||
218 | } | ||
219 | |||
220 | if (!sk_CONF_VALUE_push(extlist, conf_value)) { | ||
221 | X509V3error(ERR_R_MALLOC_FAILURE); | ||
222 | goto err; | ||
223 | } | ||
224 | conf_value = NULL; | ||
225 | |||
226 | *out_extlist = extlist; | ||
227 | extlist = NULL; | ||
228 | |||
229 | ret = 1; | ||
230 | |||
231 | err: | ||
232 | if (extlist != *out_extlist) | ||
233 | sk_CONF_VALUE_pop_free(extlist, X509V3_conf_free); | ||
234 | X509V3_conf_free(conf_value); | ||
235 | |||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static int | ||
240 | ocsp_opt_header(int argc, char **argv, int *argsused) | ||
241 | { | ||
242 | if (argc < 3 || argv[1] == NULL || argv[2] == NULL) | ||
243 | return (1); | ||
244 | |||
245 | if (!x509v3_add_value(argv[1], argv[2], &cfg.headers)) { | ||
246 | cfg.no_usage = 1; | ||
247 | return (1); | ||
248 | } | ||
249 | |||
250 | *argsused = 3; | ||
251 | return (0); | ||
252 | } | ||
253 | |||
254 | static int | ||
255 | ocsp_opt_host(char *arg) | ||
256 | { | ||
257 | if (cfg.use_ssl != -1) | ||
258 | return (1); | ||
259 | |||
260 | cfg.host = arg; | ||
261 | return (0); | ||
262 | } | ||
263 | |||
264 | static int | ||
265 | ocsp_opt_issuer(char *arg) | ||
266 | { | ||
267 | X509_free(cfg.issuer); | ||
268 | cfg.issuer = load_cert(bio_err, arg, FORMAT_PEM, NULL, | ||
269 | "issuer certificate"); | ||
270 | if (cfg.issuer == NULL) { | ||
271 | cfg.no_usage = 1; | ||
272 | return (1); | ||
273 | } | ||
274 | return (0); | ||
275 | } | ||
276 | |||
277 | static int | ||
278 | ocsp_opt_ndays(char *arg) | ||
279 | { | ||
280 | const char *errstr = NULL; | ||
281 | |||
282 | cfg.ndays = strtonum(arg, 0, INT_MAX, &errstr); | ||
283 | if (errstr != NULL) { | ||
284 | BIO_printf(bio_err, "Illegal update period %s: %s\n", | ||
285 | arg, errstr); | ||
286 | return (1); | ||
287 | } | ||
288 | return (0); | ||
289 | } | ||
290 | |||
291 | static int | ||
292 | ocsp_opt_nmin(char *arg) | ||
293 | { | ||
294 | const char *errstr = NULL; | ||
295 | |||
296 | cfg.nmin = strtonum(arg, 0, INT_MAX, &errstr); | ||
297 | if (errstr != NULL) { | ||
298 | BIO_printf(bio_err, "Illegal update period %s: %s\n", | ||
299 | arg, errstr); | ||
300 | return (1); | ||
301 | } | ||
302 | |||
303 | if (cfg.ndays != -1) | ||
304 | return (1); | ||
305 | |||
306 | cfg.ndays = 0; | ||
307 | return (0); | ||
308 | } | ||
309 | |||
310 | static int | ||
311 | ocsp_opt_nrequest(char *arg) | ||
312 | { | ||
313 | const char *errstr = NULL; | ||
314 | |||
315 | cfg.accept_count = strtonum(arg, 0, INT_MAX, &errstr); | ||
316 | if (errstr != NULL) { | ||
317 | BIO_printf(bio_err, "Illegal accept count %s: %s\n", | ||
318 | arg, errstr); | ||
319 | return (1); | ||
320 | } | ||
321 | return (0); | ||
322 | } | ||
323 | |||
324 | static int | ||
325 | ocsp_opt_port(char *arg) | ||
326 | { | ||
327 | if (cfg.use_ssl != -1) | ||
328 | return (1); | ||
329 | |||
330 | cfg.port = arg; | ||
331 | return (0); | ||
332 | } | ||
333 | |||
334 | static int | ||
335 | ocsp_opt_serial(char *arg) | ||
336 | { | ||
337 | if (cfg.cert_id_md == NULL) | ||
338 | cfg.cert_id_md = EVP_sha1(); | ||
339 | if (!add_ocsp_serial(&cfg.req, arg, cfg.cert_id_md, | ||
340 | cfg.issuer, cfg.ids)) { | ||
341 | cfg.no_usage = 1; | ||
342 | return (1); | ||
343 | } | ||
344 | if (!sk_OPENSSL_STRING_push(cfg.reqnames, arg)) { | ||
345 | cfg.no_usage = 1; | ||
346 | return (1); | ||
347 | } | ||
348 | return (0); | ||
349 | } | ||
350 | |||
351 | static int | ||
352 | ocsp_opt_status_age(char *arg) | ||
353 | { | ||
354 | const char *errstr = NULL; | ||
355 | |||
356 | cfg.maxage = strtonum(arg, 0, LONG_MAX, &errstr); | ||
357 | if (errstr != NULL) { | ||
358 | BIO_printf(bio_err, "Illegal validity age %s: %s\n", | ||
359 | arg, errstr); | ||
360 | return (1); | ||
361 | } | ||
362 | return (0); | ||
363 | } | ||
364 | |||
365 | static int | ||
366 | ocsp_opt_text(void) | ||
367 | { | ||
368 | cfg.req_text = 1; | ||
369 | cfg.resp_text = 1; | ||
370 | return (0); | ||
371 | } | ||
372 | |||
373 | static int | ||
374 | ocsp_opt_timeout(char *arg) | ||
375 | { | ||
376 | const char *errstr = NULL; | ||
377 | |||
378 | cfg.req_timeout = strtonum(arg, 0, INT_MAX, &errstr); | ||
379 | if (errstr != NULL) { | ||
380 | BIO_printf(bio_err, "Illegal timeout value %s: %s\n", | ||
381 | arg, errstr); | ||
382 | return (1); | ||
383 | } | ||
384 | return (0); | ||
385 | } | ||
386 | |||
387 | static int | ||
388 | ocsp_opt_url(char *arg) | ||
389 | { | ||
390 | if (cfg.host == NULL && cfg.port == NULL && | ||
391 | cfg.path == NULL) { | ||
392 | if (!OCSP_parse_url(arg, &cfg.host, &cfg.port, | ||
393 | &cfg.path, &cfg.use_ssl)) { | ||
394 | BIO_printf(bio_err, "Error parsing URL\n"); | ||
395 | return (1); | ||
396 | } | ||
397 | } | ||
398 | return (0); | ||
399 | } | ||
400 | |||
401 | static int | ||
402 | ocsp_opt_vafile(char *arg) | ||
403 | { | ||
404 | cfg.verify_certfile = arg; | ||
405 | cfg.verify_flags |= OCSP_TRUSTOTHER; | ||
406 | return (0); | ||
407 | } | ||
408 | |||
409 | static int | ||
410 | ocsp_opt_validity_period(char *arg) | ||
411 | { | ||
412 | const char *errstr = NULL; | ||
413 | |||
414 | cfg.nsec = strtonum(arg, 0, LONG_MAX, &errstr); | ||
415 | if (errstr != NULL) { | ||
416 | BIO_printf(bio_err, "Illegal validity period %s: %s\n", | ||
417 | arg, errstr); | ||
418 | return (1); | ||
419 | } | ||
420 | return (0); | ||
421 | } | ||
422 | |||
423 | static const struct option ocsp_options[] = { | ||
424 | { | ||
425 | .name = "CA", | ||
426 | .argname = "file", | ||
427 | .desc = "CA certificate corresponding to the revocation information", | ||
428 | .type = OPTION_ARG, | ||
429 | .opt.arg = &cfg.rca_filename, | ||
430 | }, | ||
431 | { | ||
432 | .name = "CAfile", | ||
433 | .argname = "file", | ||
434 | .desc = "Trusted certificates file", | ||
435 | .type = OPTION_ARG, | ||
436 | .opt.arg = &cfg.CAfile, | ||
437 | }, | ||
438 | { | ||
439 | .name = "CApath", | ||
440 | .argname = "directory", | ||
441 | .desc = "Trusted certificates directory", | ||
442 | .type = OPTION_ARG, | ||
443 | .opt.arg = &cfg.CApath, | ||
444 | }, | ||
445 | { | ||
446 | .name = "cert", | ||
447 | .argname = "file", | ||
448 | .desc = "Certificate to check", | ||
449 | .type = OPTION_ARG_FUNC, | ||
450 | .opt.argfunc = ocsp_opt_cert, | ||
451 | }, | ||
452 | { | ||
453 | .name = "header", | ||
454 | .argname = "name value", | ||
455 | .desc = "Add the header name with the value to the request", | ||
456 | .type = OPTION_ARGV_FUNC, | ||
457 | .opt.argvfunc = ocsp_opt_header, | ||
458 | }, | ||
459 | { | ||
460 | .name = "host", | ||
461 | .argname = "hostname:port", | ||
462 | .desc = "Send OCSP request to host on port", | ||
463 | .type = OPTION_ARG_FUNC, | ||
464 | .opt.argfunc = ocsp_opt_host, | ||
465 | }, | ||
466 | { | ||
467 | .name = "ignore_err", | ||
468 | .desc = "Ignore the invalid response", | ||
469 | .type = OPTION_FLAG, | ||
470 | .opt.flag = &cfg.ignore_err, | ||
471 | }, | ||
472 | { | ||
473 | .name = "index", | ||
474 | .argname = "indexfile", | ||
475 | .desc = "Certificate status index file", | ||
476 | .type = OPTION_ARG, | ||
477 | .opt.arg = &cfg.ridx_filename, | ||
478 | }, | ||
479 | { | ||
480 | .name = "issuer", | ||
481 | .argname = "file", | ||
482 | .desc = "Issuer certificate", | ||
483 | .type = OPTION_ARG_FUNC, | ||
484 | .opt.argfunc = ocsp_opt_issuer, | ||
485 | }, | ||
486 | { | ||
487 | .name = "ndays", | ||
488 | .argname = "days", | ||
489 | .desc = "Number of days before next update", | ||
490 | .type = OPTION_ARG_FUNC, | ||
491 | .opt.argfunc = ocsp_opt_ndays, | ||
492 | }, | ||
493 | { | ||
494 | .name = "nmin", | ||
495 | .argname = "minutes", | ||
496 | .desc = "Number of minutes before next update", | ||
497 | .type = OPTION_ARG_FUNC, | ||
498 | .opt.argfunc = ocsp_opt_nmin, | ||
499 | }, | ||
500 | { | ||
501 | .name = "no_cert_checks", | ||
502 | .desc = "Don't do additional checks on signing certificate", | ||
503 | .type = OPTION_UL_VALUE_OR, | ||
504 | .opt.ulvalue = &cfg.verify_flags, | ||
505 | .ulvalue = OCSP_NOCHECKS, | ||
506 | }, | ||
507 | { | ||
508 | .name = "no_cert_verify", | ||
509 | .desc = "Don't check signing certificate", | ||
510 | .type = OPTION_UL_VALUE_OR, | ||
511 | .opt.ulvalue = &cfg.verify_flags, | ||
512 | .ulvalue = OCSP_NOVERIFY, | ||
513 | }, | ||
514 | { | ||
515 | .name = "no_certs", | ||
516 | .desc = "Don't include any certificates in signed request", | ||
517 | .type = OPTION_UL_VALUE_OR, | ||
518 | .opt.ulvalue = &cfg.sign_flags, | ||
519 | .ulvalue = OCSP_NOCERTS, | ||
520 | }, | ||
521 | { | ||
522 | .name = "no_chain", | ||
523 | .desc = "Don't use certificates in the response", | ||
524 | .type = OPTION_UL_VALUE_OR, | ||
525 | .opt.ulvalue = &cfg.verify_flags, | ||
526 | .ulvalue = OCSP_NOCHAIN, | ||
527 | }, | ||
528 | { | ||
529 | .name = "no_explicit", | ||
530 | .desc = "Don't check the explicit trust for OCSP signing", | ||
531 | .type = OPTION_UL_VALUE_OR, | ||
532 | .opt.ulvalue = &cfg.verify_flags, | ||
533 | .ulvalue = OCSP_NOEXPLICIT, | ||
534 | }, | ||
535 | { | ||
536 | .name = "no_intern", | ||
537 | .desc = "Don't search certificates contained in response for signer", | ||
538 | .type = OPTION_UL_VALUE_OR, | ||
539 | .opt.ulvalue = &cfg.verify_flags, | ||
540 | .ulvalue = OCSP_NOINTERN, | ||
541 | }, | ||
542 | { | ||
543 | .name = "no_nonce", | ||
544 | .desc = "Don't add OCSP nonce to request", | ||
545 | .type = OPTION_VALUE, | ||
546 | .opt.value = &cfg.add_nonce, | ||
547 | .value = 0, | ||
548 | }, | ||
549 | { | ||
550 | .name = "no_signature_verify", | ||
551 | .desc = "Don't check signature on response", | ||
552 | .type = OPTION_UL_VALUE_OR, | ||
553 | .opt.ulvalue = &cfg.verify_flags, | ||
554 | .ulvalue = OCSP_NOSIGS, | ||
555 | }, | ||
556 | { | ||
557 | .name = "nonce", | ||
558 | .desc = "Add OCSP nonce to request", | ||
559 | .type = OPTION_VALUE, | ||
560 | .opt.value = &cfg.add_nonce, | ||
561 | .value = 2, | ||
562 | }, | ||
563 | { | ||
564 | .name = "noverify", | ||
565 | .desc = "Don't verify response at all", | ||
566 | .type = OPTION_FLAG, | ||
567 | .opt.flag = &cfg.noverify, | ||
568 | }, | ||
569 | { | ||
570 | .name = "nrequest", | ||
571 | .argname = "number", | ||
572 | .desc = "Number of requests to accept (default unlimited)", | ||
573 | .type = OPTION_ARG_FUNC, | ||
574 | .opt.argfunc = ocsp_opt_nrequest, | ||
575 | }, | ||
576 | { | ||
577 | .name = "out", | ||
578 | .argname = "file", | ||
579 | .desc = "Output filename", | ||
580 | .type = OPTION_ARG, | ||
581 | .opt.arg = &cfg.outfile, | ||
582 | }, | ||
583 | { | ||
584 | .name = "path", | ||
585 | .argname = "path", | ||
586 | .desc = "Path to use in OCSP request", | ||
587 | .type = OPTION_ARG, | ||
588 | .opt.arg = &cfg.path, | ||
589 | }, | ||
590 | { | ||
591 | .name = "port", | ||
592 | .argname = "portnum", | ||
593 | .desc = "Port to run responder on", | ||
594 | .type = OPTION_ARG_FUNC, | ||
595 | .opt.argfunc = ocsp_opt_port, | ||
596 | }, | ||
597 | { | ||
598 | .name = "req_text", | ||
599 | .desc = "Print text form of request", | ||
600 | .type = OPTION_FLAG, | ||
601 | .opt.flag = &cfg.req_text, | ||
602 | }, | ||
603 | { | ||
604 | .name = "reqin", | ||
605 | .argname = "file", | ||
606 | .desc = "Read DER encoded OCSP request from \"file\"", | ||
607 | .type = OPTION_ARG, | ||
608 | .opt.arg = &cfg.reqin, | ||
609 | }, | ||
610 | { | ||
611 | .name = "reqout", | ||
612 | .argname = "file", | ||
613 | .desc = "Write DER encoded OCSP request to \"file\"", | ||
614 | .type = OPTION_ARG, | ||
615 | .opt.arg = &cfg.reqout, | ||
616 | }, | ||
617 | { | ||
618 | .name = "resp_key_id", | ||
619 | .desc = "Identify response by signing certificate key ID", | ||
620 | .type = OPTION_UL_VALUE_OR, | ||
621 | .opt.ulvalue = &cfg.rflags, | ||
622 | .ulvalue = OCSP_RESPID_KEY, | ||
623 | }, | ||
624 | { | ||
625 | .name = "resp_no_certs", | ||
626 | .desc = "Don't include any certificates in response", | ||
627 | .type = OPTION_UL_VALUE_OR, | ||
628 | .opt.ulvalue = &cfg.rflags, | ||
629 | .ulvalue = OCSP_NOCERTS, | ||
630 | }, | ||
631 | { | ||
632 | .name = "resp_text", | ||
633 | .desc = "Print text form of response", | ||
634 | .type = OPTION_FLAG, | ||
635 | .opt.flag = &cfg.resp_text, | ||
636 | }, | ||
637 | { | ||
638 | .name = "respin", | ||
639 | .argname = "file", | ||
640 | .desc = "Read DER encoded OCSP response from \"file\"", | ||
641 | .type = OPTION_ARG, | ||
642 | .opt.arg = &cfg.respin, | ||
643 | }, | ||
644 | { | ||
645 | .name = "respout", | ||
646 | .argname = "file", | ||
647 | .desc = "Write DER encoded OCSP response to \"file\"", | ||
648 | .type = OPTION_ARG, | ||
649 | .opt.arg = &cfg.respout, | ||
650 | }, | ||
651 | { | ||
652 | .name = "rkey", | ||
653 | .argname = "file", | ||
654 | .desc = "Responder key to sign responses with", | ||
655 | .type = OPTION_ARG, | ||
656 | .opt.arg = &cfg.rkeyfile, | ||
657 | }, | ||
658 | { | ||
659 | .name = "rother", | ||
660 | .argname = "file", | ||
661 | .desc = "Other certificates to include in response", | ||
662 | .type = OPTION_ARG, | ||
663 | .opt.arg = &cfg.rcertfile, | ||
664 | }, | ||
665 | { | ||
666 | .name = "rsigner", | ||
667 | .argname = "file", | ||
668 | .desc = "Responder certificate to sign responses with", | ||
669 | .type = OPTION_ARG, | ||
670 | .opt.arg = &cfg.rsignfile, | ||
671 | }, | ||
672 | { | ||
673 | .name = "serial", | ||
674 | .argname = "num", | ||
675 | .desc = "Serial number to check", | ||
676 | .type = OPTION_ARG_FUNC, | ||
677 | .opt.argfunc = ocsp_opt_serial, | ||
678 | }, | ||
679 | { | ||
680 | .name = "sign_other", | ||
681 | .argname = "file", | ||
682 | .desc = "Additional certificates to include in signed request", | ||
683 | .type = OPTION_ARG, | ||
684 | .opt.arg = &cfg.sign_certfile, | ||
685 | }, | ||
686 | { | ||
687 | .name = "signer", | ||
688 | .argname = "file", | ||
689 | .desc = "Certificate to sign OCSP request with", | ||
690 | .type = OPTION_ARG, | ||
691 | .opt.arg = &cfg.signfile, | ||
692 | }, | ||
693 | { | ||
694 | .name = "signkey", | ||
695 | .argname = "file", | ||
696 | .desc = "Private key to sign OCSP request with", | ||
697 | .type = OPTION_ARG, | ||
698 | .opt.arg = &cfg.keyfile, | ||
699 | }, | ||
700 | { | ||
701 | .name = "status_age", | ||
702 | .argname = "age", | ||
703 | .desc = "Maximum status age in seconds", | ||
704 | .type = OPTION_ARG_FUNC, | ||
705 | .opt.argfunc = ocsp_opt_status_age, | ||
706 | }, | ||
707 | { | ||
708 | .name = "text", | ||
709 | .desc = "Print text form of request and response", | ||
710 | .type = OPTION_FUNC, | ||
711 | .opt.func = ocsp_opt_text, | ||
712 | }, | ||
713 | { | ||
714 | .name = "timeout", | ||
715 | .argname = "seconds", | ||
716 | .desc = "Connection timeout to the OCSP responder in seconds", | ||
717 | .type = OPTION_ARG_FUNC, | ||
718 | .opt.argfunc = ocsp_opt_timeout, | ||
719 | }, | ||
720 | { | ||
721 | .name = "trust_other", | ||
722 | .desc = "Don't verify additional certificates", | ||
723 | .type = OPTION_UL_VALUE_OR, | ||
724 | .opt.ulvalue = &cfg.verify_flags, | ||
725 | .ulvalue = OCSP_TRUSTOTHER, | ||
726 | }, | ||
727 | { | ||
728 | .name = "url", | ||
729 | .argname = "responder_url", | ||
730 | .desc = "OCSP responder URL", | ||
731 | .type = OPTION_ARG_FUNC, | ||
732 | .opt.argfunc = ocsp_opt_url, | ||
733 | }, | ||
734 | { | ||
735 | .name = "VAfile", | ||
736 | .argname = "file", | ||
737 | .desc = "Explicitly trusted responder certificates", | ||
738 | .type = OPTION_ARG_FUNC, | ||
739 | .opt.argfunc = ocsp_opt_vafile, | ||
740 | }, | ||
741 | { | ||
742 | .name = "validity_period", | ||
743 | .argname = "n", | ||
744 | .desc = "Maximum validity discrepancy in seconds", | ||
745 | .type = OPTION_ARG_FUNC, | ||
746 | .opt.argfunc = ocsp_opt_validity_period, | ||
747 | }, | ||
748 | { | ||
749 | .name = "verify_other", | ||
750 | .argname = "file", | ||
751 | .desc = "Additional certificates to search for signer", | ||
752 | .type = OPTION_ARG, | ||
753 | .opt.arg = &cfg.verify_certfile, | ||
754 | }, | ||
755 | { | ||
756 | .name = NULL, | ||
757 | .desc = "", | ||
758 | .type = OPTION_ARGV_FUNC, | ||
759 | .opt.argvfunc = ocsp_opt_cert_id_md, | ||
760 | }, | ||
761 | { NULL }, | ||
762 | }; | ||
763 | |||
764 | static void | ||
765 | ocsp_usage(void) | ||
766 | { | ||
767 | fprintf(stderr, "usage: ocsp " | ||
768 | "[-CA file] [-CAfile file] [-CApath directory] [-cert file]\n" | ||
769 | " [-dgst alg] [-header name value] [-host hostname:port]\n" | ||
770 | " [-ignore_err] [-index indexfile] [-issuer file]\n" | ||
771 | " [-ndays days] [-nmin minutes] [-no_cert_checks]\n" | ||
772 | " [-no_cert_verify] [-no_certs] [-no_chain] [-no_explicit]\n" | ||
773 | " [-no_intern] [-no_nonce] [-no_signature_verify] [-nonce]\n" | ||
774 | " [-noverify] [-nrequest number] [-out file] [-path path]\n" | ||
775 | " [-port portnum] [-req_text] [-reqin file] [-reqout file]\n" | ||
776 | " [-resp_key_id] [-resp_no_certs] [-resp_text] [-respin file]\n" | ||
777 | " [-respout file] [-rkey file] [-rother file] [-rsigner file]\n" | ||
778 | " [-serial num] [-sign_other file] [-signer file]\n" | ||
779 | " [-signkey file] [-status_age age] [-text]\n" | ||
780 | " [-timeout seconds] [-trust_other] [-url responder_url]\n" | ||
781 | " [-VAfile file] [-validity_period nsec] [-verify_other file]\n"); | ||
782 | fprintf(stderr, "\n"); | ||
783 | options_usage(ocsp_options); | ||
784 | fprintf(stderr, "\n"); | ||
785 | } | ||
786 | |||
787 | int | ||
788 | ocsp_main(int argc, char **argv) | ||
789 | { | ||
790 | OCSP_RESPONSE *resp = NULL; | ||
791 | OCSP_BASICRESP *bs = NULL; | ||
792 | X509 *signer = NULL, *rsigner = NULL; | ||
793 | EVP_PKEY *key = NULL, *rkey = NULL; | ||
794 | BIO *acbio = NULL, *cbio = NULL; | ||
795 | BIO *derbio = NULL; | ||
796 | BIO *out = NULL; | ||
797 | X509_STORE *store = NULL; | ||
798 | STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; | ||
799 | int ret = 1; | ||
800 | int badarg = 0; | ||
801 | int i; | ||
802 | X509 *rca_cert = NULL; | ||
803 | CA_DB *rdb = NULL; | ||
804 | |||
805 | if (pledge("stdio cpath wpath rpath inet dns tty", NULL) == -1) { | ||
806 | perror("pledge"); | ||
807 | exit(1); | ||
808 | } | ||
809 | |||
810 | memset(&cfg, 0, sizeof(cfg)); | ||
811 | cfg.accept_count = -1; | ||
812 | cfg.add_nonce = 1; | ||
813 | if ((cfg.ids = sk_OCSP_CERTID_new_null()) == NULL) | ||
814 | goto end; | ||
815 | cfg.maxage = -1; | ||
816 | cfg.ndays = -1; | ||
817 | cfg.nsec = MAX_VALIDITY_PERIOD; | ||
818 | cfg.req_timeout = -1; | ||
819 | if ((cfg.reqnames = sk_OPENSSL_STRING_new_null()) == NULL) | ||
820 | goto end; | ||
821 | cfg.use_ssl = -1; | ||
822 | |||
823 | if (options_parse(argc, argv, ocsp_options, NULL, NULL) != 0) { | ||
824 | if (cfg.no_usage) | ||
825 | goto end; | ||
826 | else | ||
827 | badarg = 1; | ||
828 | } | ||
829 | |||
830 | /* Have we anything to do? */ | ||
831 | if (!cfg.req && !cfg.reqin && !cfg.respin && | ||
832 | !(cfg.port && cfg.ridx_filename)) | ||
833 | badarg = 1; | ||
834 | |||
835 | if (badarg) { | ||
836 | ocsp_usage(); | ||
837 | goto end; | ||
838 | } | ||
839 | if (cfg.outfile) | ||
840 | out = BIO_new_file(cfg.outfile, "w"); | ||
841 | else | ||
842 | out = BIO_new_fp(stdout, BIO_NOCLOSE); | ||
843 | |||
844 | if (!out) { | ||
845 | BIO_printf(bio_err, "Error opening output file\n"); | ||
846 | goto end; | ||
847 | } | ||
848 | if (!cfg.req && (cfg.add_nonce != 2)) | ||
849 | cfg.add_nonce = 0; | ||
850 | |||
851 | if (!cfg.req && cfg.reqin) { | ||
852 | derbio = BIO_new_file(cfg.reqin, "rb"); | ||
853 | if (!derbio) { | ||
854 | BIO_printf(bio_err, | ||
855 | "Error Opening OCSP request file\n"); | ||
856 | goto end; | ||
857 | } | ||
858 | cfg.req = d2i_OCSP_REQUEST_bio(derbio, NULL); | ||
859 | BIO_free(derbio); | ||
860 | if (!cfg.req) { | ||
861 | BIO_printf(bio_err, "Error reading OCSP request\n"); | ||
862 | goto end; | ||
863 | } | ||
864 | } | ||
865 | if (!cfg.req && cfg.port) { | ||
866 | acbio = init_responder(cfg.port); | ||
867 | if (!acbio) | ||
868 | goto end; | ||
869 | } | ||
870 | if (cfg.rsignfile && !rdb) { | ||
871 | if (!cfg.rkeyfile) | ||
872 | cfg.rkeyfile = cfg.rsignfile; | ||
873 | rsigner = load_cert(bio_err, cfg.rsignfile, FORMAT_PEM, | ||
874 | NULL, "responder certificate"); | ||
875 | if (!rsigner) { | ||
876 | BIO_printf(bio_err, | ||
877 | "Error loading responder certificate\n"); | ||
878 | goto end; | ||
879 | } | ||
880 | rca_cert = load_cert(bio_err, cfg.rca_filename, | ||
881 | FORMAT_PEM, NULL, "CA certificate"); | ||
882 | if (cfg.rcertfile) { | ||
883 | rother = load_certs(bio_err, cfg.rcertfile, | ||
884 | FORMAT_PEM, NULL, "responder other certificates"); | ||
885 | if (!rother) | ||
886 | goto end; | ||
887 | } | ||
888 | rkey = load_key(bio_err, cfg.rkeyfile, FORMAT_PEM, 0, | ||
889 | NULL, "responder private key"); | ||
890 | if (!rkey) | ||
891 | goto end; | ||
892 | } | ||
893 | if (acbio) | ||
894 | BIO_printf(bio_err, "Waiting for OCSP client connections...\n"); | ||
895 | |||
896 | redo_accept: | ||
897 | |||
898 | if (acbio) { | ||
899 | if (!do_responder(&cfg.req, &cbio, acbio, | ||
900 | cfg.port)) | ||
901 | goto end; | ||
902 | if (!cfg.req) { | ||
903 | resp = OCSP_response_create( | ||
904 | OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); | ||
905 | send_ocsp_response(cbio, resp); | ||
906 | goto done_resp; | ||
907 | } | ||
908 | } | ||
909 | if (!cfg.req && | ||
910 | (cfg.signfile || cfg.reqout || cfg.host || | ||
911 | cfg.add_nonce || cfg.ridx_filename)) { | ||
912 | BIO_printf(bio_err, | ||
913 | "Need an OCSP request for this operation!\n"); | ||
914 | goto end; | ||
915 | } | ||
916 | if (cfg.req && cfg.add_nonce) | ||
917 | OCSP_request_add1_nonce(cfg.req, NULL, -1); | ||
918 | |||
919 | if (cfg.signfile) { | ||
920 | if (!cfg.keyfile) | ||
921 | cfg.keyfile = cfg.signfile; | ||
922 | signer = load_cert(bio_err, cfg.signfile, FORMAT_PEM, | ||
923 | NULL, "signer certificate"); | ||
924 | if (!signer) { | ||
925 | BIO_printf(bio_err, | ||
926 | "Error loading signer certificate\n"); | ||
927 | goto end; | ||
928 | } | ||
929 | if (cfg.sign_certfile) { | ||
930 | sign_other = load_certs(bio_err, | ||
931 | cfg.sign_certfile, FORMAT_PEM, NULL, | ||
932 | "signer certificates"); | ||
933 | if (!sign_other) | ||
934 | goto end; | ||
935 | } | ||
936 | key = load_key(bio_err, cfg.keyfile, FORMAT_PEM, 0, | ||
937 | NULL, "signer private key"); | ||
938 | if (!key) | ||
939 | goto end; | ||
940 | |||
941 | if (!OCSP_request_sign(cfg.req, signer, key, NULL, | ||
942 | sign_other, cfg.sign_flags)) { | ||
943 | BIO_printf(bio_err, "Error signing OCSP request\n"); | ||
944 | goto end; | ||
945 | } | ||
946 | } | ||
947 | if (cfg.req_text && cfg.req) | ||
948 | OCSP_REQUEST_print(out, cfg.req, 0); | ||
949 | |||
950 | if (cfg.reqout) { | ||
951 | derbio = BIO_new_file(cfg.reqout, "wb"); | ||
952 | if (!derbio) { | ||
953 | BIO_printf(bio_err, "Error opening file %s\n", | ||
954 | cfg.reqout); | ||
955 | goto end; | ||
956 | } | ||
957 | i2d_OCSP_REQUEST_bio(derbio, cfg.req); | ||
958 | BIO_free(derbio); | ||
959 | } | ||
960 | if (cfg.ridx_filename && (!rkey || !rsigner || !rca_cert)) { | ||
961 | BIO_printf(bio_err, | ||
962 | "Need a responder certificate, key and CA for this operation!\n"); | ||
963 | goto end; | ||
964 | } | ||
965 | if (cfg.ridx_filename && !rdb) { | ||
966 | rdb = load_index(cfg.ridx_filename, NULL); | ||
967 | if (!rdb) | ||
968 | goto end; | ||
969 | if (!index_index(rdb)) | ||
970 | goto end; | ||
971 | } | ||
972 | if (rdb) { | ||
973 | i = make_ocsp_response(&resp, cfg.req, rdb, rca_cert, | ||
974 | rsigner, rkey, rother, cfg.rflags, | ||
975 | cfg.nmin, cfg.ndays); | ||
976 | if (cbio) | ||
977 | send_ocsp_response(cbio, resp); | ||
978 | } else if (cfg.host) { | ||
979 | resp = process_responder(bio_err, cfg.req, | ||
980 | cfg.host, | ||
981 | cfg.path ? cfg.path : "/", | ||
982 | cfg.port, cfg.use_ssl, cfg.headers, | ||
983 | cfg.req_timeout); | ||
984 | if (!resp) | ||
985 | goto end; | ||
986 | } else if (cfg.respin) { | ||
987 | derbio = BIO_new_file(cfg.respin, "rb"); | ||
988 | if (!derbio) { | ||
989 | BIO_printf(bio_err, | ||
990 | "Error Opening OCSP response file\n"); | ||
991 | goto end; | ||
992 | } | ||
993 | resp = d2i_OCSP_RESPONSE_bio(derbio, NULL); | ||
994 | BIO_free(derbio); | ||
995 | if (!resp) { | ||
996 | BIO_printf(bio_err, "Error reading OCSP response\n"); | ||
997 | goto end; | ||
998 | } | ||
999 | } else { | ||
1000 | ret = 0; | ||
1001 | goto end; | ||
1002 | } | ||
1003 | |||
1004 | done_resp: | ||
1005 | |||
1006 | if (cfg.respout) { | ||
1007 | derbio = BIO_new_file(cfg.respout, "wb"); | ||
1008 | if (!derbio) { | ||
1009 | BIO_printf(bio_err, "Error opening file %s\n", | ||
1010 | cfg.respout); | ||
1011 | goto end; | ||
1012 | } | ||
1013 | i2d_OCSP_RESPONSE_bio(derbio, resp); | ||
1014 | BIO_free(derbio); | ||
1015 | } | ||
1016 | i = OCSP_response_status(resp); | ||
1017 | |||
1018 | if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | ||
1019 | BIO_printf(bio_err, "Responder Error: %s (%d)\n", | ||
1020 | OCSP_response_status_str(i), i); | ||
1021 | if (cfg.ignore_err) | ||
1022 | goto redo_accept; | ||
1023 | ret = 1; | ||
1024 | goto end; | ||
1025 | } | ||
1026 | if (cfg.resp_text) | ||
1027 | OCSP_RESPONSE_print(out, resp, 0); | ||
1028 | |||
1029 | /* If running as responder don't verify our own response */ | ||
1030 | if (cbio) { | ||
1031 | if (cfg.accept_count > 0) | ||
1032 | cfg.accept_count--; | ||
1033 | /* Redo if more connections needed */ | ||
1034 | if (cfg.accept_count) { | ||
1035 | BIO_free_all(cbio); | ||
1036 | cbio = NULL; | ||
1037 | OCSP_REQUEST_free(cfg.req); | ||
1038 | cfg.req = NULL; | ||
1039 | OCSP_RESPONSE_free(resp); | ||
1040 | resp = NULL; | ||
1041 | goto redo_accept; | ||
1042 | } | ||
1043 | goto end; | ||
1044 | } | ||
1045 | if (!store) | ||
1046 | store = setup_verify(bio_err, cfg.CAfile, | ||
1047 | cfg.CApath); | ||
1048 | if (!store) | ||
1049 | goto end; | ||
1050 | if (cfg.verify_certfile) { | ||
1051 | verify_other = load_certs(bio_err, cfg.verify_certfile, | ||
1052 | FORMAT_PEM, NULL, "validator certificate"); | ||
1053 | if (!verify_other) | ||
1054 | goto end; | ||
1055 | } | ||
1056 | bs = OCSP_response_get1_basic(resp); | ||
1057 | |||
1058 | if (!bs) { | ||
1059 | BIO_printf(bio_err, "Error parsing response\n"); | ||
1060 | goto end; | ||
1061 | } | ||
1062 | if (!cfg.noverify) { | ||
1063 | if (cfg.req && | ||
1064 | ((i = OCSP_check_nonce(cfg.req, bs)) <= 0)) { | ||
1065 | if (i == -1) { | ||
1066 | BIO_printf(bio_err, | ||
1067 | "WARNING: no nonce in response\n"); | ||
1068 | } else { | ||
1069 | BIO_printf(bio_err, "Nonce Verify error\n"); | ||
1070 | goto end; | ||
1071 | } | ||
1072 | } | ||
1073 | i = OCSP_basic_verify(bs, verify_other, store, | ||
1074 | cfg.verify_flags); | ||
1075 | if (i < 0) | ||
1076 | i = OCSP_basic_verify(bs, NULL, store, 0); | ||
1077 | |||
1078 | if (i <= 0) { | ||
1079 | BIO_printf(bio_err, "Response Verify Failure\n"); | ||
1080 | ERR_print_errors(bio_err); | ||
1081 | } else { | ||
1082 | BIO_printf(bio_err, "Response verify OK\n"); | ||
1083 | } | ||
1084 | } | ||
1085 | if (!print_ocsp_summary(out, bs, cfg.req, cfg.reqnames, | ||
1086 | cfg.ids, cfg.nsec, cfg.maxage)) | ||
1087 | goto end; | ||
1088 | |||
1089 | ret = 0; | ||
1090 | |||
1091 | end: | ||
1092 | ERR_print_errors(bio_err); | ||
1093 | X509_free(signer); | ||
1094 | X509_STORE_free(store); | ||
1095 | EVP_PKEY_free(key); | ||
1096 | EVP_PKEY_free(rkey); | ||
1097 | X509_free(cfg.issuer); | ||
1098 | X509_free(cfg.cert); | ||
1099 | X509_free(rsigner); | ||
1100 | X509_free(rca_cert); | ||
1101 | free_index(rdb); | ||
1102 | BIO_free_all(cbio); | ||
1103 | BIO_free_all(acbio); | ||
1104 | BIO_free(out); | ||
1105 | OCSP_REQUEST_free(cfg.req); | ||
1106 | OCSP_RESPONSE_free(resp); | ||
1107 | OCSP_BASICRESP_free(bs); | ||
1108 | sk_OPENSSL_STRING_free(cfg.reqnames); | ||
1109 | sk_OCSP_CERTID_free(cfg.ids); | ||
1110 | sk_X509_pop_free(sign_other, X509_free); | ||
1111 | sk_X509_pop_free(verify_other, X509_free); | ||
1112 | sk_CONF_VALUE_pop_free(cfg.headers, X509V3_conf_free); | ||
1113 | |||
1114 | if (cfg.use_ssl != -1) { | ||
1115 | free(cfg.host); | ||
1116 | free(cfg.port); | ||
1117 | free(cfg.path); | ||
1118 | } | ||
1119 | return (ret); | ||
1120 | } | ||
1121 | |||
1122 | static int | ||
1123 | add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, const EVP_MD *cert_id_md, | ||
1124 | X509 *issuer, STACK_OF(OCSP_CERTID) *ids) | ||
1125 | { | ||
1126 | OCSP_CERTID *id; | ||
1127 | |||
1128 | if (!issuer) { | ||
1129 | BIO_printf(bio_err, "No issuer certificate specified\n"); | ||
1130 | return 0; | ||
1131 | } | ||
1132 | if (!*req) | ||
1133 | *req = OCSP_REQUEST_new(); | ||
1134 | if (!*req) | ||
1135 | goto err; | ||
1136 | id = OCSP_cert_to_id(cert_id_md, cert, issuer); | ||
1137 | if (!id || !sk_OCSP_CERTID_push(ids, id)) | ||
1138 | goto err; | ||
1139 | if (!OCSP_request_add0_id(*req, id)) | ||
1140 | goto err; | ||
1141 | return 1; | ||
1142 | |||
1143 | err: | ||
1144 | BIO_printf(bio_err, "Error Creating OCSP request\n"); | ||
1145 | return 0; | ||
1146 | } | ||
1147 | |||
1148 | static int | ||
1149 | add_ocsp_serial(OCSP_REQUEST **req, char *serial, const EVP_MD *cert_id_md, | ||
1150 | X509 *issuer, STACK_OF(OCSP_CERTID) *ids) | ||
1151 | { | ||
1152 | OCSP_CERTID *id; | ||
1153 | X509_NAME *iname; | ||
1154 | ASN1_BIT_STRING *ikey; | ||
1155 | ASN1_INTEGER *sno; | ||
1156 | |||
1157 | if (!issuer) { | ||
1158 | BIO_printf(bio_err, "No issuer certificate specified\n"); | ||
1159 | return 0; | ||
1160 | } | ||
1161 | if (!*req) | ||
1162 | *req = OCSP_REQUEST_new(); | ||
1163 | if (!*req) | ||
1164 | goto err; | ||
1165 | iname = X509_get_subject_name(issuer); | ||
1166 | ikey = X509_get0_pubkey_bitstr(issuer); | ||
1167 | sno = s2i_ASN1_INTEGER(NULL, serial); | ||
1168 | if (!sno) { | ||
1169 | BIO_printf(bio_err, "Error converting serial number %s\n", | ||
1170 | serial); | ||
1171 | return 0; | ||
1172 | } | ||
1173 | id = OCSP_cert_id_new(cert_id_md, iname, ikey, sno); | ||
1174 | ASN1_INTEGER_free(sno); | ||
1175 | if (!id || !sk_OCSP_CERTID_push(ids, id)) | ||
1176 | goto err; | ||
1177 | if (!OCSP_request_add0_id(*req, id)) | ||
1178 | goto err; | ||
1179 | return 1; | ||
1180 | |||
1181 | err: | ||
1182 | BIO_printf(bio_err, "Error Creating OCSP request\n"); | ||
1183 | return 0; | ||
1184 | } | ||
1185 | |||
1186 | static int | ||
1187 | print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, | ||
1188 | STACK_OF(OPENSSL_STRING) *names, STACK_OF(OCSP_CERTID) *ids, long nsec, | ||
1189 | long maxage) | ||
1190 | { | ||
1191 | OCSP_CERTID *id; | ||
1192 | char *name; | ||
1193 | int i; | ||
1194 | int status, reason; | ||
1195 | |||
1196 | ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; | ||
1197 | |||
1198 | if (!bs || !req || !sk_OPENSSL_STRING_num(names) || | ||
1199 | !sk_OCSP_CERTID_num(ids)) | ||
1200 | return 1; | ||
1201 | |||
1202 | for (i = 0; i < sk_OCSP_CERTID_num(ids); i++) { | ||
1203 | id = sk_OCSP_CERTID_value(ids, i); | ||
1204 | name = sk_OPENSSL_STRING_value(names, i); | ||
1205 | BIO_printf(out, "%s: ", name); | ||
1206 | |||
1207 | if (!OCSP_resp_find_status(bs, id, &status, &reason, | ||
1208 | &rev, &thisupd, &nextupd)) { | ||
1209 | BIO_puts(out, "ERROR: No Status found.\n"); | ||
1210 | continue; | ||
1211 | } | ||
1212 | /* | ||
1213 | * Check validity: if invalid write to output BIO so we know | ||
1214 | * which response this refers to. | ||
1215 | */ | ||
1216 | if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) { | ||
1217 | BIO_puts(out, "WARNING: Status times invalid.\n"); | ||
1218 | ERR_print_errors(out); | ||
1219 | } | ||
1220 | BIO_printf(out, "%s\n", OCSP_cert_status_str(status)); | ||
1221 | |||
1222 | BIO_puts(out, "\tThis Update: "); | ||
1223 | ASN1_GENERALIZEDTIME_print(out, thisupd); | ||
1224 | BIO_puts(out, "\n"); | ||
1225 | |||
1226 | if (nextupd) { | ||
1227 | BIO_puts(out, "\tNext Update: "); | ||
1228 | ASN1_GENERALIZEDTIME_print(out, nextupd); | ||
1229 | BIO_puts(out, "\n"); | ||
1230 | } | ||
1231 | if (status != V_OCSP_CERTSTATUS_REVOKED) | ||
1232 | continue; | ||
1233 | |||
1234 | if (reason != -1) | ||
1235 | BIO_printf(out, "\tReason: %s\n", | ||
1236 | OCSP_crl_reason_str(reason)); | ||
1237 | |||
1238 | BIO_puts(out, "\tRevocation Time: "); | ||
1239 | ASN1_GENERALIZEDTIME_print(out, rev); | ||
1240 | BIO_puts(out, "\n"); | ||
1241 | } | ||
1242 | |||
1243 | return 1; | ||
1244 | } | ||
1245 | |||
1246 | |||
1247 | static int | ||
1248 | make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db, | ||
1249 | X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, | ||
1250 | unsigned long flags, int nmin, int ndays) | ||
1251 | { | ||
1252 | ASN1_TIME *thisupd = NULL, *nextupd = NULL; | ||
1253 | OCSP_CERTID *cid, *ca_id = NULL; | ||
1254 | OCSP_BASICRESP *bs = NULL; | ||
1255 | int i, id_count, ret = 1; | ||
1256 | |||
1257 | id_count = OCSP_request_onereq_count(req); | ||
1258 | |||
1259 | if (id_count <= 0) { | ||
1260 | *resp = OCSP_response_create( | ||
1261 | OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); | ||
1262 | goto end; | ||
1263 | } | ||
1264 | bs = OCSP_BASICRESP_new(); | ||
1265 | thisupd = X509_gmtime_adj(NULL, 0); | ||
1266 | if (ndays != -1) | ||
1267 | nextupd = X509_gmtime_adj(NULL, nmin * 60 + ndays * 3600 * 24); | ||
1268 | |||
1269 | /* Examine each certificate id in the request */ | ||
1270 | for (i = 0; i < id_count; i++) { | ||
1271 | OCSP_ONEREQ *one; | ||
1272 | ASN1_INTEGER *serial; | ||
1273 | char **inf; | ||
1274 | ASN1_OBJECT *cert_id_md_oid; | ||
1275 | const EVP_MD *cert_id_md; | ||
1276 | one = OCSP_request_onereq_get0(req, i); | ||
1277 | cid = OCSP_onereq_get0_id(one); | ||
1278 | |||
1279 | OCSP_id_get0_info(NULL, &cert_id_md_oid, NULL, NULL, cid); | ||
1280 | |||
1281 | cert_id_md = EVP_get_digestbyobj(cert_id_md_oid); | ||
1282 | if (!cert_id_md) { | ||
1283 | *resp = OCSP_response_create( | ||
1284 | OCSP_RESPONSE_STATUS_INTERNALERROR, NULL); | ||
1285 | goto end; | ||
1286 | } | ||
1287 | OCSP_CERTID_free(ca_id); | ||
1288 | ca_id = OCSP_cert_to_id(cert_id_md, NULL, ca); | ||
1289 | |||
1290 | /* Is this request about our CA? */ | ||
1291 | if (OCSP_id_issuer_cmp(ca_id, cid)) { | ||
1292 | OCSP_basic_add1_status(bs, cid, | ||
1293 | V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, | ||
1294 | thisupd, nextupd); | ||
1295 | continue; | ||
1296 | } | ||
1297 | OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); | ||
1298 | inf = lookup_serial(db, serial); | ||
1299 | if (!inf) { | ||
1300 | OCSP_basic_add1_status(bs, cid, | ||
1301 | V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, | ||
1302 | thisupd, nextupd); | ||
1303 | } else if (inf[DB_type][0] == DB_TYPE_VAL) { | ||
1304 | OCSP_basic_add1_status(bs, cid, | ||
1305 | V_OCSP_CERTSTATUS_GOOD, 0, NULL, | ||
1306 | thisupd, nextupd); | ||
1307 | } else if (inf[DB_type][0] == DB_TYPE_REV) { | ||
1308 | ASN1_OBJECT *inst = NULL; | ||
1309 | ASN1_TIME *revtm = NULL; | ||
1310 | ASN1_GENERALIZEDTIME *invtm = NULL; | ||
1311 | OCSP_SINGLERESP *single; | ||
1312 | int reason = -1; | ||
1313 | |||
1314 | unpack_revinfo(&revtm, &reason, &inst, &invtm, | ||
1315 | inf[DB_rev_date]); | ||
1316 | single = OCSP_basic_add1_status(bs, cid, | ||
1317 | V_OCSP_CERTSTATUS_REVOKED, | ||
1318 | reason, revtm, | ||
1319 | thisupd, nextupd); | ||
1320 | if (invtm) | ||
1321 | OCSP_SINGLERESP_add1_ext_i2d(single, | ||
1322 | NID_invalidity_date, invtm, 0, 0); | ||
1323 | else if (inst) | ||
1324 | OCSP_SINGLERESP_add1_ext_i2d(single, | ||
1325 | NID_hold_instruction_code, inst, 0, 0); | ||
1326 | ASN1_OBJECT_free(inst); | ||
1327 | ASN1_TIME_free(revtm); | ||
1328 | ASN1_GENERALIZEDTIME_free(invtm); | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1332 | OCSP_copy_nonce(bs, req); | ||
1333 | |||
1334 | OCSP_basic_sign(bs, rcert, rkey, NULL, rother, flags); | ||
1335 | |||
1336 | *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); | ||
1337 | |||
1338 | end: | ||
1339 | ASN1_TIME_free(thisupd); | ||
1340 | ASN1_TIME_free(nextupd); | ||
1341 | OCSP_CERTID_free(ca_id); | ||
1342 | OCSP_BASICRESP_free(bs); | ||
1343 | return ret; | ||
1344 | } | ||
1345 | |||
1346 | static char ** | ||
1347 | lookup_serial(CA_DB *db, ASN1_INTEGER *ser) | ||
1348 | { | ||
1349 | int i; | ||
1350 | BIGNUM *bn = NULL; | ||
1351 | char *itmp, *row[DB_NUMBER], **rrow; | ||
1352 | |||
1353 | for (i = 0; i < DB_NUMBER; i++) | ||
1354 | row[i] = NULL; | ||
1355 | bn = ASN1_INTEGER_to_BN(ser, NULL); | ||
1356 | OPENSSL_assert(bn); /* FIXME: should report an error at this | ||
1357 | * point and abort */ | ||
1358 | if (BN_is_zero(bn)) | ||
1359 | itmp = strdup("00"); | ||
1360 | else | ||
1361 | itmp = BN_bn2hex(bn); | ||
1362 | row[DB_serial] = itmp; | ||
1363 | BN_free(bn); | ||
1364 | rrow = TXT_DB_get_by_index(db->db, DB_serial, row); | ||
1365 | free(itmp); | ||
1366 | return rrow; | ||
1367 | } | ||
1368 | |||
1369 | /* Quick and dirty OCSP server: read in and parse input request */ | ||
1370 | |||
1371 | static BIO * | ||
1372 | init_responder(char *port) | ||
1373 | { | ||
1374 | BIO *acbio = NULL, *bufbio = NULL; | ||
1375 | |||
1376 | bufbio = BIO_new(BIO_f_buffer()); | ||
1377 | if (!bufbio) | ||
1378 | goto err; | ||
1379 | acbio = BIO_new_accept(port); | ||
1380 | if (!acbio) | ||
1381 | goto err; | ||
1382 | BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR); | ||
1383 | BIO_set_accept_bios(acbio, bufbio); | ||
1384 | bufbio = NULL; | ||
1385 | |||
1386 | if (BIO_do_accept(acbio) <= 0) { | ||
1387 | BIO_printf(bio_err, "Error setting up accept BIO\n"); | ||
1388 | ERR_print_errors(bio_err); | ||
1389 | goto err; | ||
1390 | } | ||
1391 | return acbio; | ||
1392 | |||
1393 | err: | ||
1394 | BIO_free_all(acbio); | ||
1395 | BIO_free(bufbio); | ||
1396 | return NULL; | ||
1397 | } | ||
1398 | |||
1399 | static int | ||
1400 | do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, char *port) | ||
1401 | { | ||
1402 | int have_post = 0, len; | ||
1403 | OCSP_REQUEST *req = NULL; | ||
1404 | char inbuf[1024]; | ||
1405 | BIO *cbio = NULL; | ||
1406 | |||
1407 | if (BIO_do_accept(acbio) <= 0) { | ||
1408 | BIO_printf(bio_err, "Error accepting connection\n"); | ||
1409 | ERR_print_errors(bio_err); | ||
1410 | return 0; | ||
1411 | } | ||
1412 | cbio = BIO_pop(acbio); | ||
1413 | *pcbio = cbio; | ||
1414 | |||
1415 | for (;;) { | ||
1416 | len = BIO_gets(cbio, inbuf, sizeof inbuf); | ||
1417 | if (len <= 0) | ||
1418 | return 1; | ||
1419 | /* Look for "POST" signalling start of query */ | ||
1420 | if (!have_post) { | ||
1421 | if (strncmp(inbuf, "POST", 4)) { | ||
1422 | BIO_printf(bio_err, "Invalid request\n"); | ||
1423 | return 1; | ||
1424 | } | ||
1425 | have_post = 1; | ||
1426 | } | ||
1427 | /* Look for end of headers */ | ||
1428 | if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) | ||
1429 | break; | ||
1430 | } | ||
1431 | |||
1432 | /* Try to read OCSP request */ | ||
1433 | |||
1434 | req = d2i_OCSP_REQUEST_bio(cbio, NULL); | ||
1435 | |||
1436 | if (!req) { | ||
1437 | BIO_printf(bio_err, "Error parsing OCSP request\n"); | ||
1438 | ERR_print_errors(bio_err); | ||
1439 | } | ||
1440 | *preq = req; | ||
1441 | |||
1442 | return 1; | ||
1443 | } | ||
1444 | |||
1445 | static int | ||
1446 | send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) | ||
1447 | { | ||
1448 | static const char http_resp[] = | ||
1449 | "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" | ||
1450 | "Content-Length: %d\r\n\r\n"; | ||
1451 | |||
1452 | if (!cbio) | ||
1453 | return 0; | ||
1454 | BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); | ||
1455 | i2d_OCSP_RESPONSE_bio(cbio, resp); | ||
1456 | (void) BIO_flush(cbio); | ||
1457 | return 1; | ||
1458 | } | ||
1459 | |||
1460 | static OCSP_RESPONSE * | ||
1461 | query_responder(BIO *err, BIO *cbio, char *path, STACK_OF(CONF_VALUE) *headers, | ||
1462 | const char *host, OCSP_REQUEST *req, int req_timeout) | ||
1463 | { | ||
1464 | int fd; | ||
1465 | int rv; | ||
1466 | int i; | ||
1467 | int have_host = 0; | ||
1468 | OCSP_REQ_CTX *ctx = NULL; | ||
1469 | OCSP_RESPONSE *rsp = NULL; | ||
1470 | struct pollfd pfd[1]; | ||
1471 | |||
1472 | if (req_timeout != -1) | ||
1473 | BIO_set_nbio(cbio, 1); | ||
1474 | |||
1475 | rv = BIO_do_connect(cbio); | ||
1476 | |||
1477 | if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) { | ||
1478 | BIO_puts(err, "Error connecting BIO\n"); | ||
1479 | return NULL; | ||
1480 | } | ||
1481 | if (BIO_get_fd(cbio, &fd) < 0) { | ||
1482 | BIO_puts(err, "Can't get connection fd\n"); | ||
1483 | goto err; | ||
1484 | } | ||
1485 | if (req_timeout != -1 && rv <= 0) { | ||
1486 | pfd[0].fd = fd; | ||
1487 | pfd[0].events = POLLOUT; | ||
1488 | rv = poll(pfd, 1, req_timeout * 1000); | ||
1489 | if (rv == 0) { | ||
1490 | BIO_puts(err, "Timeout on connect\n"); | ||
1491 | return NULL; | ||
1492 | } | ||
1493 | if (rv == -1) { | ||
1494 | BIO_puts(err, "Poll error\n"); | ||
1495 | return NULL; | ||
1496 | } | ||
1497 | } | ||
1498 | ctx = OCSP_sendreq_new(cbio, path, NULL, -1); | ||
1499 | if (!ctx) | ||
1500 | return NULL; | ||
1501 | |||
1502 | for (i = 0; i < sk_CONF_VALUE_num(headers); i++) { | ||
1503 | CONF_VALUE *hdr = sk_CONF_VALUE_value(headers, i); | ||
1504 | if (strcasecmp("host", hdr->name) == 0) | ||
1505 | have_host = 1; | ||
1506 | if (!OCSP_REQ_CTX_add1_header(ctx, hdr->name, hdr->value)) | ||
1507 | goto err; | ||
1508 | } | ||
1509 | |||
1510 | if (!have_host) { | ||
1511 | if (!OCSP_REQ_CTX_add1_header(ctx, "Host", host)) | ||
1512 | goto err; | ||
1513 | } | ||
1514 | |||
1515 | if (!OCSP_REQ_CTX_set1_req(ctx, req)) | ||
1516 | goto err; | ||
1517 | |||
1518 | for (;;) { | ||
1519 | rv = OCSP_sendreq_nbio(&rsp, ctx); | ||
1520 | if (rv != -1) | ||
1521 | break; | ||
1522 | if (req_timeout == -1) | ||
1523 | continue; | ||
1524 | pfd[0].fd = fd; | ||
1525 | if (BIO_should_read(cbio)) { | ||
1526 | pfd[0].events = POLLIN; | ||
1527 | } else if (BIO_should_write(cbio)) { | ||
1528 | pfd[0].events = POLLOUT; | ||
1529 | } else { | ||
1530 | BIO_puts(err, "Unexpected retry condition\n"); | ||
1531 | goto err; | ||
1532 | } | ||
1533 | rv = poll(pfd, 1, req_timeout * 1000); | ||
1534 | if (rv == 0) { | ||
1535 | BIO_puts(err, "Timeout on request\n"); | ||
1536 | break; | ||
1537 | } | ||
1538 | if (rv == -1 || (pfd[0].revents & (POLLERR|POLLNVAL))) { | ||
1539 | BIO_puts(err, "Poll error\n"); | ||
1540 | break; | ||
1541 | } | ||
1542 | } | ||
1543 | |||
1544 | err: | ||
1545 | OCSP_REQ_CTX_free(ctx); | ||
1546 | return rsp; | ||
1547 | } | ||
1548 | |||
1549 | OCSP_RESPONSE * | ||
1550 | process_responder(BIO *err, OCSP_REQUEST *req, char *host, char *path, | ||
1551 | char *port, int use_ssl, STACK_OF(CONF_VALUE) *headers, int req_timeout) | ||
1552 | { | ||
1553 | BIO *cbio = NULL; | ||
1554 | SSL_CTX *ctx = NULL; | ||
1555 | OCSP_RESPONSE *resp = NULL; | ||
1556 | |||
1557 | cbio = BIO_new_connect(host); | ||
1558 | if (!cbio) { | ||
1559 | BIO_printf(err, "Error creating connect BIO\n"); | ||
1560 | goto end; | ||
1561 | } | ||
1562 | if (port) | ||
1563 | BIO_set_conn_port(cbio, port); | ||
1564 | if (use_ssl == 1) { | ||
1565 | BIO *sbio; | ||
1566 | ctx = SSL_CTX_new(TLS_client_method()); | ||
1567 | if (ctx == NULL) { | ||
1568 | BIO_printf(err, "Error creating SSL context.\n"); | ||
1569 | goto end; | ||
1570 | } | ||
1571 | SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); | ||
1572 | sbio = BIO_new_ssl(ctx, 1); | ||
1573 | cbio = BIO_push(sbio, cbio); | ||
1574 | } | ||
1575 | resp = query_responder(err, cbio, path, headers, host, req, req_timeout); | ||
1576 | if (!resp) | ||
1577 | BIO_printf(bio_err, "Error querying OCSP responder\n"); | ||
1578 | |||
1579 | end: | ||
1580 | BIO_free_all(cbio); | ||
1581 | SSL_CTX_free(ctx); | ||
1582 | return resp; | ||
1583 | } | ||
1584 | #endif | ||