blob: 16de82aa3709ce4919c65157eeca36687e4e0c33 [file] [log] [blame]
Support for OCSP Verification in Serf
=====================================
Serf trunk currently supports OCSP stapling for verifying server
certificates. The purpose of this branch is to add minimal support
for issuing OCSP requests to responders from the client application.
The idea is that the application decides when and where to send OCSP
requests and how to verify responses, and Serf provides some basic
utility functions for constructing the requests and parsing the
responses.
These are the proposed changes:
1. serf_ssl_cert_certificate()
Extract the OCSP responder locations from the certificate's x509v3
extension field authorityInfoAccess:OCSP;URI and, if it is present,
insert the array into the returned hash table with key "OCSP".
2. serf_ssl_cert_export2()
/**
* Export a certificate to base64-encoded, zero-terminated string.
* The returned string is allocated in @a result_pool.
* Uses @a scratch_pool for temporary allocations.
* Returns NULL on failure.
*/
const char *serf_ssl_cert_export2(
const serf_ssl_certificate_t *cert,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
Discussion:
serf_ssl_cert_export() uses a single pool for both the result and
for teporary allocations. The size of the temporary buffer is on
the order of the size of the result, so if we can use a scratch
pool for that, we can effectively halve the memory used by
exported certs.
3. serf_ssl_cert_import()
Add a new function that is the inverse of serf_ssl_cert_export():
/**
* Import a certificate from a base64-encoded, zero-terminated string.
* The returned certificate is allocated in @a result_pool.
* Uses @a scratch_pool for temporary allocations.
* Returns NULL on failure.
*/
serf_ssl_certificate_t *serf_ssl_cert_import(
const char *encoded_cert,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
Discussion:
In order to create an OCSP request, the application needs both
the server certificate and its issuer certificate. An application
may have to issue OCSP requests independently and asynchronously
of any other processing, so it's nice if it can store the
certificates in a form that's independent of pool lifetimes. We
provide this form with serf_ssl_cert_export(), but there's no
easy way to consume the exported form in existing Serf APIs
(writing it to a file in PEM format and reading it back through
serf_ssl_load_cert_file() is neither easy nor sane).
4. OCSP requests
Add a new opaque type and accessor functions that can be used from
within a request setup handler to create an OCSP request.
Discussion:
HTTP OCSP requests can be sent using eithe the GET or POST
methods; see https://www.ietf.org/rfc/rfc2560.txt section A.1.1.
It's up to the application to decide which method to use, so we
don't provide a function to create the request body or set
request headers.
/**
* Internal representation of an OCSP request.
*/
typedef struct serf_ssl_ocsp_request_t serf_ssl_ocsp_request_t;
/**
* Constructs an OCSP verification request for @a server_cert with
* issuer certificate @a issuer_cert. If @a generate_nonce is
* non-zero, the request will contain a random nonce.
*
* The request will be allocated from @a result_pool.
*
* Use @a scratch_pool for temporary allocations.
*
* Returns @c NULL on failure, e.g., if @a issuer_cert is not the
* issuer certificate of @a server_cert.
*/
serf_ssl_ocsp_request_t *serf_ssl_ocsp_request_create(
const serf_ssl_certificate_t *server_cert,
const serf_ssl_certificate_t *issuer_cert,
int generate_nonce,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
* Returns an pointer to the DER-encoded OCSP request
* body within the @a ocsp_request structure.
*
* The application must decide whether to use this data as the body of
* an HTTP POST request or Base64-encoded as part of the URI for a GET
* request; see RFC 2560, section A.1.1.
*
* @see serf_ssl_ocsp_request_body_size()
*/
const void *serf_ssl_ocsp_request_body(
const serf_ssl_ocsp_request_t *ocsp_request);
/**
* Returns the size of the DER-encoded OCSP request body.
* @see serf_ssl_ocsp_request_body().
*/
apr_size_t serf_ssl_ocsp_request_body_size(
const serf_ssl_ocsp_request_t *ocsp_request);
/**
* Export @a ocsp_request to a zero-terminated string,
* allocated from @a result_pool.
*
* Use @a scratch_pool for temporary allocations.
*
* Returns @c NULL on failure.
*/
const char *serf_ssl_ocsp_request_export(
const serf_ssl_ocsp_request_t *ocsp_request,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
* Create an OCSP request from a previously exported zero-terminated
* string @a encoded_ocsp_request. The returned request will be
* allocated from @a result_pool.
*
* Use @a scratch_pool for temporary allocations.
*
* Returns @c NULL on failure.
*/
serf_ssl_ocsp_request_t *serf_ssl_ocsp_request_import(
const char *encoded_ocsp_request,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
5. OCSP responses
Add a new opaque type, response body parser and response validator.
Add a new function that can be used from within a response handler
to verify an OCSP response:
Discussion:
Parses and verifies the OCSP response received in the HTTP response
body as per RFC 2560, section 3.2.
/**
* Internal representation of an OCSP response.
*/
typedef struct serf_ssl_ocsp_response_t serf_ssl_ocsp_response_t;
/**
* Parse the body of an OCSP response in DER form, @a ocsp_response,
* of size @a ocsp_response_size, and construct an internal
* representation, allocated from @a result_pool.
*
* If @a failures is not @c NULL, it will be set as in
* #serf_ssl_need_server_cert_t.
*
* Use @a scratch_pool for temporary allocations.
*
* Returns @c NULL if on failure.
*/
serf_ssl_ocsp_response_t *serf_ssl_ocsp_response_parse(
const void *ocsp_response,
apr_size_t ocsp_response_size,
int *failures,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
* Check if the given @a ocsp_response is valid for the given
* @a ocsp_request in the provided @a ssl_ctx, as defined by
* the algorithm documented in RFC 2560, section 3.5.
*
* The OCSP responder and application wall clocks may be out of sync
* by @a clock_skew, and @a max_age is the acceptable age of the
* request. Their values are truncated to the nearest second.
*
* The returned value will be:
*
* - APR_SUCCESS,
* if all steps of the verification succeeded;
* - SERF_ERROR_SSL_OCSP_RESPONSE_CERT_REVOKED,
* if the certificate was revoked;
* - SERF_ERROR_SSL_OCSP_RESPONSE_CERT_UNKNOWN,
* if the responder knows nothing about the certificate;
* - SERF_ERROR_SSL_OCSP_RESPONSE_INVALID,
* if the response itself is invalid or not well-formed.
*
* The @a this_update and @a next_update output arguments are
* described in RFC 2560, section 2.4 and, when not @c NULL and if the
* verificateion succeeded, will be parsed from the response. Any of
* these times that are not present in the response will be set to the
* epoch, i.e., @c APR_TIME_C(0).
*
* Uses @a scratch_pool for temporary allocations.
*/
apr_status_t serf_ssl_ocsp_response_verify(
serf_ssl_context_t *ssl_ctx,
const serf_ssl_ocsp_response_t *ocsp_response,
const serf_ssl_ocsp_request_t *ocsp_request,
apr_time_t clock_skew,
apr_time_t max_age,
apr_time_t *this_update,
apr_time_t *next_update,
apr_pool_t *scratch_pool);
6. New error codes and macros
#define SERF_ERROR_SSL_OCSP_RESPONSE_CERT_REVOKED
#define SERF_ERROR_SSL_OCSP_RESPONSE_CERT_UNKNOWN
#define SERF_ERROR_SSL_OCSP_RESPONSE_INVALID
#define SERF_OCSP_UNGOOD_ERROR(status)
Discussion:
These error codes are returned from serf_ssl_ocsp_response_verify().
The SERF_OCSP_UNGOOD_ERROR() macro combines the _CERT_REVOKED
and _CERT_UNKNOWN error codes..