Return-Path: Delivered-To: apmail-httpd-cvs-archive@www.apache.org Received: (qmail 86380 invoked from network); 29 Nov 2007 11:19:17 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 29 Nov 2007 11:19:17 -0000 Received: (qmail 52395 invoked by uid 500); 29 Nov 2007 11:19:04 -0000 Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org Received: (qmail 52346 invoked by uid 500); 29 Nov 2007 11:19:04 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@httpd.apache.org Received: (qmail 52331 invoked by uid 99); 29 Nov 2007 11:19:04 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 29 Nov 2007 03:19:04 -0800 X-ASF-Spam-Status: No, hits=-100.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 29 Nov 2007 11:18:41 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 69CBA1A9832; Thu, 29 Nov 2007 03:18:43 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r599385 - in /httpd/httpd/trunk: ./ modules/ssl/ Date: Thu, 29 Nov 2007 11:18:41 -0000 To: cvs@httpd.apache.org From: jorton@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071129111843.69CBA1A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: jorton Date: Thu Nov 29 03:18:40 2007 New Revision: 599385 URL: http://svn.apache.org/viewvc?rev=599385&view=rev Log: mod_ssl: Add support for OCSP validation of client certificates: * modules/ssl/ssl_engine_config.c (modssl_ctx_init, modssl_ctx_cfg_merge): Initialize and merge OCSP config options. (ssl_cmd_SSLOCSPOverrideResponder, ssl_cmd_SSLOCSPDefaultResponder, ssl_cmd_SSLOCSPEnable): Add functions. * modules/ssl/mod_ssl.c (ssl_config_cmds): Add config options. * modules/ssl/ssl_private.h: Add prototypes, config options to modssl_ctx_t. * modules/ssl/ssl_util_ocsp.c: New file, utility interface for dispatching OCSP requests. * modules/ssl/ssl_engine_ocsp.c: New file, interface for performing OCSP validation. * modules/ssl/ssl_engine_kernel.c (ssl_callback_SSLVerify): Perform OCSP validation if configured, and the cert is so-far verified to be trusted. Fail if OCSP validation is configured an the optional-no-ca check tripped. * modules/ssl/config.m4: Check for OCSP support, build new files. * modules/ssl/mod_ssl.dsp: Build new files. * modules/ssl/ssl_toolkit_compat.h: Include headers for OCSP interfaces. PR: 41123 Submitted by: Marc Stern , Joe Orton Reviewed by: Steve Henson Added: httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c (with props) httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c (with props) Modified: httpd/httpd/trunk/CHANGES httpd/httpd/trunk/modules/ssl/config.m4 httpd/httpd/trunk/modules/ssl/mod_ssl.c httpd/httpd/trunk/modules/ssl/mod_ssl.dsp httpd/httpd/trunk/modules/ssl/ssl_engine_config.c httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c httpd/httpd/trunk/modules/ssl/ssl_private.h httpd/httpd/trunk/modules/ssl/ssl_toolkit_compat.h Modified: httpd/httpd/trunk/CHANGES URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/CHANGES [utf-8] (original) +++ httpd/httpd/trunk/CHANGES [utf-8] Thu Nov 29 03:18:40 2007 @@ -2,6 +2,9 @@ Changes with Apache 2.3.0 [ When backported to 2.2.x, remove entry from this file ] + *) mod_ssl: Add support for OCSP validation of client certificates. + PR 41123. [Marc Stern , Joe Orton] + *) mod_filter: Don't segfault on (unsupported) chained FilterProvider usage. PR 43956 [Nick Kew] Modified: httpd/httpd/trunk/modules/ssl/config.m4 URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/config.m4?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/config.m4 (original) +++ httpd/httpd/trunk/modules/ssl/config.m4 Thu Nov 29 03:18:40 2007 @@ -135,6 +135,11 @@ fi ]) +AC_DEFUN([CHECK_OCSP], [ +AC_CHECK_HEADERS(openssl/ocsp.h, + [AC_DEFINE([HAVE_OCSP], 1, [Define if OCSP is supported by OpenSSL])] +) +]) dnl # start of module specific part APACHE_MODPATH_INIT(ssl) @@ -163,6 +168,8 @@ ssl_scache_memcache.lo dnl ssl_util.lo dnl ssl_util_ssl.lo dnl +ssl_engine_ocsp.lo dnl +ssl_util_ocsp.lo dnl " dnl # hook module into the Autoconf mechanism (--enable-ssl option) APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , no, [ @@ -170,6 +177,7 @@ APR_SETVAR(MOD_SSL_LDADD, [\$(SSL_LIBS)]) CHECK_DISTCACHE CHECK_SSL_MEMCACHE + CHECK_OCSP if test "x$enable_ssl" = "xshared"; then # The only symbol which needs to be exported is the module # structure, so ask libtool to hide everything else: Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/mod_ssl.c?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/mod_ssl.c (original) +++ httpd/httpd/trunk/modules/ssl/mod_ssl.c Thu Nov 29 03:18:40 2007 @@ -178,6 +178,13 @@ "Require a boolean expression to evaluate to true for granting access" "(arbitrary complex boolean expression - see manual)") + SSL_CMD_SRV(OCSPEnable, FLAG, + "Enable use of OCSP to verify certificate revocation (`on', `off')") + SSL_CMD_SRV(OCSPDefaultResponder, TAKE1, + "URL of the default OCSP Responder") + SSL_CMD_SRV(OCSPOverrideResponder, FLAG, + "Force use of the default responder URL (`on', `off')") + /* Deprecated directives. */ AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL, "SSLLog directive is no longer supported - use ErrorLog."), Modified: httpd/httpd/trunk/modules/ssl/mod_ssl.dsp URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/mod_ssl.dsp?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/mod_ssl.dsp (original) +++ httpd/httpd/trunk/modules/ssl/mod_ssl.dsp Thu Nov 29 03:18:40 2007 @@ -198,6 +198,14 @@ # End Source File # Begin Source File +SOURCE=.\ssl_engine_ocsp.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_util_ocsp.c +# End Source File +# Begin Source File + SOURCE=.\ssl_scache.c # End Source File # Begin Source File Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_config.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_config.c?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_engine_config.c (original) +++ httpd/httpd/trunk/modules/ssl/ssl_engine_config.c Thu Nov 29 03:18:40 2007 @@ -128,6 +128,10 @@ mctx->auth.cipher_suite = NULL; mctx->auth.verify_depth = UNSET; mctx->auth.verify_mode = SSL_CVERIFY_UNSET; + + mctx->ocsp_enabled = FALSE; + mctx->ocsp_force_default = FALSE; + mctx->ocsp_responder = NULL; } static void modssl_ctx_init_proxy(SSLSrvConfigRec *sc, @@ -217,6 +221,10 @@ cfgMergeString(auth.cipher_suite); cfgMergeInt(auth.verify_depth); cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET); + + cfgMergeBool(ocsp_enabled); + cfgMergeBool(ocsp_force_default); + cfgMerge(ocsp_responder, NULL); } static void modssl_ctx_cfg_merge_proxy(modssl_ctx_t *base, @@ -1407,6 +1415,40 @@ { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; dc->szUserName = arg; + return NULL; +} + +const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_enabled = flag ? TRUE : FALSE; + +#ifndef HAVE_OCSP + if (flag) { + return "OCSP support not detected in SSL library; cannot enable " + "OCSP validation"; + } +#endif + + return NULL; +} + +const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_force_default = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_responder = arg; + return NULL; } Modified: httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c (original) +++ httpd/httpd/trunk/modules/ssl/ssl_engine_kernel.c Thu Nov 29 03:18:40 2007 @@ -1293,12 +1293,35 @@ } /* - * Additionally perform CRL-based revocation checks + * Perform OCSP/CRL-based revocation checks */ if (ok) { if (!(ok = ssl_callback_SSLVerify_CRL(ok, ctx, conn))) { errnum = X509_STORE_CTX_get_error(ctx); } + +#ifdef HAVE_OCSP + /* If there was an optional verification error, it's not + * possible to perform OCSP validation since the issuer may be + * missing/untrusted. Fail in that case. */ + if (ok && ssl_verify_error_is_optional(errnum) + && sc->server->ocsp_enabled) { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); + errnum = X509_V_ERR_APPLICATION_VERIFICATION; + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, + "cannot perform OCSP validation for cert " + "if issuer has not been verified " + "(optional_no_ca configured)"); + ok = FALSE; + } + + if (ok && sc->server->ocsp_enabled) { + ok = modssl_verify_ocsp(ctx, sc, s, conn, conn->pool); + if (!ok) { + errnum = X509_STORE_CTX_get_error(ctx); + } + } +#endif } /* Added: httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c?rev=599385&view=auto ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c (added) +++ httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c Thu Nov 29 03:18:40 2007 @@ -0,0 +1,253 @@ +/* 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "ssl_private.h" + +#ifdef HAVE_OCSP +#include "apr_base64.h" + +/* Return the responder URI specified in the given certificate, or + * NULL if none specified. */ +static const char *extract_responder_uri(X509 *cert, apr_pool_t *pool) +{ + STACK_OF(ACCESS_DESCRIPTION) *values; + char *result = NULL; + int j; + + values = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL); + if (!values) { + return NULL; + } + + for (j = 0; j < sk_ACCESS_DESCRIPTION_num(values) && !result; j++) { + ACCESS_DESCRIPTION *value = sk_ACCESS_DESCRIPTION_value(values, j); + + /* Name found in extension, and is a URI: */ + if (OBJ_obj2nid(value->method) == NID_ad_OCSP + && value->location->type == GEN_URI) { + result = apr_pstrdup(pool, + (char *)value->location->d.uniformResourceIdentifier->data); + } + } + + AUTHORITY_INFO_ACCESS_free(values); + + return result; +} + +/* Return the responder URI object which should be used in the given + * configuration for the given certificate, or NULL if none can be + * determined. */ +static apr_uri_t *determine_responder_uri(SSLSrvConfigRec *sc, X509 *cert, + conn_rec *c, apr_pool_t *p) +{ + apr_uri_t *u = apr_palloc(p, sizeof *u); + const char *s; + apr_status_t rv; + + /* Use default responder URL if forced by configuration, else use + * certificate-specified responder, falling back to default if + * necessary and possible. */ + if (sc->server->ocsp_force_default) { + s = sc->server->ocsp_responder; + } + else { + s = extract_responder_uri(cert, p); + + if (s == NULL && sc->server->ocsp_responder) { + s = sc->server->ocsp_responder; + } + } + + if (s == NULL) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "no OCSP responder specified in certificate and " + "no default configured"); + return NULL; + } + + rv = apr_uri_parse(p, s, u); + if (rv || !u->hostname || !u->path) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, + "failed to parse OCSP responder URI '%s'", s); + return NULL; + } + + if (strcasecmp(u->scheme, "http") != 0) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, + "cannot handle OCSP responder URI '%s'", s); + return NULL; + } + + return u; +} + +/* Create an OCSP request for the given certificate; returning the + * certificate ID in *certid and *issuer on success. Returns the + * request object on success, or NULL on error. */ +static OCSP_REQUEST *create_request(X509_STORE_CTX *ctx, X509 *cert, + OCSP_CERTID **certid, + server_rec *s, apr_pool_t *p) +{ + OCSP_REQUEST *req = OCSP_REQUEST_new(); + + *certid = OCSP_cert_to_id(NULL, cert, ctx->current_issuer); + if (!*certid || !OCSP_request_add0_id(req, *certid)) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "could not retrieve certificate id"); + return NULL; + } + + OCSP_request_add1_nonce(req, 0, -1); + + return req; +} + +/* Verify the OCSP status of given certificate. Returns + * V_OCSP_CERTSTATUS_* result code. */ +static int verify_ocsp_status(X509 *cert, X509_STORE_CTX *ctx, conn_rec *c, + SSLSrvConfigRec *sc, server_rec *s, + apr_pool_t *pool) +{ + int rc = V_OCSP_CERTSTATUS_GOOD; + OCSP_RESPONSE *response = NULL; + OCSP_BASICRESP *basicResponse = NULL; + OCSP_REQUEST *request = NULL; + OCSP_CERTID *certID = NULL; + apr_uri_t *ruri; + + ruri = determine_responder_uri(sc, cert, c, pool); + if (!ruri) { + return V_OCSP_CERTSTATUS_UNKNOWN; + } + + request = create_request(ctx, cert, &certID, s, pool); + if (request) { + response = modssl_dispatch_ocsp_request(ruri, request, c, pool); + } + + if (!request || !response) { + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + + if (rc == V_OCSP_CERTSTATUS_GOOD) { + int r = OCSP_response_status(response); + + if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "OCSP response not successful: %d", rc); + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + } + + if (rc == V_OCSP_CERTSTATUS_GOOD) { + basicResponse = OCSP_response_get1_basic(response); + if (!basicResponse) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, + "could not retrieve OCSP basic response"); + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + } + + if (rc == V_OCSP_CERTSTATUS_GOOD) { + if (OCSP_check_nonce(request, basicResponse) != 1) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Bad OCSP responder answer (bad nonce)"); + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + } + + if (rc == V_OCSP_CERTSTATUS_GOOD) { + /* TODO: allow flags configuration. */ + if (OCSP_basic_verify(basicResponse, NULL, ctx->ctx, 0) != 1) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "failed to verify the OCSP response"); + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + } + + if (rc == V_OCSP_CERTSTATUS_GOOD) { + int reason = -1, status; + + rc = OCSP_resp_find_status(basicResponse, certID, &status, + &reason, NULL, NULL, NULL); + if (rc != 1) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_log_cxerror(APLOG_MARK, APLOG_ERR, 0, c, cert, + "failed to retrieve OCSP response status"); + rc = V_OCSP_CERTSTATUS_UNKNOWN; + } + else { + int level = + (status == V_OCSP_CERTSTATUS_GOOD) ? APLOG_INFO : APLOG_ERR; + const char *result = + status == V_OCSP_CERTSTATUS_GOOD ? "good" : + (status == V_OCSP_CERTSTATUS_REVOKED ? "revoked" : "unknown"); + + ssl_log_cxerror(APLOG_MARK, level, 0, c, cert, + "OCSP validation completed, " + "certificate status: %s (%d, %d)", + result, status, reason); + rc = status; + } + } + + if (request) OCSP_REQUEST_free(request); + if (response) OCSP_RESPONSE_free(response); + if (basicResponse) OCSP_BASICRESP_free(basicResponse); + /* certID is freed when the request is freed */ + + return rc; +} + +int modssl_verify_ocsp(X509_STORE_CTX *ctx, SSLSrvConfigRec *sc, + server_rec *s, conn_rec *c, apr_pool_t *pool) +{ + X509 *cert = X509_STORE_CTX_get_current_cert(ctx); + apr_pool_t *vpool; + int rv; + + /* Since the passed-inpool is likely to be the connection pool, + * create a temporary pool to constrain memory use. */ + apr_pool_create(&vpool, pool); + + rv = verify_ocsp_status(cert, ctx, c, sc, s, vpool); + + apr_pool_destroy(vpool); + + /* Propagate the verification status back to the passed-in + * context. */ + switch (rv) { + case V_OCSP_CERTSTATUS_GOOD: + X509_STORE_CTX_set_error(ctx, X509_V_OK); + break; + + case V_OCSP_CERTSTATUS_REVOKED: + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); + break; + + case V_OCSP_CERTSTATUS_UNKNOWN: + /* correct error code for application errors? */ + X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); + break; + } + + return rv == V_OCSP_CERTSTATUS_GOOD; +} +#endif /* HAVE_OCSP */ Propchange: httpd/httpd/trunk/modules/ssl/ssl_engine_ocsp.c ------------------------------------------------------------------------------ svn:eol-style = native Modified: httpd/httpd/trunk/modules/ssl/ssl_private.h URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_private.h?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_private.h (original) +++ httpd/httpd/trunk/modules/ssl/ssl_private.h Thu Nov 29 03:18:40 2007 @@ -452,6 +452,12 @@ X509_STORE *crl; modssl_auth_ctx_t auth; + + BOOL ocsp_enabled; /* true if OCSP verification enabled */ + BOOL ocsp_force_default; /* true if the default responder URL is + * used regardless of per-cert URL */ + const char *ocsp_responder; /* default responder URL */ + } modssl_ctx_t; struct SSLSrvConfigRec { @@ -541,6 +547,10 @@ const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, void *, const char *); +const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag); +const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg); +const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, int flag); + /** module initialization */ int ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *); void ssl_init_Engine(server_rec *, apr_pool_t *); @@ -699,6 +709,23 @@ void ssl_var_log_config_register(apr_pool_t *p); #define APR_SHM_MAXSIZE (64 * 1024 * 1024) + +#ifdef HAVE_OCSP +/* Perform OCSP verification using the given context and + * configuration. Returns non-zero on success or zero on failure. On + * failure, the context error code is set. */ +int modssl_verify_ocsp(X509_STORE_CTX *ctx, + SSLSrvConfigRec *sc, server_rec *s, conn_rec *c, + apr_pool_t *pool); + +/* OCSP helper interface; dispatches the given OCSP request to the + * responder at the given URI. Returns the decoded OCSP response + * object, or NULL on error (in which case, errors will have been + * logged). Pool 'p' is used for temporary allocations. */ +OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri, + OCSP_REQUEST *request, + conn_rec *c, apr_pool_t *p); +#endif #endif /* SSL_PRIVATE_H */ /** @} */ Modified: httpd/httpd/trunk/modules/ssl/ssl_toolkit_compat.h URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_toolkit_compat.h?rev=599385&r1=599384&r2=599385&view=diff ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_toolkit_compat.h (original) +++ httpd/httpd/trunk/modules/ssl/ssl_toolkit_compat.h Thu Nov 29 03:18:40 2007 @@ -38,6 +38,12 @@ #include #include #include + +#ifdef HAVE_OCSP +#include +#include +#endif + /** Avoid tripping over an engine build installed globally and detected * when the user points at an explicit non-engine flavor of OpenSSL */ Added: httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c?rev=599385&view=auto ============================================================================== --- httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c (added) +++ httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c Thu Nov 29 03:18:40 2007 @@ -0,0 +1,299 @@ +/* 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +/* This file implements an OCSP client including a toy HTTP/1.0 + * client. Once httpd depends on a real HTTP client library, most of + * this can be thrown away. */ + +#include "ssl_private.h" + +#ifdef HAVE_OCSP + +#include "apr_buckets.h" +#include "apr_uri.h" + +/* Serialize an OCSP request which will be sent to the responder at + * given URI to a memory BIO object, which is returned. */ +static BIO *serialize_request(OCSP_REQUEST *req, const apr_uri_t *uri) +{ + BIO *bio; + int len; + + len = i2d_OCSP_REQUEST(req, NULL); + + bio = BIO_new(BIO_s_mem()); + + BIO_printf(bio, "POST %s%s HTTP/1.0\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + "\r\n", + uri->path, uri->query ? uri->query : "", + uri->hostname, uri->port, len); + + if (i2d_OCSP_REQUEST_bio(bio, req) != 1) { + BIO_free(bio); + return NULL; + } + + return bio; +} + +/* Send the OCSP request serialized into BIO 'request' to the + * responder at given server given by URI. Returns socket object or + * NULL on error. */ +static apr_socket_t *send_request(BIO *request, const apr_uri_t *uri, + conn_rec *c, apr_pool_t *p) +{ + apr_status_t rv; + apr_sockaddr_t *sa; + apr_socket_t *sd; + char buf[HUGE_STRING_LEN]; + int len; + + rv = apr_sockaddr_info_get(&sa, uri->hostname, APR_UNSPEC, uri->port, 0, p); + if (rv) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "could not resolve address of OCSP responder %s", + uri->hostinfo); + return NULL; + } + + /* establish a connection to the OCSP responder */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "connecting to OCSP responder '%s'", uri->hostinfo); + + /* Cycle through address until a connect() succeeds. */ + for (; sa; sa = sa->next) { + rv = apr_socket_create(&sd, sa->family, SOCK_STREAM, APR_PROTO_TCP, p); + if (rv == APR_SUCCESS) { + /* Inherit the default I/O timeout. */ + apr_socket_timeout_set(sd, c->base_server->timeout); + + rv = apr_socket_connect(sd, sa); + if (rv == APR_SUCCESS) { + break; + } + apr_socket_close(sd); + } + } + + if (sa == NULL) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "could not connect to OCSP responder '%s'", + uri->hostinfo); + apr_socket_close(sd); + return NULL; + } + + /* send the request and get a response */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "sending request to OCSP responder"); + + while ((len = BIO_read(request, buf, sizeof buf)) > 0) { + char *wbuf = buf; + apr_size_t remain = len; + + do { + apr_size_t wlen = remain; + + rv = apr_socket_send(sd, wbuf, &wlen); + wbuf += remain; + remain -= wlen; + } while (rv == APR_SUCCESS && remain > 0); + + if (rv) { + apr_socket_close(sd); + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "failed to send request to OCSP responder '%s'", + uri->hostinfo); + return NULL; + } + } + + return sd; +} + +/* Return a pool-alocated NUL-terminated line, with CRLF stripped, + * read from brigade 'bbin' using 'bbout' as temporary storage. */ +static char *get_line(apr_bucket_brigade *bbout, apr_bucket_brigade *bbin, + conn_rec *c, apr_pool_t *p) +{ + apr_status_t rv; + apr_size_t len; + char *line; + + apr_brigade_cleanup(bbout); + + rv = apr_brigade_split_line(bbout, bbin, APR_BLOCK_READ, 8192); + if (rv) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "failed reading line from OCSP server"); + return NULL; + } + + rv = apr_brigade_pflatten(bbout, &line, &len, p); + if (rv) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "failed reading line from OCSP server"); + return NULL; + } + + if (len && line[len-1] != APR_ASCII_LF) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "response header line too long from OCSP server"); + return NULL; + } + + line[len-1] = '\0'; + if (len > 1 && line[len-2] == APR_ASCII_CR) { + line[len-2] = '\0'; + } + + return line; +} + +/* Maximum values to prevent eating RAM forever. */ +#define MAX_HEADERS (256) +#define MAX_CONTENT (2048 * 1024) + +/* Read the OCSP response from the socket 'sd', using temporary memory + * BIO 'bio', and return the decoded OCSP response object, or NULL on + * error. */ +static OCSP_RESPONSE *read_response(apr_socket_t *sd, BIO *bio, conn_rec *c, + apr_pool_t *p) +{ + apr_bucket_brigade *bb, *tmpbb; + OCSP_RESPONSE *response; + char *line; + apr_size_t count; + apr_int64_t code; + + /* Using brigades for response parsing is much simpler than using + * apr_socket_* directly. */ + bb = apr_brigade_create(p, c->bucket_alloc); + tmpbb = apr_brigade_create(p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sd, c->bucket_alloc)); + + line = get_line(tmpbb, bb, c, p); + if (!line || strncmp(line, "HTTP/", 5) + || (line = ap_strchr(line, ' ')) == NULL + || (code = apr_atoi64(++line)) < 200 || code > 299) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, + "bad response from OCSP server: %s", + line ? line : "(none)"); + return NULL; + } + + /* Read till end of headers; don't have to even bother parsing the + * Content-Length since the server is obliged to close the + * connection after the response anyway for HTTP/1.0. */ + count = 0; + while ((line = get_line(tmpbb, bb, c, p)) != NULL && line[0] + && ++count < MAX_HEADERS) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "OCSP response header: %s", line); + } + + if (!line) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "could not read response header from OCSP server"); + return NULL; + } + + /* Read the response body into the memory BIO. */ + count = 0; + while (!APR_BRIGADE_EMPTY(bb)) { + const char *data; + apr_size_t len; + apr_status_t rv; + apr_bucket *e = APR_BRIGADE_FIRST(bb); + + rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + if (rv == APR_EOF || (rv == APR_SUCCESS && len == 0)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "OCSP response: got EOF"); + break; + } + if (rv != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "error reading response from OCSP server"); + return NULL; + } + count += len; + if (count > MAX_CONTENT) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, + "OCSP response size exceeds %u byte limit", + MAX_CONTENT); + return NULL; + } + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "OCSP response: got %" APR_SIZE_T_FMT + " bytes, %" APR_SIZE_T_FMT " total", len, count); + + BIO_write(bio, data, (int)len); + apr_bucket_delete(e); + } + + apr_brigade_destroy(bb); + apr_brigade_destroy(tmpbb); + + /* Finally decode the OCSP response from what's stored in the + * bio. */ + response = d2i_OCSP_RESPONSE_bio(bio, NULL); + if (response == NULL) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, + "failed to decode OCSP response data"); + } + + return response; +} + +OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri, + OCSP_REQUEST *request, + conn_rec *c, apr_pool_t *p) +{ + OCSP_RESPONSE *response = NULL; + apr_socket_t *sd; + BIO *bio; + + bio = serialize_request(request, uri); + if (bio == NULL) { + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, + "could not serialize OCSP request"); + return NULL; + } + + sd = send_request(bio, uri, c, p); + if (sd == NULL) { + /* Errors already logged. */ + BIO_free(bio); + return NULL; + } + + /* Clear the BIO contents, ready for the response. */ + (void)BIO_reset(bio); + + response = read_response(sd, bio, c, p); + + apr_socket_close(sd); + BIO_free(bio); + + return response; +} + +#endif /* HAVE_OCSP */ Propchange: httpd/httpd/trunk/modules/ssl/ssl_util_ocsp.c ------------------------------------------------------------------------------ svn:eol-style = native