Changeset 16ad0eb in mod_gnutls


Ignore:
Timestamp:
Jun 14, 2016, 2:40:13 PM (2 years ago)
Author:
Thomas Klute <thomas2.klute@…>
Branches:
debian/master, debian/stretch-backports, master, upstream
Children:
82745d1
Parents:
04addef
Message:

Perform OCSP request over HTTP

Finally the whole stack is there! Quite a bit of polishing left to do
(especially regarding timing/timeouts), but sending a request and
caching the response work.

Location:
src
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • src/Makefile.am

    r04addef r16ad0eb  
    77
    88mod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c \
    9         gnutls_config.c gnutls_hooks.c gnutls_ocsp.c
     9        gnutls_config.c gnutls_hooks.c gnutls_ocsp.c gnutls_util.c
    1010mod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS}
    1111mod_gnutls_la_LDFLAGS = -module -avoid-version ${MODULE_LIBS}
    12 noinst_HEADERS = gnutls_cache.h gnutls_ocsp.h
     12noinst_HEADERS = gnutls_cache.h gnutls_ocsp.h gnutls_util.h
    1313
    1414apmodpkglib_LTLIBRARIES = mod_gnutls.la
  • src/gnutls_ocsp.c

    r04addef r16ad0eb  
    1818#include "mod_gnutls.h"
    1919#include "gnutls_cache.h"
     20#include "gnutls_util.h"
    2021
    2122#include <apr_escape.h>
     
    2829APLOG_USE_MODULE(gnutls);
    2930#endif
     31
     32/* maximum supported OCSP response size, 8K should be plenty */
     33#define OCSP_RESP_SIZE_MAX (8 * 1024)
     34#define OCSP_REQ_TYPE "application/ocsp-request"
     35#define OCSP_RESP_TYPE "application/ocsp-response"
    3036
    3137
     
    8490 * Returns GNUTLS_E_SUCCESS, or a GnuTLS error code.
    8591 */
    86 int mgs_create_ocsp_request(server_rec *s, gnutls_datum_t *req,
     92static int mgs_create_ocsp_request(server_rec *s, gnutls_datum_t *req,
    8793                            gnutls_datum_t *nonce)
    88 {
    89     if (req == NULL || s == NULL)
    90         return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
    91 
     94    __attribute__((nonnull(1, 2)));
     95static int mgs_create_ocsp_request(server_rec *s, gnutls_datum_t *req,
     96                            gnutls_datum_t *nonce)
     97{
    9298    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
    9399        ap_get_module_config(s->module_config, &gnutls_module);
     
    168174    return ret;
    169175}
     176
    170177
    171178
     
    348355
    349356
     357static apr_status_t do_ocsp_request(apr_pool_t *p, server_rec *s,
     358                                    gnutls_datum_t *request,
     359                                    gnutls_datum_t *response)
     360    __attribute__((nonnull));
     361static apr_status_t do_ocsp_request(apr_pool_t *p, server_rec *s,
     362                                    gnutls_datum_t *request,
     363                                    gnutls_datum_t *response)
     364{
     365    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
     366        ap_get_module_config(s->module_config, &gnutls_module);
     367
     368    if (apr_strnatcmp(sc->ocsp->uri->scheme, "http"))
     369    {
     370        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
     371                     "Scheme \"%s\" is not supported for OCSP requests!",
     372                     sc->ocsp->uri->scheme);
     373        return APR_EINVAL;
     374    }
     375
     376    const char* header = http_post_header(p, sc->ocsp->uri,
     377                                          OCSP_REQ_TYPE, OCSP_RESP_TYPE,
     378                                          request->size);
     379    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
     380                 "OCSP POST header: %s", header);
     381
     382    /* Find correct port */
     383    apr_port_t port = sc->ocsp->uri->port ?
     384        sc->ocsp->uri->port : apr_uri_port_of_scheme(sc->ocsp->uri->scheme);
     385
     386    apr_sockaddr_t *sa;
     387    apr_status_t rv = apr_sockaddr_info_get(&sa, sc->ocsp->uri->hostname,
     388                                            APR_UNSPEC, port, 0, p);
     389    if (rv != APR_SUCCESS)
     390    {
     391        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     392                     "Address resolution for OCSP responder %s failed.",
     393                     sc->ocsp->uri->hostinfo);
     394    }
     395
     396    /* There may be multiple answers, try them in order until one
     397     * works. */
     398    apr_socket_t *sock;
     399    /* TODO: configurable timeout */
     400    apr_interval_time_t timeout = apr_time_from_sec(2);
     401    while (sa)
     402    {
     403        rv = apr_socket_create(&sock, sa->family, SOCK_STREAM,
     404                               APR_PROTO_TCP, p);
     405        if (rv == APR_SUCCESS)
     406        {
     407            apr_socket_timeout_set(sock, timeout);
     408            rv = apr_socket_connect(sock, sa);
     409            if (rv == APR_SUCCESS)
     410                /* Connected! */
     411                break;
     412            apr_socket_close(sock);
     413        }
     414        sa = sa->next;
     415    }
     416    /* If the socket is connected, 'sa' points at the matching
     417     * address. */
     418    if (sa == NULL)
     419    {
     420        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     421                     "Connecting to OCSP responder %s failed.",
     422                     sc->ocsp->uri->hostinfo);
     423        return rv;
     424    }
     425
     426    /* Header is generated locally, so strlen() is safe. */
     427    rv = sock_send_buf(sock, header, strlen(header));
     428    if (rv == APR_SUCCESS)
     429        rv = sock_send_buf(sock, (char*) request->data, request->size);
     430    /* catches errors from both header and request */
     431    if (rv != APR_SUCCESS)
     432    {
     433        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     434                     "Sending OCSP request failed.");
     435        goto exit;
     436    }
     437
     438    /* Prepare bucket brigades to read the response header. BBs make
     439     * it easy to split the header into lines. */
     440    apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p);
     441    apr_bucket_brigade *bb = apr_brigade_create(p, ba);
     442    /* will carry split response headers */
     443    apr_bucket_brigade *rh = apr_brigade_create(p, ba);
     444
     445    APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sock, ba));
     446    /* The first line in the response header must be the status, check
     447     * for OK status code. Line looks similar to "HTTP/1.0 200 OK". */
     448    const char *h = read_line(p, bb, rh);
     449    const char *code = 0;
     450    if (h == NULL
     451        || strncmp(h, "HTTP/", 5)
     452        || (code = ap_strchr(h, ' ')) == NULL
     453        || apr_atoi64(code + 1) != HTTP_OK)
     454    {
     455        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     456                     "Invalid HTTP response status from %s: %s",
     457                     sc->ocsp->uri->hostinfo, h);
     458        rv = APR_ECONNRESET;
     459        goto exit;
     460    }
     461    /* Read remaining header lines */
     462    for (h = read_line(p, bb, rh); h != NULL && apr_strnatcmp(h, "") != 0;
     463         h = read_line(p, bb, rh))
     464    {
     465        ap_log_error(APLOG_MARK, APLOG_TRACE2, APR_SUCCESS, s,
     466                     "Received header: %s", h);
     467    }
     468    /* The last header line should be empty (""), NULL indicates an
     469     * error. */
     470    if (h == NULL)
     471    {
     472        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     473                     "Error while reading HTTP response header from %s",
     474                     sc->ocsp->uri->hostinfo);
     475        rv = APR_ECONNRESET;
     476        goto exit;
     477    }
     478
     479    /* Headers have been consumed, the rest of the available data
     480     * should be the actual response. */
     481    apr_size_t len = OCSP_RESP_SIZE_MAX;
     482    char buf[OCSP_RESP_SIZE_MAX];
     483    /* apr_brigade_pflatten() can allocate directly from the pool, but
     484     * the documentation does not describe a way to limit the size of
     485     * the buffer, which is necessary here to prevent DoS by endless
     486     * response. Use apr_brigade_flatten() to read to a stack pool,
     487     * then create a copy to return. */
     488    rv = apr_brigade_flatten(bb, buf, &len);
     489    if (rv != APR_SUCCESS)
     490    {
     491        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
     492                     "Failed to read OCSP response.");
     493        goto exit;
     494    }
     495
     496    /* With the length restriction this really should not happen. */
     497    if (__builtin_add_overflow(len, 0, &response->size))
     498    {
     499        response->data = NULL;
     500        rv = APR_ENOMEM;
     501    }
     502    else
     503    {
     504        response->data = apr_pmemdup(p, buf, len);
     505    }
     506
     507 exit:
     508    apr_socket_close(sock);
     509    return rv;
     510}
     511
     512
     513
    350514apr_status_t mgs_cache_ocsp_response(server_rec *s)
    351515{
     
    369533    }
    370534
    371     /* TODO: actually send this request to sc->ocsp->uri and parse the
    372      * received response (including nonce) */
    373535    gnutls_datum_t req;
    374536    int ret = mgs_create_ocsp_request(s, &req, NULL);
     
    382544    }
    383545
    384     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
    385                  "Loading OCSP response from %s",
    386                  sc->ocsp_response_file);
    387     apr_file_t *file;
    388     apr_finfo_t finfo;
    389     apr_size_t br = 0;
    390     rv = apr_file_open(&file, sc->ocsp_response_file,
    391                        APR_READ | APR_BINARY, APR_OS_DEFAULT, tmp);
     546    gnutls_datum_t resp;
     547    rv = do_ocsp_request(tmp, s, &req, &resp);
    392548    if (rv != APR_SUCCESS)
    393549    {
     550        /* do_ocsp_request() does its own error logging. */
    394551        apr_pool_destroy(tmp);
    395552        return rv;
    396553    }
    397     rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file);
    398     if (rv != APR_SUCCESS)
    399     {
    400         apr_pool_destroy(tmp);
    401         return rv;
    402     }
    403 
    404     gnutls_datum_t resp;
    405     resp.data = apr_palloc(tmp, finfo.size);
    406     rv = apr_file_read_full(file, resp.data, finfo.size, &br);
    407     if (rv != APR_SUCCESS)
    408     {
    409         apr_pool_destroy(tmp);
    410         return rv;
    411     }
    412     apr_file_close(file);
    413     /* safe integer type conversion */
    414     if (__builtin_add_overflow(br, 0, &resp.size))
    415     {
    416         apr_pool_destroy(tmp);
    417         return APR_EINVAL;
    418     }
     554    /* TODO: check nonce */
     555
     556    /* TODO: separate option to enable/disable OCSP stapling, restore
     557     * reading response from file for debugging/expert use. */
    419558
    420559    apr_time_t expiry;
Note: See TracChangeset for help on using the changeset viewer.