ssl_util_stapling.c revision 11f2c481e1d57bedb3f758565307501e9a2730dd
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* _ _
* _ __ ___ ___ __| | ___ ___| | mod_ssl
* | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
* | | | | | | (_) | (_| | \__ \__ \ |
* |_| |_| |_|\___/ \__,_|___|___/___/_|
* |_____|
* OCSP Stapling Support
*/
/* ``Where's the spoons?
Where's the spoons?
Where's the bloody spoons?''
-- Alexei Sayle */
#include "ssl_private.h"
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
#ifdef HAVE_OCSP_STAPLING
/**
* Maxiumum OCSP stapling response size. This should be the response for a
* single certificate and will typically include the responder certificate chain
* so 10K should be more than enough.
*
*/
#define MAX_STAPLING_DER 10240
/* Cached info stored in certificate ex_info. */
typedef struct {
/* Index in session cache SHA1 hash of certificate */
/* Certificate ID for OCSP requests or NULL if ID cannot be determined */
/* Responder details */
char *uri;
} certinfo;
{
if (!cinf)
return;
}
static int stapling_ex_idx = -1;
void ssl_stapling_ex_init(void)
{
if (stapling_ex_idx != -1)
return;
}
{
int i;
return issuer;
}
}
return 0;
return issuer;
}
{
if (x == NULL)
return 0;
if (cinf) {
"ssl_stapling_init_cert: certificate already initialized!");
return 0;
}
if (!cinf) {
"ssl_stapling_init_cert: error allocating memory!");
return 0;
}
"ssl_stapling_init_cert: Can't retrieve issuer certificate!");
return 0;
}
return 0;
aia = X509_get1_ocsp(x);
if (aia)
"ssl_stapling_init_cert: no responder URL");
}
if (aia)
return 1;
}
{
X509 *x;
x = SSL_get_certificate(ssl);
if (x == NULL)
return NULL;
return cinf;
"stapling_get_cert_info: stapling not supported for certificate");
return NULL;
}
/*
* OCSP response caching code. The response is preceded by a flag value
* which indicates whether the response was invalid when it was stored.
* the purpose of this flag is to avoid repeated queries to a server
* which has given an invalid response while allowing a response which
* has subsequently become invalid to be retried immediately.
*
* The key for the cache is the hash of the certificate the response
* is for.
*/
{
unsigned char resp_der[MAX_STAPLING_DER];
unsigned char *p;
int resp_derlen;
if (resp_derlen <= 0) {
"OCSP stapling response encode error??");
return FALSE;
}
if (resp_derlen > sizeof resp_der) {
"OCSP stapling response too big (%u bytes)", resp_derlen);
return FALSE;
}
p = resp_der;
*p++ = 1;
}
else {
*p++ = 0;
}
i2d_OCSP_RESPONSE(rsp, &p);
if (rv != APR_SUCCESS) {
"stapling_cache_response: OCSP response session store error!");
return FALSE;
}
return TRUE;
}
{
unsigned char resp_der[MAX_STAPLING_DER];
const unsigned char *p;
unsigned int resp_derlen = MAX_STAPLING_DER;
if (rv != APR_SUCCESS) {
"stapling_get_cached_response: cache miss");
return TRUE;
}
if (resp_derlen <= 1) {
"stapling_get_cached_response: response length invalid??");
return TRUE;
}
p = resp_der;
if (pok) {
if (*p)
else
}
p++;
resp_derlen--;
if (!rsp) {
"stapling_get_cached_response: response parse error??");
return TRUE;
}
"stapling_get_cached_response: cache hit");
return TRUE;
}
{
int rspderlen;
if (rspderlen <= 0)
return 0;
return 1;
}
{
if (pok)
/* Check to see if response is an error. If so we automatically accept
* it because it would have expired from the cache if it was time to
* retry.
*/
if (mctx->stapling_return_errors)
return SSL_TLSEXT_ERR_OK;
else
return SSL_TLSEXT_ERR_NOACK;
}
/* If we can't parse response just pass it to client */
"stapling_check_response: Error Parsing Response!");
return SSL_TLSEXT_ERR_OK;
}
/* If ID not present just pass back to client */
"stapling_check_response: certificate ID not present in response!");
}
else {
mctx->stapling_resp_maxage)) {
if (pok)
}
else {
/* If pok is not NULL response was direct from a responder and
* the times should be valide. If pok is NULL the response was
* retrieved from cache and it is expected to subsequently expire
*/
if (pok) {
"stapling_check_response: response times invalid");
}
else {
"stapling_check_response: cached response expired");
}
return SSL_TLSEXT_ERR_NOACK;
}
}
return SSL_TLSEXT_ERR_OK;
}
{
int i;
const char *ocspuri;
/* Build up OCSP query from server certificate info */
"stapling_renew_response: querying responder");
req = OCSP_REQUEST_new();
if (!req)
goto err;
if (!id)
goto err;
goto err;
/* Add any extensions to the request */
for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
goto err;
}
if (mctx->stapling_force_url)
else
/* Create a temporary pool to constrain memory use */
if (ok != APR_SUCCESS) {
"stapling_renew_response: Error parsing uri %s",
ocspuri);
goto done;
}
"stapling_renew_response: Unsupported uri %s", ocspuri);
goto done;
}
if (!*prsp) {
"stapling_renew_response: responder error");
if (mctx->stapling_fake_trylater) {
}
else {
goto done;
}
}
else {
"stapling_renew_response: query response received");
"stapling_renew_response: error in retreived response!");
}
}
else {
"stapling_renew_response: responder error %s",
}
}
"stapling_renew_response: error caching response!");
}
done:
if (id)
if (req)
return rv;
err:
goto done;
}
/*
* SSLStaplingMutex operations. Similar to SSL mutex except a mutex is
* mandatory if stapling is enabled.
*/
{
return TRUE;
}
return FALSE;
}
return TRUE;
}
{
const char *lockfile;
return TRUE;
}
lockfile, p)) != APR_SUCCESS) {
if (lockfile) {
"Cannot reinit %s mutex with file `%s'",
}
else {
"Cannot reinit %s mutex", ssl_stapling_mutex_type);
}
return FALSE;
}
return TRUE;
}
static int stapling_mutex_on(server_rec *s)
{
"Failed to acquire OCSP stapling lock");
return FALSE;
}
return TRUE;
}
static int stapling_mutex_off(server_rec *s)
{
"Failed to release OCSP stapling lock");
return FALSE;
}
return TRUE;
}
/* Certificate Status callback. This is called when a client includes a
* certificate status request extension.
*
* Check for cached responses in session cache. If valid send back to
* client. If absent or no longer valid query responder and update
* cache. */
{
int rv;
"stapling_cb: OCSP Stapling disabled");
return SSL_TLSEXT_ERR_NOACK;
}
"stapling_cb: OCSP Stapling callback called");
return SSL_TLSEXT_ERR_NOACK;
}
"stapling_cb: retrieved cached certificate data");
/* Check to see if we already have a response for this certificate */
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
if (rsp) {
/* see if response is acceptable */
"stapling_cb: retrieved cached response");
if (rv == SSL_TLSEXT_ERR_ALERT_FATAL) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
else if (rv == SSL_TLSEXT_ERR_NOACK) {
/* Error in response. If this error was not present when it was
* stored (i.e. response no longer valid) then it can be
* renewed straight away.
*
* If the error *was* present at the time it was stored then we
* don't renew the response straight away we just wait for the
* cached response to expire.
*/
if (ok) {
}
else if (!mctx->stapling_return_errors) {
return SSL_TLSEXT_ERR_NOACK;
}
}
}
"stapling_cb: renewing cached response");
"stapling_cb: fatal error");
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
}
if (rsp) {
"stapling_cb: setting response");
return SSL_TLSEXT_ERR_ALERT_FATAL;
return SSL_TLSEXT_ERR_OK;
}
"stapling_cb: no response available");
return SSL_TLSEXT_ERR_NOACK;
}
{
"SSLStapling: no stapling cache available");
ssl_die();
}
/* Set some default values for parameters if they are not set */
}
}
}
}
}
}
}
#endif