sl@0: /* ocsp.c */ sl@0: /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL sl@0: * project 2000. sl@0: */ sl@0: /* ==================================================================== sl@0: * Copyright (c) 1999 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * licensing@OpenSSL.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: #ifndef OPENSSL_NO_OCSP sl@0: sl@0: #include sl@0: #include sl@0: #include "apps.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /* Maximum leeway in validity period: default 5 minutes */ sl@0: #define MAX_VALIDITY_PERIOD (5 * 60) sl@0: sl@0: static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, sl@0: STACK_OF(OCSP_CERTID) *ids); sl@0: static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer, sl@0: STACK_OF(OCSP_CERTID) *ids); sl@0: static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, sl@0: STACK *names, STACK_OF(OCSP_CERTID) *ids, sl@0: long nsec, long maxage); sl@0: sl@0: static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db, sl@0: X509 *ca, X509 *rcert, EVP_PKEY *rkey, sl@0: STACK_OF(X509) *rother, unsigned long flags, sl@0: int nmin, int ndays); sl@0: sl@0: static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser); sl@0: static BIO *init_responder(char *port); sl@0: static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, char *port); sl@0: static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); sl@0: sl@0: #undef PROG sl@0: #define PROG ocsp_main sl@0: sl@0: int MAIN(int, char **); sl@0: sl@0: int MAIN(int argc, char **argv) sl@0: { sl@0: ENGINE *e = NULL; sl@0: char **args; sl@0: char *host = NULL, *port = NULL, *path = "/"; sl@0: char *reqin = NULL, *respin = NULL; sl@0: char *reqout = NULL, *respout = NULL; sl@0: char *signfile = NULL, *keyfile = NULL; sl@0: char *rsignfile = NULL, *rkeyfile = NULL; sl@0: char *outfile = NULL; sl@0: int add_nonce = 1, noverify = 0, use_ssl = -1; sl@0: OCSP_REQUEST *req = NULL; sl@0: OCSP_RESPONSE *resp = NULL; sl@0: OCSP_BASICRESP *bs = NULL; sl@0: X509 *issuer = NULL, *cert = NULL; sl@0: X509 *signer = NULL, *rsigner = NULL; sl@0: EVP_PKEY *key = NULL, *rkey = NULL; sl@0: BIO *acbio = NULL, *cbio = NULL; sl@0: BIO *derbio = NULL; sl@0: BIO *out = NULL; sl@0: int req_text = 0, resp_text = 0; sl@0: long nsec = MAX_VALIDITY_PERIOD, maxage = -1; sl@0: char *CAfile = NULL, *CApath = NULL; sl@0: X509_STORE *store = NULL; sl@0: SSL_CTX *ctx = NULL; sl@0: STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; sl@0: char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL; sl@0: unsigned long sign_flags = 0, verify_flags = 0, rflags = 0; sl@0: int ret = 1; sl@0: int accept_count = -1; sl@0: int badarg = 0; sl@0: int i; sl@0: int ignore_err = 0; sl@0: STACK *reqnames = NULL; sl@0: STACK_OF(OCSP_CERTID) *ids = NULL; sl@0: sl@0: X509 *rca_cert = NULL; sl@0: char *ridx_filename = NULL; sl@0: char *rca_filename = NULL; sl@0: CA_DB *rdb = NULL; sl@0: int nmin = 0, ndays = -1; sl@0: sl@0: if (bio_err == NULL) sl@0: bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); sl@0: if (!load_config(bio_err, NULL)) sl@0: goto end; sl@0: SSL_load_error_strings(); sl@0: OpenSSL_add_ssl_algorithms(); sl@0: args = argv + 1; sl@0: reqnames = sk_new_null(); sl@0: ids = sk_OCSP_CERTID_new_null(); sl@0: while (!badarg && *args && *args[0] == '-') sl@0: { sl@0: if (!strcmp(*args, "-out")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: outfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-url")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: if (!OCSP_parse_url(*args, &host, &port, &path, &use_ssl)) sl@0: { sl@0: BIO_printf(bio_err, "Error parsing URL\n"); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-host")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: host = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-port")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: port = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-ignore_err")) sl@0: ignore_err = 1; sl@0: else if (!strcmp(*args, "-noverify")) sl@0: noverify = 1; sl@0: else if (!strcmp(*args, "-nonce")) sl@0: add_nonce = 2; sl@0: else if (!strcmp(*args, "-no_nonce")) sl@0: add_nonce = 0; sl@0: else if (!strcmp(*args, "-resp_no_certs")) sl@0: rflags |= OCSP_NOCERTS; sl@0: else if (!strcmp(*args, "-resp_key_id")) sl@0: rflags |= OCSP_RESPID_KEY; sl@0: else if (!strcmp(*args, "-no_certs")) sl@0: sign_flags |= OCSP_NOCERTS; sl@0: else if (!strcmp(*args, "-no_signature_verify")) sl@0: verify_flags |= OCSP_NOSIGS; sl@0: else if (!strcmp(*args, "-no_cert_verify")) sl@0: verify_flags |= OCSP_NOVERIFY; sl@0: else if (!strcmp(*args, "-no_chain")) sl@0: verify_flags |= OCSP_NOCHAIN; sl@0: else if (!strcmp(*args, "-no_cert_checks")) sl@0: verify_flags |= OCSP_NOCHECKS; sl@0: else if (!strcmp(*args, "-no_explicit")) sl@0: verify_flags |= OCSP_NOEXPLICIT; sl@0: else if (!strcmp(*args, "-trust_other")) sl@0: verify_flags |= OCSP_TRUSTOTHER; sl@0: else if (!strcmp(*args, "-no_intern")) sl@0: verify_flags |= OCSP_NOINTERN; sl@0: else if (!strcmp(*args, "-text")) sl@0: { sl@0: req_text = 1; sl@0: resp_text = 1; sl@0: } sl@0: else if (!strcmp(*args, "-req_text")) sl@0: req_text = 1; sl@0: else if (!strcmp(*args, "-resp_text")) sl@0: resp_text = 1; sl@0: else if (!strcmp(*args, "-reqin")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: reqin = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-respin")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: respin = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-signer")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: signfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-VAfile")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: verify_certfile = *args; sl@0: verify_flags |= OCSP_TRUSTOTHER; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-sign_other")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: sign_certfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-verify_other")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: verify_certfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-CAfile")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: CAfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-CApath")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: CApath = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-validity_period")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: nsec = atol(*args); sl@0: if (nsec < 0) sl@0: { sl@0: BIO_printf(bio_err, sl@0: "Illegal validity period %s\n", sl@0: *args); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-status_age")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: maxage = atol(*args); sl@0: if (maxage < 0) sl@0: { sl@0: BIO_printf(bio_err, sl@0: "Illegal validity age %s\n", sl@0: *args); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-signkey")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: keyfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-reqout")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: reqout = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-respout")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: respout = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-path")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: path = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-issuer")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: X509_free(issuer); sl@0: issuer = load_cert(bio_err, *args, FORMAT_PEM, sl@0: NULL, e, "issuer certificate"); sl@0: if(!issuer) goto end; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-cert")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: X509_free(cert); sl@0: cert = load_cert(bio_err, *args, FORMAT_PEM, sl@0: NULL, e, "certificate"); sl@0: if(!cert) goto end; sl@0: if(!add_ocsp_cert(&req, cert, issuer, ids)) sl@0: goto end; sl@0: if(!sk_push(reqnames, *args)) sl@0: goto end; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-serial")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: if(!add_ocsp_serial(&req, *args, issuer, ids)) sl@0: goto end; sl@0: if(!sk_push(reqnames, *args)) sl@0: goto end; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-index")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: ridx_filename = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-CA")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: rca_filename = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-nmin")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: nmin = atol(*args); sl@0: if (nmin < 0) sl@0: { sl@0: BIO_printf(bio_err, sl@0: "Illegal update period %s\n", sl@0: *args); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: if (ndays == -1) sl@0: ndays = 0; sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-nrequest")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: accept_count = atol(*args); sl@0: if (accept_count < 0) sl@0: { sl@0: BIO_printf(bio_err, sl@0: "Illegal accept count %s\n", sl@0: *args); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp (*args, "-ndays")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: ndays = atol(*args); sl@0: if (ndays < 0) sl@0: { sl@0: BIO_printf(bio_err, sl@0: "Illegal update period %s\n", sl@0: *args); sl@0: badarg = 1; sl@0: } sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-rsigner")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: rsignfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-rkey")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: rkeyfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else if (!strcmp(*args, "-rother")) sl@0: { sl@0: if (args[1]) sl@0: { sl@0: args++; sl@0: rcertfile = *args; sl@0: } sl@0: else badarg = 1; sl@0: } sl@0: else badarg = 1; sl@0: args++; sl@0: } sl@0: sl@0: /* Have we anything to do? */ sl@0: if (!req && !reqin && !respin && !(port && ridx_filename)) badarg = 1; sl@0: sl@0: if (badarg) sl@0: { sl@0: BIO_printf (bio_err, "OCSP utility\n"); sl@0: BIO_printf (bio_err, "Usage ocsp [options]\n"); sl@0: BIO_printf (bio_err, "where options are\n"); sl@0: BIO_printf (bio_err, "-out file output filename\n"); sl@0: BIO_printf (bio_err, "-issuer file issuer certificate\n"); sl@0: BIO_printf (bio_err, "-cert file certificate to check\n"); sl@0: BIO_printf (bio_err, "-serial n serial number to check\n"); sl@0: BIO_printf (bio_err, "-signer file certificate to sign OCSP request with\n"); sl@0: BIO_printf (bio_err, "-signkey file private key to sign OCSP request with\n"); sl@0: BIO_printf (bio_err, "-sign_other file additional certificates to include in signed request\n"); sl@0: BIO_printf (bio_err, "-no_certs don't include any certificates in signed request\n"); sl@0: BIO_printf (bio_err, "-req_text print text form of request\n"); sl@0: BIO_printf (bio_err, "-resp_text print text form of response\n"); sl@0: BIO_printf (bio_err, "-text print text form of request and response\n"); sl@0: BIO_printf (bio_err, "-reqout file write DER encoded OCSP request to \"file\"\n"); sl@0: BIO_printf (bio_err, "-respout file write DER encoded OCSP reponse to \"file\"\n"); sl@0: BIO_printf (bio_err, "-reqin file read DER encoded OCSP request from \"file\"\n"); sl@0: BIO_printf (bio_err, "-respin file read DER encoded OCSP reponse from \"file\"\n"); sl@0: BIO_printf (bio_err, "-nonce add OCSP nonce to request\n"); sl@0: BIO_printf (bio_err, "-no_nonce don't add OCSP nonce to request\n"); sl@0: BIO_printf (bio_err, "-url URL OCSP responder URL\n"); sl@0: BIO_printf (bio_err, "-host host:n send OCSP request to host on port n\n"); sl@0: BIO_printf (bio_err, "-path path to use in OCSP request\n"); sl@0: BIO_printf (bio_err, "-CApath dir trusted certificates directory\n"); sl@0: BIO_printf (bio_err, "-CAfile file trusted certificates file\n"); sl@0: BIO_printf (bio_err, "-VAfile file validator certificates file\n"); sl@0: BIO_printf (bio_err, "-validity_period n maximum validity discrepancy in seconds\n"); sl@0: BIO_printf (bio_err, "-status_age n maximum status age in seconds\n"); sl@0: BIO_printf (bio_err, "-noverify don't verify response at all\n"); sl@0: BIO_printf (bio_err, "-verify_other file additional certificates to search for signer\n"); sl@0: BIO_printf (bio_err, "-trust_other don't verify additional certificates\n"); sl@0: BIO_printf (bio_err, "-no_intern don't search certificates contained in response for signer\n"); sl@0: BIO_printf (bio_err, "-no_signature_verify don't check signature on response\n"); sl@0: BIO_printf (bio_err, "-no_cert_verify don't check signing certificate\n"); sl@0: BIO_printf (bio_err, "-no_chain don't chain verify response\n"); sl@0: BIO_printf (bio_err, "-no_cert_checks don't do additional checks on signing certificate\n"); sl@0: BIO_printf (bio_err, "-port num port to run responder on\n"); sl@0: BIO_printf (bio_err, "-index file certificate status index file\n"); sl@0: BIO_printf (bio_err, "-CA file CA certificate\n"); sl@0: BIO_printf (bio_err, "-rsigner file responder certificate to sign responses with\n"); sl@0: BIO_printf (bio_err, "-rkey file responder key to sign responses with\n"); sl@0: BIO_printf (bio_err, "-rother file other certificates to include in response\n"); sl@0: BIO_printf (bio_err, "-resp_no_certs don't include any certificates in response\n"); sl@0: BIO_printf (bio_err, "-nmin n number of minutes before next update\n"); sl@0: BIO_printf (bio_err, "-ndays n number of days before next update\n"); sl@0: BIO_printf (bio_err, "-resp_key_id identify reponse by signing certificate key ID\n"); sl@0: BIO_printf (bio_err, "-nrequest n number of requests to accept (default unlimited)\n"); sl@0: goto end; sl@0: } sl@0: sl@0: if(outfile) out = BIO_new_file(outfile, "w"); sl@0: else out = BIO_new_fp(stdout, BIO_NOCLOSE); sl@0: if(!out) sl@0: { sl@0: BIO_printf(bio_err, "Error opening output file\n"); sl@0: goto end; sl@0: } sl@0: sl@0: if (!req && (add_nonce != 2)) add_nonce = 0; sl@0: sl@0: if (!req && reqin) sl@0: { sl@0: derbio = BIO_new_file(reqin, "rb"); sl@0: if (!derbio) sl@0: { sl@0: BIO_printf(bio_err, "Error Opening OCSP request file\n"); sl@0: goto end; sl@0: } sl@0: req = d2i_OCSP_REQUEST_bio(derbio, NULL); sl@0: BIO_free(derbio); sl@0: if(!req) sl@0: { sl@0: BIO_printf(bio_err, "Error reading OCSP request\n"); sl@0: goto end; sl@0: } sl@0: } sl@0: sl@0: if (!req && port) sl@0: { sl@0: acbio = init_responder(port); sl@0: if (!acbio) sl@0: goto end; sl@0: } sl@0: sl@0: if (rsignfile && !rdb) sl@0: { sl@0: if (!rkeyfile) rkeyfile = rsignfile; sl@0: rsigner = load_cert(bio_err, rsignfile, FORMAT_PEM, sl@0: NULL, e, "responder certificate"); sl@0: if (!rsigner) sl@0: { sl@0: BIO_printf(bio_err, "Error loading responder certificate\n"); sl@0: goto end; sl@0: } sl@0: rca_cert = load_cert(bio_err, rca_filename, FORMAT_PEM, sl@0: NULL, e, "CA certificate"); sl@0: if (rcertfile) sl@0: { sl@0: rother = load_certs(bio_err, rcertfile, FORMAT_PEM, sl@0: NULL, e, "responder other certificates"); sl@0: if (!rother) goto end; sl@0: } sl@0: rkey = load_key(bio_err, rkeyfile, FORMAT_PEM, 0, NULL, NULL, sl@0: "responder private key"); sl@0: if (!rkey) sl@0: goto end; sl@0: } sl@0: if(acbio) sl@0: BIO_printf(bio_err, "Waiting for OCSP client connections...\n"); sl@0: sl@0: redo_accept: sl@0: sl@0: if (acbio) sl@0: { sl@0: if (!do_responder(&req, &cbio, acbio, port)) sl@0: goto end; sl@0: if (!req) sl@0: { sl@0: resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); sl@0: send_ocsp_response(cbio, resp); sl@0: goto done_resp; sl@0: } sl@0: } sl@0: sl@0: if (!req && (signfile || reqout || host || add_nonce || ridx_filename)) sl@0: { sl@0: BIO_printf(bio_err, "Need an OCSP request for this operation!\n"); sl@0: goto end; sl@0: } sl@0: sl@0: if (req && add_nonce) OCSP_request_add1_nonce(req, NULL, -1); sl@0: sl@0: if (signfile) sl@0: { sl@0: if (!keyfile) keyfile = signfile; sl@0: signer = load_cert(bio_err, signfile, FORMAT_PEM, sl@0: NULL, e, "signer certificate"); sl@0: if (!signer) sl@0: { sl@0: BIO_printf(bio_err, "Error loading signer certificate\n"); sl@0: goto end; sl@0: } sl@0: if (sign_certfile) sl@0: { sl@0: sign_other = load_certs(bio_err, sign_certfile, FORMAT_PEM, sl@0: NULL, e, "signer certificates"); sl@0: if (!sign_other) goto end; sl@0: } sl@0: key = load_key(bio_err, keyfile, FORMAT_PEM, 0, NULL, NULL, sl@0: "signer private key"); sl@0: if (!key) sl@0: goto end; sl@0: if (!OCSP_request_sign(req, signer, key, EVP_sha1(), sign_other, sign_flags)) sl@0: { sl@0: BIO_printf(bio_err, "Error signing OCSP request\n"); sl@0: goto end; sl@0: } sl@0: } sl@0: sl@0: if (req_text && req) OCSP_REQUEST_print(out, req, 0); sl@0: sl@0: if (reqout) sl@0: { sl@0: derbio = BIO_new_file(reqout, "wb"); sl@0: if(!derbio) sl@0: { sl@0: BIO_printf(bio_err, "Error opening file %s\n", reqout); sl@0: goto end; sl@0: } sl@0: i2d_OCSP_REQUEST_bio(derbio, req); sl@0: BIO_free(derbio); sl@0: } sl@0: sl@0: if (ridx_filename && (!rkey || !rsigner || !rca_cert)) sl@0: { sl@0: BIO_printf(bio_err, "Need a responder certificate, key and CA for this operation!\n"); sl@0: goto end; sl@0: } sl@0: sl@0: if (ridx_filename && !rdb) sl@0: { sl@0: rdb = load_index(ridx_filename, NULL); sl@0: if (!rdb) goto end; sl@0: if (!index_index(rdb)) goto end; sl@0: } sl@0: sl@0: if (rdb) sl@0: { sl@0: i = make_ocsp_response(&resp, req, rdb, rca_cert, rsigner, rkey, rother, rflags, nmin, ndays); sl@0: if (cbio) sl@0: send_ocsp_response(cbio, resp); sl@0: } sl@0: else if (host) sl@0: { sl@0: #ifndef OPENSSL_NO_SOCK sl@0: cbio = BIO_new_connect(host); sl@0: #else sl@0: BIO_printf(bio_err, "Error creating connect BIO - sockets not supported.\n"); sl@0: goto end; sl@0: #endif sl@0: if (!cbio) sl@0: { sl@0: BIO_printf(bio_err, "Error creating connect BIO\n"); sl@0: goto end; sl@0: } sl@0: if (port) BIO_set_conn_port(cbio, port); sl@0: if (use_ssl == 1) sl@0: { sl@0: BIO *sbio; sl@0: #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) sl@0: ctx = SSL_CTX_new(SSLv23_client_method()); sl@0: #elif !defined(OPENSSL_NO_SSL3) sl@0: ctx = SSL_CTX_new(SSLv3_client_method()); sl@0: #elif !defined(OPENSSL_NO_SSL2) sl@0: ctx = SSL_CTX_new(SSLv2_client_method()); sl@0: #else sl@0: BIO_printf(bio_err, "SSL is disabled\n"); sl@0: goto end; sl@0: #endif sl@0: if (ctx == NULL) sl@0: { sl@0: BIO_printf(bio_err, "Error creating SSL context.\n"); sl@0: goto end; sl@0: } sl@0: SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); sl@0: sbio = BIO_new_ssl(ctx, 1); sl@0: cbio = BIO_push(sbio, cbio); sl@0: } sl@0: if (BIO_do_connect(cbio) <= 0) sl@0: { sl@0: BIO_printf(bio_err, "Error connecting BIO\n"); sl@0: goto end; sl@0: } sl@0: resp = OCSP_sendreq_bio(cbio, path, req); sl@0: BIO_free_all(cbio); sl@0: cbio = NULL; sl@0: if (!resp) sl@0: { sl@0: BIO_printf(bio_err, "Error querying OCSP responsder\n"); sl@0: goto end; sl@0: } sl@0: } sl@0: else if (respin) sl@0: { sl@0: derbio = BIO_new_file(respin, "rb"); sl@0: if (!derbio) sl@0: { sl@0: BIO_printf(bio_err, "Error Opening OCSP response file\n"); sl@0: goto end; sl@0: } sl@0: resp = d2i_OCSP_RESPONSE_bio(derbio, NULL); sl@0: BIO_free(derbio); sl@0: if(!resp) sl@0: { sl@0: BIO_printf(bio_err, "Error reading OCSP response\n"); sl@0: goto end; sl@0: } sl@0: sl@0: } sl@0: else sl@0: { sl@0: ret = 0; sl@0: goto end; sl@0: } sl@0: sl@0: done_resp: sl@0: sl@0: if (respout) sl@0: { sl@0: derbio = BIO_new_file(respout, "wb"); sl@0: if(!derbio) sl@0: { sl@0: BIO_printf(bio_err, "Error opening file %s\n", respout); sl@0: goto end; sl@0: } sl@0: i2d_OCSP_RESPONSE_bio(derbio, resp); sl@0: BIO_free(derbio); sl@0: } sl@0: sl@0: i = OCSP_response_status(resp); sl@0: sl@0: if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) sl@0: { sl@0: BIO_printf(out, "Responder Error: %s (%d)\n", sl@0: OCSP_response_status_str(i), i); sl@0: if (ignore_err) sl@0: goto redo_accept; sl@0: ret = 0; sl@0: goto end; sl@0: } sl@0: sl@0: if (resp_text) OCSP_RESPONSE_print(out, resp, 0); sl@0: sl@0: /* If running as responder don't verify our own response */ sl@0: if (cbio) sl@0: { sl@0: if (accept_count > 0) sl@0: accept_count--; sl@0: /* Redo if more connections needed */ sl@0: if (accept_count) sl@0: { sl@0: BIO_free_all(cbio); sl@0: cbio = NULL; sl@0: OCSP_REQUEST_free(req); sl@0: req = NULL; sl@0: OCSP_RESPONSE_free(resp); sl@0: resp = NULL; sl@0: goto redo_accept; sl@0: } sl@0: goto end; sl@0: } sl@0: sl@0: if (!store) sl@0: store = setup_verify(bio_err, CAfile, CApath); sl@0: if (!store) sl@0: goto end; sl@0: if (verify_certfile) sl@0: { sl@0: verify_other = load_certs(bio_err, verify_certfile, FORMAT_PEM, sl@0: NULL, e, "validator certificate"); sl@0: if (!verify_other) goto end; sl@0: } sl@0: sl@0: bs = OCSP_response_get1_basic(resp); sl@0: sl@0: if (!bs) sl@0: { sl@0: BIO_printf(bio_err, "Error parsing response\n"); sl@0: goto end; sl@0: } sl@0: sl@0: if (!noverify) sl@0: { sl@0: if (req && ((i = OCSP_check_nonce(req, bs)) <= 0)) sl@0: { sl@0: if (i == -1) sl@0: BIO_printf(bio_err, "WARNING: no nonce in response\n"); sl@0: else sl@0: { sl@0: BIO_printf(bio_err, "Nonce Verify error\n"); sl@0: goto end; sl@0: } sl@0: } sl@0: sl@0: i = OCSP_basic_verify(bs, verify_other, store, verify_flags); sl@0: if (i < 0) i = OCSP_basic_verify(bs, NULL, store, 0); sl@0: sl@0: if(i <= 0) sl@0: { sl@0: BIO_printf(bio_err, "Response Verify Failure\n"); sl@0: ERR_print_errors(bio_err); sl@0: } sl@0: else sl@0: BIO_printf(bio_err, "Response verify OK\n"); sl@0: sl@0: } sl@0: sl@0: if (!print_ocsp_summary(out, bs, req, reqnames, ids, nsec, maxage)) sl@0: goto end; sl@0: sl@0: ret = 0; sl@0: sl@0: end: sl@0: ERR_print_errors(bio_err); sl@0: X509_free(signer); sl@0: X509_STORE_free(store); sl@0: EVP_PKEY_free(key); sl@0: EVP_PKEY_free(rkey); sl@0: X509_free(issuer); sl@0: X509_free(cert); sl@0: X509_free(rsigner); sl@0: X509_free(rca_cert); sl@0: free_index(rdb); sl@0: BIO_free_all(cbio); sl@0: BIO_free_all(acbio); sl@0: BIO_free(out); sl@0: OCSP_REQUEST_free(req); sl@0: OCSP_RESPONSE_free(resp); sl@0: OCSP_BASICRESP_free(bs); sl@0: sk_free(reqnames); sl@0: sk_OCSP_CERTID_free(ids); sl@0: sk_X509_pop_free(sign_other, X509_free); sl@0: sk_X509_pop_free(verify_other, X509_free); sl@0: sl@0: if (use_ssl != -1) sl@0: { sl@0: OPENSSL_free(host); sl@0: OPENSSL_free(port); sl@0: OPENSSL_free(path); sl@0: SSL_CTX_free(ctx); sl@0: } sl@0: sl@0: OPENSSL_EXIT(ret); sl@0: } sl@0: sl@0: static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, sl@0: STACK_OF(OCSP_CERTID) *ids) sl@0: { sl@0: OCSP_CERTID *id; sl@0: if(!issuer) sl@0: { sl@0: BIO_printf(bio_err, "No issuer certificate specified\n"); sl@0: return 0; sl@0: } sl@0: if(!*req) *req = OCSP_REQUEST_new(); sl@0: if(!*req) goto err; sl@0: id = OCSP_cert_to_id(NULL, cert, issuer); sl@0: if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err; sl@0: if(!OCSP_request_add0_id(*req, id)) goto err; sl@0: return 1; sl@0: sl@0: err: sl@0: BIO_printf(bio_err, "Error Creating OCSP request\n"); sl@0: return 0; sl@0: } sl@0: sl@0: static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer, sl@0: STACK_OF(OCSP_CERTID) *ids) sl@0: { sl@0: OCSP_CERTID *id; sl@0: X509_NAME *iname; sl@0: ASN1_BIT_STRING *ikey; sl@0: ASN1_INTEGER *sno; sl@0: if(!issuer) sl@0: { sl@0: BIO_printf(bio_err, "No issuer certificate specified\n"); sl@0: return 0; sl@0: } sl@0: if(!*req) *req = OCSP_REQUEST_new(); sl@0: if(!*req) goto err; sl@0: iname = X509_get_subject_name(issuer); sl@0: ikey = X509_get0_pubkey_bitstr(issuer); sl@0: sno = s2i_ASN1_INTEGER(NULL, serial); sl@0: if(!sno) sl@0: { sl@0: BIO_printf(bio_err, "Error converting serial number %s\n", serial); sl@0: return 0; sl@0: } sl@0: id = OCSP_cert_id_new(EVP_sha1(), iname, ikey, sno); sl@0: ASN1_INTEGER_free(sno); sl@0: if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err; sl@0: if(!OCSP_request_add0_id(*req, id)) goto err; sl@0: return 1; sl@0: sl@0: err: sl@0: BIO_printf(bio_err, "Error Creating OCSP request\n"); sl@0: return 0; sl@0: } sl@0: sl@0: static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, sl@0: STACK *names, STACK_OF(OCSP_CERTID) *ids, sl@0: long nsec, long maxage) sl@0: { sl@0: OCSP_CERTID *id; sl@0: char *name; sl@0: int i; sl@0: sl@0: int status, reason; sl@0: sl@0: ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; sl@0: sl@0: if (!bs || !req || !sk_num(names) || !sk_OCSP_CERTID_num(ids)) sl@0: return 1; sl@0: sl@0: for (i = 0; i < sk_OCSP_CERTID_num(ids); i++) sl@0: { sl@0: id = sk_OCSP_CERTID_value(ids, i); sl@0: name = sk_value(names, i); sl@0: BIO_printf(out, "%s: ", name); sl@0: sl@0: if(!OCSP_resp_find_status(bs, id, &status, &reason, sl@0: &rev, &thisupd, &nextupd)) sl@0: { sl@0: BIO_puts(out, "ERROR: No Status found.\n"); sl@0: continue; sl@0: } sl@0: sl@0: /* Check validity: if invalid write to output BIO so we sl@0: * know which response this refers to. sl@0: */ sl@0: if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) sl@0: { sl@0: BIO_puts(out, "WARNING: Status times invalid.\n"); sl@0: ERR_print_errors(out); sl@0: } sl@0: BIO_printf(out, "%s\n", OCSP_cert_status_str(status)); sl@0: sl@0: BIO_puts(out, "\tThis Update: "); sl@0: ASN1_GENERALIZEDTIME_print(out, thisupd); sl@0: BIO_puts(out, "\n"); sl@0: sl@0: if(nextupd) sl@0: { sl@0: BIO_puts(out, "\tNext Update: "); sl@0: ASN1_GENERALIZEDTIME_print(out, nextupd); sl@0: BIO_puts(out, "\n"); sl@0: } sl@0: sl@0: if (status != V_OCSP_CERTSTATUS_REVOKED) sl@0: continue; sl@0: sl@0: if (reason != -1) sl@0: BIO_printf(out, "\tReason: %s\n", sl@0: OCSP_crl_reason_str(reason)); sl@0: sl@0: BIO_puts(out, "\tRevocation Time: "); sl@0: ASN1_GENERALIZEDTIME_print(out, rev); sl@0: BIO_puts(out, "\n"); sl@0: } sl@0: sl@0: return 1; sl@0: } sl@0: sl@0: sl@0: static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, CA_DB *db, sl@0: X509 *ca, X509 *rcert, EVP_PKEY *rkey, sl@0: STACK_OF(X509) *rother, unsigned long flags, sl@0: int nmin, int ndays) sl@0: { sl@0: ASN1_TIME *thisupd = NULL, *nextupd = NULL; sl@0: OCSP_CERTID *cid, *ca_id = NULL; sl@0: OCSP_BASICRESP *bs = NULL; sl@0: int i, id_count, ret = 1; sl@0: sl@0: sl@0: id_count = OCSP_request_onereq_count(req); sl@0: sl@0: if (id_count <= 0) sl@0: { sl@0: *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); sl@0: goto end; sl@0: } sl@0: sl@0: ca_id = OCSP_cert_to_id(EVP_sha1(), NULL, ca); sl@0: sl@0: bs = OCSP_BASICRESP_new(); sl@0: thisupd = X509_gmtime_adj(NULL, 0); sl@0: if (ndays != -1) sl@0: nextupd = X509_gmtime_adj(NULL, nmin * 60 + ndays * 3600 * 24 ); sl@0: sl@0: /* Examine each certificate id in the request */ sl@0: for (i = 0; i < id_count; i++) sl@0: { sl@0: OCSP_ONEREQ *one; sl@0: ASN1_INTEGER *serial; sl@0: char **inf; sl@0: one = OCSP_request_onereq_get0(req, i); sl@0: cid = OCSP_onereq_get0_id(one); sl@0: /* Is this request about our CA? */ sl@0: if (OCSP_id_issuer_cmp(ca_id, cid)) sl@0: { sl@0: OCSP_basic_add1_status(bs, cid, sl@0: V_OCSP_CERTSTATUS_UNKNOWN, sl@0: 0, NULL, sl@0: thisupd, nextupd); sl@0: continue; sl@0: } sl@0: OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); sl@0: inf = lookup_serial(db, serial); sl@0: if (!inf) sl@0: OCSP_basic_add1_status(bs, cid, sl@0: V_OCSP_CERTSTATUS_UNKNOWN, sl@0: 0, NULL, sl@0: thisupd, nextupd); sl@0: else if (inf[DB_type][0] == DB_TYPE_VAL) sl@0: OCSP_basic_add1_status(bs, cid, sl@0: V_OCSP_CERTSTATUS_GOOD, sl@0: 0, NULL, sl@0: thisupd, nextupd); sl@0: else if (inf[DB_type][0] == DB_TYPE_REV) sl@0: { sl@0: ASN1_OBJECT *inst = NULL; sl@0: ASN1_TIME *revtm = NULL; sl@0: ASN1_GENERALIZEDTIME *invtm = NULL; sl@0: OCSP_SINGLERESP *single; sl@0: int reason = -1; sl@0: unpack_revinfo(&revtm, &reason, &inst, &invtm, inf[DB_rev_date]); sl@0: single = OCSP_basic_add1_status(bs, cid, sl@0: V_OCSP_CERTSTATUS_REVOKED, sl@0: reason, revtm, sl@0: thisupd, nextupd); sl@0: if (invtm) sl@0: OCSP_SINGLERESP_add1_ext_i2d(single, NID_invalidity_date, invtm, 0, 0); sl@0: else if (inst) sl@0: OCSP_SINGLERESP_add1_ext_i2d(single, NID_hold_instruction_code, inst, 0, 0); sl@0: ASN1_OBJECT_free(inst); sl@0: ASN1_TIME_free(revtm); sl@0: ASN1_GENERALIZEDTIME_free(invtm); sl@0: } sl@0: } sl@0: sl@0: OCSP_copy_nonce(bs, req); sl@0: sl@0: OCSP_basic_sign(bs, rcert, rkey, EVP_sha1(), rother, flags); sl@0: sl@0: *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); sl@0: sl@0: end: sl@0: ASN1_TIME_free(thisupd); sl@0: ASN1_TIME_free(nextupd); sl@0: OCSP_CERTID_free(ca_id); sl@0: OCSP_BASICRESP_free(bs); sl@0: return ret; sl@0: sl@0: } sl@0: sl@0: static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser) sl@0: { sl@0: int i; sl@0: BIGNUM *bn = NULL; sl@0: char *itmp, *row[DB_NUMBER],**rrow; sl@0: for (i = 0; i < DB_NUMBER; i++) row[i] = NULL; sl@0: bn = ASN1_INTEGER_to_BN(ser,NULL); sl@0: if (BN_is_zero(bn)) sl@0: itmp = BUF_strdup("00"); sl@0: else sl@0: itmp = BN_bn2hex(bn); sl@0: row[DB_serial] = itmp; sl@0: BN_free(bn); sl@0: rrow=TXT_DB_get_by_index(db->db,DB_serial,row); sl@0: OPENSSL_free(itmp); sl@0: return rrow; sl@0: } sl@0: sl@0: /* Quick and dirty OCSP server: read in and parse input request */ sl@0: sl@0: static BIO *init_responder(char *port) sl@0: { sl@0: BIO *acbio = NULL, *bufbio = NULL; sl@0: bufbio = BIO_new(BIO_f_buffer()); sl@0: if (!bufbio) sl@0: goto err; sl@0: #ifndef OPENSSL_NO_SOCK sl@0: acbio = BIO_new_accept(port); sl@0: #else sl@0: BIO_printf(bio_err, "Error setting up accept BIO - sockets not supported.\n"); sl@0: #endif sl@0: if (!acbio) sl@0: goto err; sl@0: BIO_set_accept_bios(acbio, bufbio); sl@0: bufbio = NULL; sl@0: sl@0: if (BIO_do_accept(acbio) <= 0) sl@0: { sl@0: BIO_printf(bio_err, "Error setting up accept BIO\n"); sl@0: ERR_print_errors(bio_err); sl@0: goto err; sl@0: } sl@0: sl@0: return acbio; sl@0: sl@0: err: sl@0: BIO_free_all(acbio); sl@0: BIO_free(bufbio); sl@0: return NULL; sl@0: } sl@0: sl@0: static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, char *port) sl@0: { sl@0: int have_post = 0, len; sl@0: OCSP_REQUEST *req = NULL; sl@0: char inbuf[1024]; sl@0: BIO *cbio = NULL; sl@0: sl@0: if (BIO_do_accept(acbio) <= 0) sl@0: { sl@0: BIO_printf(bio_err, "Error accepting connection\n"); sl@0: ERR_print_errors(bio_err); sl@0: return 0; sl@0: } sl@0: sl@0: cbio = BIO_pop(acbio); sl@0: *pcbio = cbio; sl@0: sl@0: for(;;) sl@0: { sl@0: len = BIO_gets(cbio, inbuf, sizeof inbuf); sl@0: if (len <= 0) sl@0: return 1; sl@0: /* Look for "POST" signalling start of query */ sl@0: if (!have_post) sl@0: { sl@0: if(strncmp(inbuf, "POST", 4)) sl@0: { sl@0: BIO_printf(bio_err, "Invalid request\n"); sl@0: return 1; sl@0: } sl@0: have_post = 1; sl@0: } sl@0: /* Look for end of headers */ sl@0: if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) sl@0: break; sl@0: } sl@0: sl@0: /* Try to read OCSP request */ sl@0: sl@0: req = d2i_OCSP_REQUEST_bio(cbio, NULL); sl@0: sl@0: if (!req) sl@0: { sl@0: BIO_printf(bio_err, "Error parsing OCSP request\n"); sl@0: ERR_print_errors(bio_err); sl@0: } sl@0: sl@0: *preq = req; sl@0: sl@0: return 1; sl@0: sl@0: } sl@0: sl@0: static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) sl@0: { sl@0: char http_resp[] = sl@0: "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" sl@0: "Content-Length: %d\r\n\r\n"; sl@0: if (!cbio) sl@0: return 0; sl@0: BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); sl@0: i2d_OCSP_RESPONSE_bio(cbio, resp); sl@0: (void)BIO_flush(cbio); sl@0: return 1; sl@0: } sl@0: sl@0: #endif