/* 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"
#ifndef OPENSSL_NO_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, const apr_uri_t *proxy_uri)
{
BIO *bio; int len;
len = i2d_OCSP_REQUEST(req, NULL);
bio = BIO_new(BIO_s_mem());
BIO_printf(bio, "POST "); /* Use full URL instead of URI in case of a request through a proxy */ if (proxy_uri) {
BIO_printf(bio, "http://%s:%d",
uri->hostname, uri->port);
}
BIO_printf(bio, "%s%s%s HTTP/1.0\r\n" "Host: %s:%d\r\n" "Content-Type: application/ocsp-request\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n",
uri->path ? uri->path : "/",
uri->query ? "?" : "", 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,
apr_interval_time_t timeout,
conn_rec *c, apr_pool_t *p, const apr_uri_t *proxy_uri)
{
apr_status_t rv;
apr_sockaddr_t *sa;
apr_socket_t *sd; char buf[HUGE_STRING_LEN]; int len; const apr_uri_t *next_hop_uri;
/* establish a connection to the OCSP responder */
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01973) "connecting to %s '%s'",
proxy_uri ? "proxy" : "OCSP responder",
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) {
apr_socket_timeout_set(sd, timeout);
if (rv) {
apr_socket_close(sd);
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01976) "failed to send request to OCSP responder '%s'",
uri->hostinfo); return NULL;
}
}
return sd;
}
/* Return a pool-allocated NUL-terminated line, with CRLF stripped,
* read from brigade 'bbin' using 'bbout' as temporary storage. */ staticchar *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, APLOGNO(01977) "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, APLOGNO(01978) "failed reading line from OCSP server"); return NULL;
}
if (len == 0) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02321) "empty response from OCSP server"); return NULL;
}
if (line[len-1] != APR_ASCII_LF) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01979) "response header line too long from OCSP server"); return NULL;
}
/* 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, APLOGNO(01980) "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, APLOGNO(01981) "OCSP response header: %s", line);
}
if (count == MAX_HEADERS) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01982) "could not read response headers from OCSP server, " "exceeded maximum count (%u)", MAX_HEADERS); return NULL;
} elseif (!line) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01983) "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)) { constchar *data;
apr_size_t len;
apr_status_t rv;
apr_bucket *e = APR_BRIGADE_FIRST(bb);