/*
* Copyright (C) 2011-2012 Free Software Foundation, Inc.
* Author: Simon Josefsson
*
* This file is part of GnuTLS.
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
/* Online Certificate Status Protocol - RFC 2560
*/
#include
#include
#include
#include
#include
#include "algorithms.h"
#include
/* I18n of error codes. */
#include "gettext.h"
#define _(String) dgettext (PACKAGE, String)
#define addf _gnutls_buffer_append_printf
#define adds _gnutls_buffer_append_str
static void
print_req (gnutls_buffer_st * str, gnutls_ocsp_req_t req)
{
int ret;
unsigned indx;
/* Version. */
{
int version = gnutls_ocsp_req_get_version (req);
if (version < 0)
addf (str, "error: get_version: %s\n", gnutls_strerror (version));
else
addf (str, _("\tVersion: %d\n"), version);
}
/* XXX requestorName */
/* requestList */
addf (str, "\tRequest List:\n");
for (indx = 0; ; indx++)
{
gnutls_digest_algorithm_t digest;
gnutls_datum_t in, ik, sn;
ret = gnutls_ocsp_req_get_cert_id (req, indx, &digest, &in, &ik, &sn);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
break;
addf (str, "\t\tCertificate ID:\n");
if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_cert_id: %s\n",
gnutls_strerror (ret));
continue;
}
addf (str, "\t\t\tHash Algorithm: %s\n",
_gnutls_digest_get_name (mac_to_entry(digest)));
adds (str, "\t\t\tIssuer Name Hash: ");
_gnutls_buffer_hexprint (str, in.data, in.size);
adds (str, "\n");
adds (str, "\t\t\tIssuer Key Hash: ");
_gnutls_buffer_hexprint (str, ik.data, ik.size);
adds (str, "\n");
adds (str, "\t\t\tSerial Number: ");
_gnutls_buffer_hexprint (str, sn.data, sn.size);
adds (str, "\n");
gnutls_free (in.data);
gnutls_free (ik.data);
gnutls_free (sn.data);
/* XXX singleRequestExtensions */
}
for (indx = 0; ; indx++)
{
gnutls_datum_t oid;
unsigned int critical;
gnutls_datum_t data;
ret = gnutls_ocsp_req_get_extension (req, indx, &oid, &critical, &data);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
break;
else if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_extension: %s\n",
gnutls_strerror (ret));
continue;
}
if (indx == 0)
adds (str, "\tExtensions:\n");
if (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0)
{
gnutls_datum_t nonce;
unsigned int critical;
ret = gnutls_ocsp_req_get_nonce (req, &critical, &nonce);
if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_nonce: %s\n",
gnutls_strerror (ret));
}
else
{
addf (str, "\t\tNonce%s: ", critical ? " (critical)" : "");
_gnutls_buffer_hexprint (str, nonce.data, nonce.size);
adds (str, "\n");
gnutls_free (nonce.data);
}
}
else
{
addf (str, "\t\tUnknown extension %s (%s):\n", oid.data,
critical ? "critical" : "not critical");
adds (str, _("\t\t\tASCII: "));
_gnutls_buffer_asciiprint (str, (char*)data.data, data.size);
addf (str, "\n");
adds (str, _("\t\t\tHexdump: "));
_gnutls_buffer_hexprint (str, (char*)data.data, data.size);
adds (str, "\n");
}
gnutls_free (oid.data);
gnutls_free (data.data);
}
/* XXX Signature */
}
/**
* gnutls_ocsp_req_print:
* @req: The structure to be printed
* @format: Indicate the format to use
* @out: Newly allocated datum with (0) terminated string.
*
* This function will pretty print a OCSP request, suitable for
* display to a human.
*
* If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the
* request will be output, on multiple lines.
*
* The output @out->data needs to be deallocate using gnutls_free().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
**/
int
gnutls_ocsp_req_print (gnutls_ocsp_req_t req,
gnutls_ocsp_print_formats_t format,
gnutls_datum_t * out)
{
gnutls_buffer_st str;
int rc;
if (format != GNUTLS_OCSP_PRINT_FULL)
{
gnutls_assert ();
return GNUTLS_E_INVALID_REQUEST;
}
_gnutls_buffer_init (&str);
_gnutls_buffer_append_str (&str, _("OCSP Request Information:\n"));
print_req (&str, req);
_gnutls_buffer_append_data (&str, "\0", 1);
rc = _gnutls_buffer_to_datum (&str, out);
if (rc != GNUTLS_E_SUCCESS)
{
gnutls_assert ();
return rc;
}
return GNUTLS_E_SUCCESS;
}
static void
print_resp (gnutls_buffer_st * str, gnutls_ocsp_resp_t resp,
gnutls_ocsp_print_formats_t format)
{
int ret;
unsigned indx;
ret = gnutls_ocsp_resp_get_status (resp);
if (ret < 0)
{
addf (str, "error: ocsp_resp_get_status: %s\n",
gnutls_strerror (ret));
return;
}
adds (str, "\tResponse Status: ");
switch (ret)
{
case GNUTLS_OCSP_RESP_SUCCESSFUL:
adds (str, "Successful\n");
break;
case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
adds (str, "malformedRequest\n");
return;
case GNUTLS_OCSP_RESP_INTERNALERROR:
adds (str, "internalError\n");
return;
case GNUTLS_OCSP_RESP_TRYLATER:
adds (str, "tryLater\n");
return;
case GNUTLS_OCSP_RESP_SIGREQUIRED:
adds (str, "sigRequired\n");
return;
case GNUTLS_OCSP_RESP_UNAUTHORIZED:
adds (str, "unauthorized\n");
return;
default:
adds (str, "unknown\n");
return;
}
{
gnutls_datum_t oid;
ret = gnutls_ocsp_resp_get_response (resp, &oid, NULL);
if (ret < 0)
{
addf (str, "error: get_response: %s\n", gnutls_strerror (ret));
return;
}
adds (str, "\tResponse Type: ");
#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
if (oid.size == sizeof (OCSP_BASIC)
&& memcmp (oid.data, OCSP_BASIC, oid.size) == 0)
{
adds (str, "Basic OCSP Response\n");
gnutls_free (oid.data);
}
else
{
addf (str, "Unknown response type (%.*s)\n", oid.size, oid.data);
gnutls_free (oid.data);
return;
}
}
/* Version. */
{
int version = gnutls_ocsp_resp_get_version (resp);
if (version < 0)
addf (str, "error: get_version: %s\n", gnutls_strerror (version));
else
addf (str, _("\tVersion: %d\n"), version);
}
/* responderID */
{
gnutls_datum_t dn;
/* XXX byKey */
ret = gnutls_ocsp_resp_get_responder (resp, &dn);
if (ret < 0)
addf (str, "error: get_dn: %s\n", gnutls_strerror (ret));
else
{
addf (str, _("\tResponder ID: %.*s\n"), dn.size, dn.data);
gnutls_free (dn.data);
}
}
{
char s[42];
size_t max = sizeof (s);
struct tm t;
time_t tim = gnutls_ocsp_resp_get_produced (resp);
if (tim == (time_t) -1)
addf (str, "error: ocsp_resp_get_produced\n");
else if (gmtime_r (&tim, &t) == NULL)
addf (str, "error: gmtime_r (%ld)\n", (unsigned long) tim);
else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
addf (str, "error: strftime (%ld)\n", (unsigned long) tim);
else
addf (str, _("\tProduced At: %s\n"), s);
}
addf (str, "\tResponses:\n");
for (indx = 0; ; indx++)
{
gnutls_digest_algorithm_t digest;
gnutls_datum_t in, ik, sn;
unsigned int cert_status;
time_t this_update;
time_t next_update;
time_t revocation_time;
unsigned int revocation_reason;
ret = gnutls_ocsp_resp_get_single (resp,
indx,
&digest, &in, &ik, &sn,
&cert_status,
&this_update,
&next_update,
&revocation_time,
&revocation_reason);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
break;
addf (str, "\t\tCertificate ID:\n");
if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_singleresponse: %s\n",
gnutls_strerror (ret));
continue;
}
addf (str, "\t\t\tHash Algorithm: %s\n",
_gnutls_digest_get_name (mac_to_entry(digest)));
adds (str, "\t\t\tIssuer Name Hash: ");
_gnutls_buffer_hexprint (str, in.data, in.size);
adds (str, "\n");
adds (str, "\t\t\tIssuer Key Hash: ");
_gnutls_buffer_hexprint (str, ik.data, ik.size);
adds (str, "\n");
adds (str, "\t\t\tSerial Number: ");
_gnutls_buffer_hexprint (str, sn.data, sn.size);
adds (str, "\n");
gnutls_free (in.data);
gnutls_free (ik.data);
gnutls_free (sn.data);
{
const char *p = NULL;
switch (cert_status)
{
case GNUTLS_OCSP_CERT_GOOD:
p = "good";
break;
case GNUTLS_OCSP_CERT_REVOKED:
p = "revoked";
break;
case GNUTLS_OCSP_CERT_UNKNOWN:
p = "unknown";
break;
default:
addf (str, "\t\tCertificate Status: unexpected value %d\n",
cert_status);
break;
}
if (p)
addf (str, "\t\tCertificate Status: %s\n", p);
}
/* XXX revocation reason */
if (cert_status == GNUTLS_OCSP_CERT_REVOKED)
{
char s[42];
size_t max = sizeof (s);
struct tm t;
if (revocation_time == (time_t) -1)
addf (str, "error: revocation_time\n");
else if (gmtime_r (&revocation_time, &t) == NULL)
addf (str, "error: gmtime_r (%ld)\n",
(unsigned long) revocation_time);
else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
addf (str, "error: strftime (%ld)\n",
(unsigned long) revocation_time);
else
addf (str, _("\t\tRevocation time: %s\n"), s);
}
{
char s[42];
size_t max = sizeof (s);
struct tm t;
if (this_update == (time_t) -1)
addf (str, "error: this_update\n");
else if (gmtime_r (&this_update, &t) == NULL)
addf (str, "error: gmtime_r (%ld)\n", (unsigned long) this_update);
else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
addf (str, "error: strftime (%ld)\n", (unsigned long) this_update);
else
addf (str, _("\t\tThis Update: %s\n"), s);
}
{
char s[42];
size_t max = sizeof (s);
struct tm t;
if (next_update == (time_t) -1)
addf (str, "error: next_update\n");
else if (gmtime_r (&next_update, &t) == NULL)
addf (str, "error: gmtime_r (%ld)\n", (unsigned long) next_update);
else if (strftime (s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
addf (str, "error: strftime (%ld)\n", (unsigned long) next_update);
else
addf (str, _("\t\tNext Update: %s\n"), s);
}
/* XXX singleRequestExtensions */
}
adds (str, "\tExtensions:\n");
for (indx = 0; ; indx++)
{
gnutls_datum_t oid;
unsigned int critical;
gnutls_datum_t data;
ret = gnutls_ocsp_resp_get_extension (resp, indx, &oid, &critical, &data);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
break;
else if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_extension: %s\n",
gnutls_strerror (ret));
continue;
}
if (memcmp (oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0)
{
gnutls_datum_t nonce;
unsigned int critical;
ret = gnutls_ocsp_resp_get_nonce (resp, &critical, &nonce);
if (ret != GNUTLS_E_SUCCESS)
{
addf (str, "error: get_nonce: %s\n",
gnutls_strerror (ret));
}
else
{
addf (str, "\t\tNonce%s: ", critical ? " (critical)" : "");
_gnutls_buffer_hexprint (str, nonce.data, nonce.size);
adds (str, "\n");
gnutls_free (nonce.data);
}
}
else
{
addf (str, "\t\tUnknown extension %s (%s):\n", oid.data,
critical ? "critical" : "not critical");
adds (str, _("\t\t\tASCII: "));
_gnutls_buffer_asciiprint (str, (char*)data.data, data.size);
addf (str, "\n");
adds (str, _("\t\t\tHexdump: "));
_gnutls_buffer_hexprint (str, (char*)data.data, data.size);
adds (str, "\n");
}
gnutls_free (oid.data);
gnutls_free (data.data);
}
/* Signature. */
if (format == GNUTLS_OCSP_PRINT_FULL)
{
gnutls_datum_t sig;
ret = gnutls_ocsp_resp_get_signature_algorithm (resp);
if (ret < 0)
addf (str, "error: get_signature_algorithm: %s\n",
gnutls_strerror (ret));
else
{
const char *name = gnutls_sign_algorithm_get_name (ret);
if (name == NULL)
name = _("unknown");
addf (str, _("\tSignature Algorithm: %s\n"), name);
}
if (gnutls_sign_is_secure(ret) == 0)
{
adds (str, _("warning: signed using a broken signature "
"algorithm that can be forged.\n"));
}
ret = gnutls_ocsp_resp_get_signature (resp, &sig);
if (ret < 0)
addf (str, "error: get_signature: %s\n", gnutls_strerror (ret));
else
{
adds (str, _("\tSignature:\n"));
_gnutls_buffer_hexdump (str, sig.data, sig.size, "\t\t");
gnutls_free (sig.data);
}
}
/* certs */
if (format == GNUTLS_OCSP_PRINT_FULL)
{
gnutls_x509_crt_t *certs;
size_t ncerts, i;
gnutls_datum_t out;
ret = gnutls_ocsp_resp_get_certs (resp, &certs, &ncerts);
if (ret < 0)
addf (str, "error: get_certs: %s\n", gnutls_strerror (ret));
else
{
for (i = 0; i < ncerts; i++)
{
size_t s = 0;
ret = gnutls_x509_crt_print (certs[i], GNUTLS_CRT_PRINT_FULL,
&out);
if (ret < 0)
addf (str, "error: crt_print: %s\n", gnutls_strerror (ret));
else
{
addf (str, "%.*s", out.size, out.data);
gnutls_free (out.data);
}
ret = gnutls_x509_crt_export (certs[i], GNUTLS_X509_FMT_PEM,
NULL, &s);
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
addf (str, "error: crt_export: %s\n", gnutls_strerror (ret));
else
{
out.data = gnutls_malloc (s);
if (out.data == NULL)
addf (str, "error: malloc: %s\n",
gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
else
{
ret = gnutls_x509_crt_export (certs[i], GNUTLS_X509_FMT_PEM,
out.data, &s);
if (ret < 0)
addf (str, "error: crt_export: %s\n", gnutls_strerror (ret));
else
{
out.size = s;
addf (str, "%.*s", out.size, out.data);
}
gnutls_free (out.data);
}
}
gnutls_x509_crt_deinit (certs[i]);
}
gnutls_free (certs);
}
}
}
/**
* gnutls_ocsp_resp_print:
* @resp: The structure to be printed
* @format: Indicate the format to use
* @out: Newly allocated datum with (0) terminated string.
*
* This function will pretty print a OCSP response, suitable for
* display to a human.
*
* If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the
* response will be output, on multiple lines.
*
* The output @out->data needs to be deallocate using gnutls_free().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
**/
int
gnutls_ocsp_resp_print (gnutls_ocsp_resp_t resp,
gnutls_ocsp_print_formats_t format,
gnutls_datum_t * out)
{
gnutls_buffer_st str;
int rc;
_gnutls_buffer_init (&str);
_gnutls_buffer_append_str (&str, _("OCSP Response Information:\n"));
print_resp (&str, resp, format);
_gnutls_buffer_append_data (&str, "\0", 1);
rc = _gnutls_buffer_to_datum (&str, out);
if (rc != GNUTLS_E_SUCCESS)
{
gnutls_assert ();
return rc;
}
return GNUTLS_E_SUCCESS;
}