|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "ssl_private.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifndef OPENSSL_NO_OCSP
|
|
Packit |
90a5c9 |
#include "apr_base64.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Return the responder URI specified in the given certificate, or
|
|
Packit |
90a5c9 |
* NULL if none specified. */
|
|
Packit |
90a5c9 |
static const char *extract_responder_uri(X509 *cert, apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STACK_OF(ACCESS_DESCRIPTION) *values;
|
|
Packit |
90a5c9 |
char *result = NULL;
|
|
Packit |
90a5c9 |
int j;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
values = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL);
|
|
Packit |
90a5c9 |
if (!values) {
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (j = 0; j < sk_ACCESS_DESCRIPTION_num(values) && !result; j++) {
|
|
Packit |
90a5c9 |
ACCESS_DESCRIPTION *value = sk_ACCESS_DESCRIPTION_value(values, j);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Name found in extension, and is a URI: */
|
|
Packit |
90a5c9 |
if (OBJ_obj2nid(value->method) == NID_ad_OCSP
|
|
Packit |
90a5c9 |
&& value->location->type == GEN_URI) {
|
|
Packit |
90a5c9 |
result = apr_pstrdup(pool,
|
|
Packit |
90a5c9 |
(char *)value->location->d.uniformResourceIdentifier->data);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AUTHORITY_INFO_ACCESS_free(values);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return result;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Return the responder URI object which should be used in the given
|
|
Packit |
90a5c9 |
* configuration for the given certificate, or NULL if none can be
|
|
Packit |
90a5c9 |
* determined. */
|
|
Packit |
90a5c9 |
static apr_uri_t *determine_responder_uri(SSLSrvConfigRec *sc, X509 *cert,
|
|
Packit |
90a5c9 |
conn_rec *c, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_uri_t *u = apr_palloc(p, sizeof *u);
|
|
Packit |
90a5c9 |
const char *s;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Use default responder URL if forced by configuration, else use
|
|
Packit |
90a5c9 |
* certificate-specified responder, falling back to default if
|
|
Packit |
90a5c9 |
* necessary and possible. */
|
|
Packit |
90a5c9 |
if (sc->server->ocsp_force_default == TRUE) {
|
|
Packit |
90a5c9 |
s = sc->server->ocsp_responder;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
s = extract_responder_uri(cert, p);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (s == NULL && sc->server->ocsp_responder) {
|
|
Packit |
90a5c9 |
s = sc->server->ocsp_responder;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (s == NULL) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01918)
|
|
Packit |
90a5c9 |
"no OCSP responder specified in certificate and "
|
|
Packit |
90a5c9 |
"no default configured");
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_uri_parse(p, s, u);
|
|
Packit |
90a5c9 |
if (rv || !u->hostname) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01919)
|
|
Packit |
90a5c9 |
"failed to parse OCSP responder URI '%s'", s);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strcasecmp(u->scheme, "http") != 0) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(01920)
|
|
Packit |
90a5c9 |
"cannot handle OCSP responder URI '%s'", s);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!u->port) {
|
|
Packit |
90a5c9 |
u->port = apr_uri_port_of_scheme(u->scheme);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return u;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Create an OCSP request for the given certificate; returning the
|
|
Packit |
90a5c9 |
* certificate ID in *certid and *issuer on success. Returns the
|
|
Packit |
90a5c9 |
* request object on success, or NULL on error. */
|
|
Packit |
90a5c9 |
static OCSP_REQUEST *create_request(X509_STORE_CTX *ctx, X509 *cert,
|
|
Packit |
90a5c9 |
OCSP_CERTID **certid,
|
|
Packit |
90a5c9 |
server_rec *s, apr_pool_t *p,
|
|
Packit |
90a5c9 |
SSLSrvConfigRec *sc)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
OCSP_REQUEST *req = OCSP_REQUEST_new();
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*certid = OCSP_cert_to_id(NULL, cert, X509_STORE_CTX_get0_current_issuer(ctx));
|
|
Packit |
90a5c9 |
if (!*certid || !OCSP_request_add0_id(req, *certid)) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01921)
|
|
Packit |
90a5c9 |
"could not retrieve certificate id");
|
|
Packit |
90a5c9 |
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (sc->server->ocsp_use_request_nonce != FALSE) {
|
|
Packit |
90a5c9 |
OCSP_request_add1_nonce(req, 0, -1);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return req;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Verify the OCSP status of given certificate. Returns
|
|
Packit |
90a5c9 |
* V_OCSP_CERTSTATUS_* result code. */
|
|
Packit |
90a5c9 |
static int verify_ocsp_status(X509 *cert, X509_STORE_CTX *ctx, conn_rec *c,
|
|
Packit |
90a5c9 |
SSLSrvConfigRec *sc, server_rec *s,
|
|
Packit |
90a5c9 |
apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int rc = V_OCSP_CERTSTATUS_GOOD;
|
|
Packit |
90a5c9 |
OCSP_RESPONSE *response = NULL;
|
|
Packit |
90a5c9 |
OCSP_BASICRESP *basicResponse = NULL;
|
|
Packit |
90a5c9 |
OCSP_REQUEST *request = NULL;
|
|
Packit |
90a5c9 |
OCSP_CERTID *certID = NULL;
|
|
Packit |
90a5c9 |
apr_uri_t *ruri;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ruri = determine_responder_uri(sc, cert, c, pool);
|
|
Packit |
90a5c9 |
if (!ruri) {
|
|
Packit |
90a5c9 |
if (sc->server->ocsp_mask & SSL_OCSPCHECK_NO_OCSP_FOR_CERT_OK) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
Packit |
90a5c9 |
"Skipping OCSP check for certificate cos no OCSP URL"
|
|
Packit |
90a5c9 |
" found and no_ocsp_for_cert_ok is set");
|
|
Packit |
90a5c9 |
return V_OCSP_CERTSTATUS_GOOD;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
return V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
request = create_request(ctx, cert, &certID, s, pool, sc);
|
|
Packit |
90a5c9 |
if (request) {
|
|
Packit |
90a5c9 |
apr_interval_time_t to = sc->server->ocsp_responder_timeout == UNSET ?
|
|
Packit |
90a5c9 |
apr_time_from_sec(DEFAULT_OCSP_TIMEOUT) :
|
|
Packit |
90a5c9 |
sc->server->ocsp_responder_timeout;
|
|
Packit |
90a5c9 |
response = modssl_dispatch_ocsp_request(ruri, to, request, c, pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!request || !response) {
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rc == V_OCSP_CERTSTATUS_GOOD) {
|
|
Packit |
90a5c9 |
int r = OCSP_response_status(response);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01922)
|
|
Packit |
90a5c9 |
"OCSP response not successful: %d", r);
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rc == V_OCSP_CERTSTATUS_GOOD) {
|
|
Packit |
90a5c9 |
basicResponse = OCSP_response_get1_basic(response);
|
|
Packit |
90a5c9 |
if (!basicResponse) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01923)
|
|
Packit |
90a5c9 |
"could not retrieve OCSP basic response");
|
|
Packit |
90a5c9 |
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rc == V_OCSP_CERTSTATUS_GOOD &&
|
|
Packit |
90a5c9 |
sc->server->ocsp_use_request_nonce != FALSE &&
|
|
Packit |
90a5c9 |
OCSP_check_nonce(request, basicResponse) != 1) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01924)
|
|
Packit |
90a5c9 |
"Bad OCSP responder answer (bad nonce)");
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rc == V_OCSP_CERTSTATUS_GOOD) {
|
|
Packit |
90a5c9 |
/* Check if OCSP certificate verification required */
|
|
Packit |
90a5c9 |
if (sc->server->ocsp_noverify != TRUE) {
|
|
Packit |
90a5c9 |
/* Modify OCSP response verification to include OCSP Responder cert */
|
|
Packit |
90a5c9 |
if (OCSP_basic_verify(basicResponse, sc->server->ocsp_certs, X509_STORE_CTX_get0_store(ctx),
|
|
Packit |
90a5c9 |
sc->server->ocsp_verify_flags) != 1) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01925)
|
|
Packit |
90a5c9 |
"failed to verify the OCSP response");
|
|
Packit |
90a5c9 |
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rc == V_OCSP_CERTSTATUS_GOOD) {
|
|
Packit |
90a5c9 |
int reason = -1, status;
|
|
Packit |
90a5c9 |
ASN1_GENERALIZEDTIME *thisup = NULL, *nextup = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rc = OCSP_resp_find_status(basicResponse, certID, &status,
|
|
Packit |
90a5c9 |
&reason, NULL, &thisup, &nextup);
|
|
Packit |
90a5c9 |
if (rc != 1) {
|
|
Packit |
90a5c9 |
ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02272)
|
|
Packit |
90a5c9 |
"failed to retrieve OCSP response status");
|
|
Packit |
90a5c9 |
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rc = status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check whether the response is inside the defined validity
|
|
Packit |
90a5c9 |
* period; otherwise fail. */
|
|
Packit |
90a5c9 |
if (rc != V_OCSP_CERTSTATUS_UNKNOWN) {
|
|
Packit |
90a5c9 |
long resptime_skew = sc->server->ocsp_resptime_skew == UNSET ?
|
|
Packit |
90a5c9 |
DEFAULT_OCSP_MAX_SKEW : sc->server->ocsp_resptime_skew;
|
|
Packit |
90a5c9 |
/* oscp_resp_maxage can be passed verbatim - UNSET (-1) means
|
|
Packit |
90a5c9 |
* that responses can be of any age as long as nextup is in the
|
|
Packit |
90a5c9 |
* future. */
|
|
Packit |
90a5c9 |
int vrc = OCSP_check_validity(thisup, nextup, resptime_skew,
|
|
Packit |
90a5c9 |
sc->server->ocsp_resp_maxage);
|
|
Packit |
90a5c9 |
if (vrc != 1) {
|
|
Packit |
90a5c9 |
ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02273)
|
|
Packit |
90a5c9 |
"OCSP response outside validity period");
|
|
Packit |
90a5c9 |
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
|
|
Packit |
90a5c9 |
rc = V_OCSP_CERTSTATUS_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int level =
|
|
Packit |
90a5c9 |
(status == V_OCSP_CERTSTATUS_GOOD) ? APLOG_INFO : APLOG_ERR;
|
|
Packit |
90a5c9 |
const char *result =
|
|
Packit |
90a5c9 |
status == V_OCSP_CERTSTATUS_GOOD ? "good" :
|
|
Packit |
90a5c9 |
(status == V_OCSP_CERTSTATUS_REVOKED ? "revoked" : "unknown");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ssl_log_cxerror(SSLLOG_MARK, level, 0, c, cert, APLOGNO(03239)
|
|
Packit |
90a5c9 |
"OCSP validation completed, "
|
|
Packit |
90a5c9 |
"certificate status: %s (%d, %d)",
|
|
Packit |
90a5c9 |
result, status, reason);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (request) OCSP_REQUEST_free(request);
|
|
Packit |
90a5c9 |
if (response) OCSP_RESPONSE_free(response);
|
|
Packit |
90a5c9 |
if (basicResponse) OCSP_BASICRESP_free(basicResponse);
|
|
Packit |
90a5c9 |
/* certID is freed when the request is freed */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc,
|
|
Packit |
90a5c9 |
server_rec *s, conn_rec *c, apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
|
|
Packit |
90a5c9 |
apr_pool_t *vpool;
|
|
Packit |
90a5c9 |
int rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!cert) {
|
|
Packit |
90a5c9 |
/* starting with OpenSSL 1.0, X509_STORE_CTX_get_current_cert()
|
|
Packit |
90a5c9 |
* may yield NULL. Return early, but leave the ctx error as is. */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
Packit |
90a5c9 |
"No cert available to check with OCSP");
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (X509_check_issued(cert,cert) == X509_V_OK) {
|
|
Packit |
90a5c9 |
/* don't do OCSP checking for valid self-issued certs */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
|
Packit |
90a5c9 |
"Skipping OCSP check for valid self-issued cert");
|
|
Packit |
90a5c9 |
X509_STORE_CTX_set_error(ctx, X509_V_OK);
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Create a temporary pool to constrain memory use (the passed-in
|
|
Packit |
90a5c9 |
* pool may be e.g. a connection pool). */
|
|
Packit |
90a5c9 |
apr_pool_create(&vpool, pool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_pool_destroy(vpool);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Propagate the verification status back to the passed-in
|
|
Packit |
90a5c9 |
* context. */
|
|
Packit |
90a5c9 |
switch (rv) {
|
|
Packit |
90a5c9 |
case V_OCSP_CERTSTATUS_GOOD:
|
|
Packit |
90a5c9 |
X509_STORE_CTX_set_error(ctx, X509_V_OK);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case V_OCSP_CERTSTATUS_REVOKED:
|
|
Packit |
90a5c9 |
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case V_OCSP_CERTSTATUS_UNKNOWN:
|
|
Packit |
90a5c9 |
/* correct error code for application errors? */
|
|
Packit |
90a5c9 |
X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv == V_OCSP_CERTSTATUS_GOOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif /* HAVE_OCSP */
|