New function OCSP_parse_url() and -url option for ocsp utility.
[openssl.git] / apps / ocsp.c
index 09357ae09871e5bf0ec1f3203d70b5d8a6d2f554..a2501c6093ffe021b92cfe83621bf70e4243aed4 100644 (file)
 #include <openssl/err.h>
 #include "apps.h"
 
-static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer);
-static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer);
+static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer,
+                               STACK_OF(OCSP_CERTID) *ids);
+static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
+                               STACK_OF(OCSP_CERTID) *ids);
+static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
+                               STACK *names, STACK_OF(OCSP_CERTID) *ids);
 
 #undef PROG
 #define PROG ocsp_main
@@ -74,25 +78,36 @@ int MAIN(int, char **);
 int MAIN(int argc, char **argv)
        {
        char **args;
-       char *host = NULL, *path = "/";
+       char *host = NULL, *port = NULL, *path = "/";
        char *reqin = NULL, *respin = NULL;
        char *reqout = NULL, *respout = NULL;
        char *signfile = NULL, *keyfile = NULL;
        char *outfile = NULL;
-       int add_nonce = 1;
+       int add_nonce = 1, noverify = 0, use_ssl = -1;
        OCSP_REQUEST *req = NULL;
        OCSP_RESPONSE *resp = NULL;
+       OCSP_BASICRESP *bs = NULL;
        X509 *issuer = NULL, *cert = NULL;
        X509 *signer = NULL;
        EVP_PKEY *key = NULL;
        BIO *cbio = NULL, *derbio = NULL;
        BIO *out = NULL;
        int req_text = 0, resp_text = 0;
+       char *CAfile = NULL, *CApath = NULL;
+       X509_STORE *store = NULL;
+       STACK_OF(X509) *sign_other = NULL, *verify_other = NULL;
+       char *sign_certfile = NULL, *verify_certfile = NULL;
+       unsigned long sign_flags = 0, verify_flags = 0;
        int ret = 1;
        int badarg = 0;
+       int i;
+       STACK *reqnames = NULL;
+       STACK_OF(OCSP_CERTID) *ids = NULL;
        if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
        ERR_load_crypto_strings();
        args = argv + 1;
+       reqnames = sk_new_null();
+       ids = sk_OCSP_CERTID_new_null();
        while (!badarg && *args && *args[0] == '-')
                {
                if (!strcmp(*args, "-out"))
@@ -104,6 +119,19 @@ int MAIN(int argc, char **argv)
                                }
                        else badarg = 1;
                        }
+               else if (!strcmp(*args, "-url"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               if (!OCSP_parse_url(*args, &host, &port, &path, &use_ssl))
+                                       {
+                                       BIO_printf(bio_err, "Error parsing URL\n");
+                                       badarg = 1;
+                                       }
+                               }
+                       else badarg = 1;
+                       }
                else if (!strcmp(*args, "-host"))
                        {
                        if (args[1])
@@ -113,10 +141,28 @@ int MAIN(int argc, char **argv)
                                }
                        else badarg = 1;
                        }
+               else if (!strcmp(*args, "-noverify"))
+                       noverify = 1;
                else if (!strcmp(*args, "-nonce"))
                        add_nonce = 2;
                else if (!strcmp(*args, "-no_nonce"))
                        add_nonce = 0;
+               else if (!strcmp(*args, "-no_certs"))
+                       sign_flags |= OCSP_NOCERTS;
+               else if (!strcmp(*args, "-no_signature_verify"))
+                       verify_flags |= OCSP_NOSIGS;
+               else if (!strcmp(*args, "-no_cert_verify"))
+                       verify_flags |= OCSP_NOVERIFY;
+               else if (!strcmp(*args, "-no_chain"))
+                       verify_flags |= OCSP_NOCHAIN;
+               else if (!strcmp(*args, "-no_cert_checks"))
+                       verify_flags |= OCSP_NOCHECKS;
+               else if (!strcmp(*args, "-no_explicit"))
+                       verify_flags |= OCSP_NOEXPLICIT;
+               else if (!strcmp(*args, "-trust_other"))
+                       verify_flags |= OCSP_TRUSTOTHER;
+               else if (!strcmp(*args, "-no_intern"))
+                       verify_flags |= OCSP_NOINTERN;
                else if (!strcmp(*args, "-text"))
                        {
                        req_text = 1;
@@ -153,6 +199,52 @@ int MAIN(int argc, char **argv)
                                }
                        else badarg = 1;
                        }
