/* t-ocsp.c - Basic tests for the OCSP subsystem.
* Copyright (C) 2003 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* KSBA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "../src/ksba.h"
#include "t-common.h"
#include "oidtranstbl.h"
int verbose;
int debug;
int no_nonce;
/* Return the description for OID; if no description is available
NULL is returned. */
static const char *
get_oid_desc (const char *oid)
{
int i;
if (oid)
for (i=0; oidtranstbl[i].oid; i++)
if (!strcmp (oidtranstbl[i].oid, oid))
return oidtranstbl[i].desc;
return NULL;
}
static unsigned char *
read_file (const char *fname, size_t *r_length)
{
FILE *fp;
struct stat st;
char *buf;
size_t buflen;
fp = fopen (fname, "rb");
if (!fp)
{
fprintf (stderr, "can't open `%s': %s\n", fname, strerror (errno));
return NULL;
}
if (fstat (fileno(fp), &st))
{
fprintf (stderr, "can't stat `%s': %s\n", fname, strerror (errno));
fclose (fp);
return NULL;
}
buflen = st.st_size;
buf = xmalloc (buflen+1);
if (fread (buf, buflen, 1, fp) != 1)
{
fprintf (stderr, "error reading `%s': %s\n", fname, strerror (errno));
fclose (fp);
xfree (buf);
return NULL;
}
fclose (fp);
*r_length = buflen;
return buf;
}
ksba_cert_t
get_one_cert (const char *fname)
{
gpg_error_t err;
FILE *fp;
ksba_reader_t r;
ksba_cert_t cert;
fp = fopen (fname, "r");
if (!fp)
{
fprintf (stderr, "%s:%d: can't open `%s': %s\n",
__FILE__, __LINE__, fname, strerror (errno));
exit (1);
}
err = ksba_reader_new (&r);
if (err)
fail_if_err (err);
err = ksba_reader_set_file (r, fp);
fail_if_err (err);
err = ksba_cert_new (&cert);
if (err)
fail_if_err (err);
err = ksba_cert_read_der (cert, r);
fail_if_err2 (fname, err);
return cert;
}
/* Create a request for the DER encoded certificate in the file
CERT_FNAME and its issuer's certificate in the file
ISSUER_CERT_FNAME. */
void
one_request (const char *cert_fname, const char *issuer_cert_fname)
{
gpg_error_t err;
ksba_cert_t cert = get_one_cert (cert_fname);
ksba_cert_t issuer_cert = get_one_cert (issuer_cert_fname);
ksba_ocsp_t ocsp;
unsigned char *request;
size_t requestlen;
err = ksba_ocsp_new (&ocsp);
fail_if_err (err);
err = ksba_ocsp_add_target (ocsp, cert, issuer_cert);
fail_if_err (err);
ksba_cert_release (cert);
ksba_cert_release (issuer_cert);
if (!no_nonce)
ksba_ocsp_set_nonce (ocsp, "ABCDEFGHIJKLMNOP", 16);
err = ksba_ocsp_build_request (ocsp, &request, &requestlen);
fail_if_err (err);
ksba_ocsp_release (ocsp);
printf ("OCSP request of length %u created\n", (unsigned int)requestlen);
{
FILE *fp = fopen ("a.req", "wb");
if (!fp)
fail ("can't create output file `a.req'");
if (fwrite (request, requestlen, 1, fp) != 1)
fail ("can't write output");
fclose (fp);
}
xfree (request);
}
void
one_response (const char *cert_fname, const char *issuer_cert_fname,
char *response_fname)
{
gpg_error_t err;
ksba_ocsp_t ocsp;
unsigned char *request, *response;
size_t requestlen, responselen;
ksba_cert_t cert = get_one_cert (cert_fname);
ksba_cert_t issuer_cert = get_one_cert (issuer_cert_fname);
ksba_ocsp_response_status_t response_status;
const char *t;
err = ksba_ocsp_new (&ocsp);
fail_if_err (err);
/* We need to build a request, so that the context is properly
prepared for the response. */
err = ksba_ocsp_add_target (ocsp, cert, issuer_cert);
fail_if_err (err);
ksba_cert_release (issuer_cert);
if (!no_nonce)
ksba_ocsp_set_nonce (ocsp, "ABCDEFGHIJKLMNOP", 16);
err = ksba_ocsp_build_request (ocsp, &request, &requestlen);
fail_if_err (err);
xfree (request);
/* Now for the response. */
response = read_file (response_fname, &responselen);
if (!response)
fail ("file error");
err = ksba_ocsp_parse_response (ocsp, response, responselen,
&response_status);
fail_if_err (err);
switch (response_status)
{
case KSBA_OCSP_RSPSTATUS_SUCCESS: t = "success"; break;
case KSBA_OCSP_RSPSTATUS_MALFORMED: t = "malformed"; break;
case KSBA_OCSP_RSPSTATUS_INTERNAL: t = "internal error"; break;
case KSBA_OCSP_RSPSTATUS_TRYLATER: t = "try later"; break;
case KSBA_OCSP_RSPSTATUS_SIGREQUIRED: t = "must sign request"; break;
case KSBA_OCSP_RSPSTATUS_UNAUTHORIZED: t = "unauthorized"; break;
case KSBA_OCSP_RSPSTATUS_REPLAYED: t = "replay detected"; break;
case KSBA_OCSP_RSPSTATUS_OTHER: t = "other (unknown)"; break;
case KSBA_OCSP_RSPSTATUS_NONE: t = "no status"; break;
default: fail ("impossible response_status"); break;
}
printf ("response status ..: %s\n", t);
if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS
|| response_status == KSBA_OCSP_RSPSTATUS_REPLAYED)
{
ksba_status_t status;
ksba_crl_reason_t reason;
ksba_isotime_t this_update, next_update, revocation_time, produced_at;
ksba_sexp_t sigval;
char *name;
ksba_sexp_t keyid;
err = ksba_ocsp_get_responder_id (ocsp, &name, &keyid);
fail_if_err (err);
printf ("responder id .....: ");
if (name)
printf ("`%s'", name);
else
print_sexp (keyid);
putchar ('\n');
ksba_free (name);
ksba_free (keyid);
sigval = ksba_ocsp_get_sig_val (ocsp, produced_at);
printf ("signature value ..: ");
print_sexp (sigval);
printf ("\nproduced at ......: ");
print_time (produced_at);
putchar ('\n');
err = ksba_ocsp_get_status (ocsp, cert,
&status, this_update, next_update,
revocation_time, &reason);
fail_if_err (err);
printf ("certificate status: %s\n",
status == KSBA_STATUS_GOOD? "good":
status == KSBA_STATUS_REVOKED? "revoked":
status == KSBA_STATUS_UNKNOWN? "unknown":
status == KSBA_STATUS_NONE? "none": "?");
if (status == KSBA_STATUS_REVOKED)
{
printf ("revocation time ..: ");
print_time (revocation_time);
printf ("\nrevocation reason : %s\n",
reason == KSBA_CRLREASON_UNSPECIFIED? "unspecified":
reason == KSBA_CRLREASON_KEY_COMPROMISE? "key compromise":
reason == KSBA_CRLREASON_CA_COMPROMISE? "CA compromise":
reason == KSBA_CRLREASON_AFFILIATION_CHANGED?
"affiliation changed":
reason == KSBA_CRLREASON_SUPERSEDED? "superseeded":
reason == KSBA_CRLREASON_CESSATION_OF_OPERATION?
"cessation of operation":
reason == KSBA_CRLREASON_CERTIFICATE_HOLD?
"certificate on hold":
reason == KSBA_CRLREASON_REMOVE_FROM_CRL? "removed from CRL":
reason == KSBA_CRLREASON_PRIVILEGE_WITHDRAWN?
"privilege withdrawn":
reason == KSBA_CRLREASON_AA_COMPROMISE? "AA compromise":
reason == KSBA_CRLREASON_OTHER? "other":"?");
}
printf ("this update ......: ");
print_time (this_update);
printf ("\nnext update ......: ");
print_time (next_update);
putchar ('\n');
{
int cert_idx;
ksba_cert_t acert;
for (cert_idx=0; (acert = ksba_ocsp_get_cert (ocsp, cert_idx));
cert_idx++)
ksba_cert_release (acert);
printf ("extra certificates: %d\n", cert_idx );
}
{
int idx, crit;
const char *oid;
const unsigned char *der;
size_t derlen;
for (idx=0; !(err=ksba_ocsp_get_extension (ocsp, NULL, idx,
&oid, &crit,
&der, &derlen)); idx++)
{
const char *s = get_oid_desc (oid);
printf ("%sresp-extn ..%s: %s%s%s%s (",
crit? "crit. ":"",
crit?"":"......",
s?"(":"", s?s:"", s?") ":"", oid);
print_hex (der, derlen);
putchar (')');
putchar ('\n');
}
if (err && gpg_err_code (err) != GPG_ERR_EOF)
fail_if_err (err);
for (idx=0; !(err=ksba_ocsp_get_extension (ocsp, cert, idx,
&oid, &crit,
&der, &derlen)); idx++)
{
const char *s = get_oid_desc (oid);
printf ("%ssngl-extn ..%s: %s%s%s%s (",
crit? "crit. ":"",
crit?"":"......",
s?"(":"", s?s:"", s?") ":"", oid);
print_hex (der, derlen);
putchar (')');
putchar ('\n');
}
if (err && gpg_err_code (err) != GPG_ERR_EOF)
fail_if_err (err);
}
}
ksba_cert_release (cert);
ksba_ocsp_release (ocsp);
xfree (response);
}
static gpg_error_t
my_hash_buffer (void *arg, const char *oid,
const void *buffer, size_t length, size_t resultsize,
unsigned char *result, size_t *resultlen)
{
(void)arg; /* Not used. */
if (oid && strcmp (oid, "1.3.14.3.2.26"))
return gpg_error (GPG_ERR_NOT_SUPPORTED); /* We only support SHA-1. */
if (resultsize < 20)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
sha1_hash_buffer (result, buffer, length);
*resultlen = 20;
return 0;
}
/* ( printf "POST / HTTP/1.0\r\nContent-Type: application/ocsp-request\r\nContent-Length: `wc -c a.rsp
Openvalidation test reponders:
Port: 80 Standard configuration. OCSP Responder will accept
all proper requests and send a signed response.
Port: 8080 Response does not contain any attached certificates.
Client must accept this response
Port: 8081 Never replies nonce. Insecure but standard conform mode.
Client application should warn in case of replay-attacks.
Port: 8082 The OCSP Responder will sign the response with randomized
bytecode. Client should NOT accept this response.
Port: 8083 OCSP response will always be revoked.
Port: 8084 OCSP response will always be unknown.
Port: 8085 OCSP response will always be malformed.
Port: 8086 OCSP response will always be internal error.
Port: 8087 OCSP response will always be try later.
Port: 8088 OCSP response will always be signature required.
Port: 8089 OCSP response will always be unauth.
Port: 8090 Standard configuration with full Debuglogs. Access the
logs at http://www.openvalidation.org/en/test/logs.html
*/
int
main (int argc, char **argv)
{
int last_argc = -1;
int response_mode = 0;
const char *srcdir = getenv ("srcdir");
if (!srcdir)
srcdir = ".";
ksba_set_hash_buffer_function (my_hash_buffer, NULL);
if (argc)
{
argc--; argv++;
}
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--help"))
{
puts (
"usage: ./t-ocsp [options] {CERTFILE ISSUERCERTFILE}\n"
" ./t-ocsp [options] --response {CERTFILE ISSUERCERTFILE RESPONSEFILE}\n"
"\n"
" Options are --verbose and --debug");
exit (0);
}
if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose = debug = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--response"))
{
response_mode = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-nonce"))
{
no_nonce = 1;
argc--; argv++;
}
}
if (response_mode)
{
for ( ; argc > 2; argc -=3, argv += 3)
one_response (*argv, argv[1], argv[2]);
if (argc)
fputs ("warning: extra argument ignored\n", stderr);
}
else if (argc)
{
for ( ; argc > 1; argc -=2, argv += 2)
one_request (*argv, argv[1]);
if (argc)
fputs ("warning: extra argument ignored\n", stderr);
}
else
{
struct {
const char *cert_fname;
const char *issuer_cert_fname;
const char *response_fname;
} files[] = {
{ "samples/ov-userrev.crt", "samples/ov-root-ca-cert.crt", NULL },
{ NULL }
};
int idx;
for (idx=0; files[idx].cert_fname; idx++)
{
char *f1, *f2;
f1 = prepend_srcdir (files[idx].cert_fname);
f2 = prepend_srcdir (files[idx].issuer_cert_fname);
one_request (f1, f2);
xfree (f2);
xfree (f1);
}
}
return 0;
}