+               else if (!strcmp (*args, "-VAfile"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               verify_certfile = *args;
+                               verify_flags |= OCSP_TRUSTOTHER;
+                               }
+                       else badarg = 1;
+                       }
+               else if (!strcmp(*args, "-sign_other"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               sign_certfile = *args;
+                               }
+                       else badarg = 1;
+                       }
+               else if (!strcmp(*args, "-verify_other"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               verify_certfile = *args;
+                               }
+                       else badarg = 1;
+                       }
+               else if (!strcmp (*args, "-CAfile"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               CAfile = *args;
+                               }
+                       else badarg = 1;
+                       }
+               else if (!strcmp (*args, "-CApath"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               CApath = *args;
+                               }
+                       else badarg = 1;
+                       }
                 else if (!strcmp(*args, "-signkey"))
                        {
                        if (args[1])
@@ -208,7 +300,9 @@ int MAIN(int argc, char **argv)
                                X509_free(cert);
                                cert = load_cert(bio_err, *args, FORMAT_PEM);
                                if(!cert) goto end;
-                               if(!add_ocsp_cert(&req, cert, issuer))
+                               if(!add_ocsp_cert(&req, cert, issuer, ids))
+                                       goto end;
+                               if(!sk_push(reqnames, *args))
                                        goto end;
                                }
                        else badarg = 1;
@@ -218,7 +312,9 @@ int MAIN(int argc, char **argv)
                        if (args[1])
                                {
                                args++;
-                               if(!add_ocsp_serial(&req, *args, issuer))
+                               if(!add_ocsp_serial(&req, *args, issuer, ids))
+                                       goto end;
+                               if(!sk_push(reqnames, *args))
                                        goto end;
                                }
                        else badarg = 1;
@@ -235,20 +331,37 @@ int MAIN(int argc, char **argv)
                BIO_printf (bio_err, "OCSP utility\n");
                BIO_printf (bio_err, "Usage ocsp [options]\n");
                BIO_printf (bio_err, "where options are\n");
-               BIO_printf (bio_err, "-issuer file  issuer certificate\n");
-               BIO_printf (bio_err, "-cert file    certificate to check\n");
-               BIO_printf (bio_err, "-serial n     serial number to check\n");
-               BIO_printf (bio_err, "-req_text     print text form of request\n");
-               BIO_printf (bio_err, "-resp_text    print text form of response\n");
-               BIO_printf (bio_err, "-text         print text form of request and response\n");
-               BIO_printf (bio_err, "-reqout file  write DER encoded OCSP request to \"file\"\n");
-               BIO_printf (bio_err, "-respout file write DER encoded OCSP reponse to \"file\"\n");
-               BIO_printf (bio_err, "-reqin file   read DER encoded OCSP request from \"file\"\n");
-               BIO_printf (bio_err, "-respin file  read DER encoded OCSP reponse from \"file\"\n");
-               BIO_printf (bio_err, "-nonce        add OCSP nonce to request\n");
-               BIO_printf (bio_err, "-no_nonce     don't add OCSP nonce to request\n");
-               BIO_printf (bio_err, "-host host:n  send OCSP request to host on port n\n");
-               BIO_printf (bio_err, "-path         path to use in OCSP request\n");
+               BIO_printf (bio_err, "-out file          output filename\n");
+               BIO_printf (bio_err, "-issuer file       issuer certificate\n");
+               BIO_printf (bio_err, "-cert file         certificate to check\n");
+               BIO_printf (bio_err, "-serial n          serial number to check\n");
+               BIO_printf (bio_err, "-signer file       certificate to sign OCSP request with\n");
+               BIO_printf (bio_err, "-signkey file      private key to sign OCSP request with\n");
+               BIO_printf (bio_err, "-sign_certs file   additional certificates to include in signed request\n");
+               BIO_printf (bio_err, "-no_certs          don't include any certificates in signed request\n");
+               BIO_printf (bio_err, "-req_text          print text form of request\n");
+               BIO_printf (bio_err, "-resp_text         print text form of response\n");
+               BIO_printf (bio_err, "-text              print text form of request and response\n");
+               BIO_printf (bio_err, "-reqout file       write DER encoded OCSP request to \"file\"\n");
+               BIO_printf (bio_err, "-respout file      write DER encoded OCSP reponse to \"file\"\n");
+               BIO_printf (bio_err, "-reqin file        read DER encoded OCSP request from \"file\"\n");
+               BIO_printf (bio_err, "-respin file       read DER encoded OCSP reponse from \"file\"\n");
+               BIO_printf (bio_err, "-nonce             add OCSP nonce to request\n");
+               BIO_printf (bio_err, "-no_nonce          don't add OCSP nonce to request\n");
+               BIO_printf (bio_err, "-url URL           OCSP responder URL\n");
+               BIO_printf (bio_err, "-host host:n       send OCSP request to host on port n\n");
+               BIO_printf (bio_err, "-path              path to use in OCSP request\n");
+               BIO_printf (bio_err, "-CApath dir        trusted certificates directory\n");
+               BIO_printf (bio_err, "-CAfile file       trusted certificates file\n");
+               BIO_printf (bio_err, "-VAfile file       validator certificates file\n");
+               BIO_printf (bio_err, "-noverify          don't verify response at all\n");
+               BIO_printf (bio_err, "-verify_certs file additional certificates to search for signer\n");
+               BIO_printf (bio_err, "-trust_other       don't verify additional certificates\n");
+               BIO_printf (bio_err, "-no_intern         don't search certificates contained in response for signer\n");
+               BIO_printf (bio_err, "-no_sig_verify     don't check signature on response\n");
+               BIO_printf (bio_err, "-no_cert_verify    don't check signing certificate\n");
+               BIO_printf (bio_err, "-no_chain          don't chain verify response\n");
+               BIO_printf (bio_err, "-no_cert_checks    don't do additional checks on signing certificate\n");
                goto end;
                }
 
@@ -286,7 +399,7 @@ int MAIN(int argc, char **argv)
                goto end;
                }
 
-       if (req) OCSP_request_add1_nonce(req, NULL, -1);
+       if (req && add_nonce) OCSP_request_add1_nonce(req, NULL, -1);
 
        if (signfile)
                {
@@ -297,13 +410,18 @@ int MAIN(int argc, char **argv)
                        BIO_printf(bio_err, "Error loading signer certificate\n");
                        goto end;
                        }
+               if (sign_certfile)
+                       {
+                       sign_other = load_certs(bio_err, sign_certfile, FORMAT_PEM);
+                       if (!sign_other) goto end;
+                       }
                key = load_key(bio_err, keyfile, FORMAT_PEM, NULL, NULL);
                if (!key)
                        {
                        BIO_printf(bio_err, "Error loading signer private key\n");
                        goto end;
                        }
-               if (!OCSP_request_sign(req, signer, key, EVP_sha1(), NULL, 0))
+               if (!OCSP_request_sign(req, signer, key, EVP_sha1(), sign_other, sign_flags))
                        {
                        BIO_printf(bio_err, "Error signing OCSP request\n");
                        goto end;
@@ -332,6 +450,7 @@ int MAIN(int argc, char **argv)
                        BIO_printf(bio_err, "Error creating connect BIO\n");
                        goto end;
                        }
+               if (port) BIO_set_conn_port(cbio, port);
                if (BIO_do_connect(cbio) <= 0)
                        {
                        BIO_printf(bio_err, "Error connecting BIO\n");
@@ -381,13 +500,69 @@ int MAIN(int argc, char **argv)
                BIO_free(derbio);
                }
 
+       i = OCSP_response_status(resp);
+
+       if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+               {
+               BIO_printf(out, "Responder Error: %s (%ld)\n",
+                               OCSP_response_status_str(i), i);
+               ret = 0;
+               goto end;
+               }
+
        if (resp_text) OCSP_RESPONSE_print(out, resp, 0);
 
+       store = setup_verify(bio_err, CAfile, CApath);
+       if(!store) goto end;
+       if (verify_certfile)
+               {
+               verify_other = load_certs(bio_err, verify_certfile, FORMAT_PEM);
+               if (!verify_other) goto end;
+               }
+
+       bs = OCSP_response_get1_basic(resp);
+
+       if (!bs)
+               {
+               BIO_printf(bio_err, "Error parsing response\n");
+               goto end;
+               }
+
+       if (!noverify)
+               {
+               if (req && ((i = OCSP_check_nonce(req, bs)) <= 0))
+                       {
+                       if (i == -1)
+                               BIO_printf(bio_err, "WARNING: no nonce in response\n");
+                       else
+                               {
+                               BIO_printf(bio_err, "Nonce Verify error\n");
+                               goto end;
+                               }
+                       }
+
+               i = OCSP_basic_verify(bs, verify_other, store, verify_flags);
+                if (i < 0) i = OCSP_basic_verify(bs, NULL, store, 0);
+
+               if(i <= 0)
+                       {
+                       BIO_printf(bio_err, "Response Verify Failure\n", i);
+                       ERR_print_errors(bio_err);
+                       }
+               else
+                       BIO_printf(bio_err, "Response verify OK\n");
+
+               }
+
+       if (!print_ocsp_summary(out, bs, req, reqnames, ids))
+               goto end;
+
        ret = 0;
 
 end:
        ERR_print_errors(bio_err);
        X509_free(signer);
+       X509_STORE_free(store);
        EVP_PKEY_free(key);
        X509_free(issuer);
        X509_free(cert);
@@ -395,11 +570,24 @@ end:
        BIO_free(out);
        OCSP_REQUEST_free(req);
        OCSP_RESPONSE_free(resp);
+       OCSP_BASICRESP_free(bs);
+       sk_free(reqnames);
+       sk_OCSP_CERTID_free(ids);
+       sk_X509_pop_free(sign_other, X509_free);
+       sk_X509_pop_free(verify_other, X509_free);
+
+       if (use_ssl != -1)
+               {
+               OPENSSL_free(host);
+               OPENSSL_free(port);
+               OPENSSL_free(path);
+               }
 
        EXIT(ret);
 }
 
-static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer)
+static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer,
+                               STACK_OF(OCSP_CERTID) *ids)
        {
        OCSP_CERTID *id;
        if(!issuer)
@@ -410,7 +598,7 @@ static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer)
        if(!*req) *req = OCSP_REQUEST_new();
        if(!*req) goto err;
        id = OCSP_cert_to_id(NULL, cert, issuer);
-       if(!id) goto err;
+       if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err;
        if(!OCSP_request_add0_id(*req, id)) goto err;
        return 1;
 
@@ -419,7 +607,8 @@ static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer)
        return 0;
        }
 
-static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer)
+static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
+                               STACK_OF(OCSP_CERTID) *ids)
        {
        OCSP_CERTID *id;
        X509_NAME *iname;
@@ -433,7 +622,7 @@ static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer)
        if(!*req) *req = OCSP_REQUEST_new();
        if(!*req) goto err;
        iname = X509_get_subject_name(issuer);
-       ikey = issuer->cert_info->key->public_key;
+       ikey = X509_get0_pubkey_bitstr(issuer);
        sno = s2i_ASN1_INTEGER(NULL, serial);
        if(!sno)
                {
@@ -442,7 +631,7 @@ static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer)
                }
        id = OCSP_cert_id_new(EVP_sha1(), iname, ikey, sno);
        ASN1_INTEGER_free(sno);
-       if(!id) goto err;
+       if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err;
        if(!OCSP_request_add0_id(*req, id)) goto err;
        return 1;
 
@@ -450,3 +639,58 @@ static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer)
        BIO_printf(bio_err, "Error Creating OCSP request\n");
        return 0;
        }
+
+static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
+                                       STACK *names, STACK_OF(OCSP_CERTID) *ids)
+       {
+       OCSP_CERTID *id;
+       char *name;
+       int i;
+
+       int status, reason;
+
+       ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+
+       if (!bs || !req || !sk_num(names) || !sk_OCSP_CERTID_num(ids))
+               return 1;
+
+       for (i = 0; i < sk_OCSP_CERTID_num(ids); i++)
+               {
+               id = sk_OCSP_CERTID_value(ids, i);
+               name = sk_value(names, i);
+               BIO_printf(out, "%s: ", name);
+
+               if(!OCSP_resp_find_status(bs, id, &status, &reason,
+                                       &rev, &thisupd, &nextupd))
+                       {
+                       BIO_puts(out, "ERROR: No Status found.\n");
+                       continue;
+                       }
+               BIO_printf(out, "%s\n", OCSP_cert_status_str(status));
+
+               BIO_puts(out, "\tThis Update: ");
+               ASN1_GENERALIZEDTIME_print(out, thisupd);
+               BIO_puts(out, "\n");
+
+               if(nextupd)
+                       {
+                       BIO_puts(out, "\tNext Update: ");
+                       ASN1_GENERALIZEDTIME_print(out, thisupd);
+                       BIO_puts(out, "\n");
+                       }
+
+               if (status != V_OCSP_CERTSTATUS_REVOKED)
+                       continue;
+
+               if (reason != -1)
+                       BIO_printf(out, "\tReason: %s\n",
+                               OCSP_crl_reason_str(reason));
+
+               BIO_puts(out, "\tRevocation Time: ");
+               ASN1_GENERALIZEDTIME_print(out, rev);
+               BIO_puts(out, "\n");
+               }
+
+       return 1;
+       }
+