mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-03 10:42:07 +00:00
MINOR: ssl: Add "update ssl ocsp-response" cli command
The new "update ssl ocsp-response <certfile>" CLI command allows to update the stored OCSP response for a given certificate. It relies on the http_client which is used to send an HTTP request to the OCSP responder whose URI can be extracted from the certificate. This command won't work for a certificate that did not have a stored OCSP response yet.
This commit is contained in:
parent
c0b4058e7e
commit
eeaa29b36b
@ -3820,6 +3820,19 @@ trace <source> verbosity [<level>]
|
||||
information available at the trace point. The first level above "quiet" is
|
||||
set by default.
|
||||
|
||||
update ssl ocsp-response <certfile>
|
||||
Create an OCSP request for the specified <certfile> and send it to the OCSP
|
||||
responder whose URI should be specified in the "Authority Information Access"
|
||||
section of the certificate. Only the first URI is taken into account. The
|
||||
OCSP response that we should receive in return is then checked and inserted
|
||||
in the local OCSP response tree. This command will only work for certificates
|
||||
that already had a stored OCSP response, either because it was provided
|
||||
during init or if it was previously set through the "set ssl cert" or "set
|
||||
ssl ocsp-response" commands.
|
||||
If the received OCSP response is valid and was properly inserted into the
|
||||
local tree, its contents will be displayed on the standard output. The format
|
||||
is the same as the one described in "show ssl ocsp-response".
|
||||
|
||||
|
||||
9.4. Master CLI
|
||||
---------------
|
||||
|
277
src/ssl_sock.c
277
src/ssl_sock.c
@ -83,6 +83,7 @@
|
||||
#include <haproxy/vars.h>
|
||||
#include <haproxy/xxhash.h>
|
||||
#include <haproxy/istbuf.h>
|
||||
#include <haproxy/http_client.h>
|
||||
|
||||
|
||||
/* ***** READ THIS before adding code here! *****
|
||||
@ -8126,17 +8127,285 @@ end:
|
||||
appctx->svcctx = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ocsp_cli_ctx {
|
||||
struct httpclient *hc;
|
||||
struct ckch_data *ckch_data;
|
||||
uint flags;
|
||||
uint do_update;
|
||||
};
|
||||
|
||||
const struct http_hdr ocsp_request_hdrs[] = {
|
||||
{ IST("Content-Type"), IST("application/ocsp-request") },
|
||||
{ IST_NULL, IST_NULL }
|
||||
};
|
||||
|
||||
void cli_ocsp_res_stline_cb(struct httpclient *hc)
|
||||
{
|
||||
struct appctx *appctx = hc->caller;
|
||||
struct ocsp_cli_ctx *ctx;
|
||||
|
||||
if (!appctx)
|
||||
return;
|
||||
|
||||
ctx = appctx->svcctx;
|
||||
ctx->flags |= HC_F_RES_STLINE;
|
||||
appctx_wakeup(appctx);
|
||||
}
|
||||
|
||||
void cli_ocsp_res_headers_cb(struct httpclient *hc)
|
||||
{
|
||||
struct appctx *appctx = hc->caller;
|
||||
struct ocsp_cli_ctx *ctx;
|
||||
|
||||
if (!appctx)
|
||||
return;
|
||||
|
||||
ctx = appctx->svcctx;
|
||||
ctx->flags |= HC_F_RES_HDR;
|
||||
appctx_wakeup(appctx);
|
||||
}
|
||||
|
||||
void cli_ocsp_res_body_cb(struct httpclient *hc)
|
||||
{
|
||||
struct appctx *appctx = hc->caller;
|
||||
struct ocsp_cli_ctx *ctx;
|
||||
|
||||
if (!appctx)
|
||||
return;
|
||||
|
||||
ctx = appctx->svcctx;
|
||||
ctx->flags |= HC_F_RES_BODY;
|
||||
appctx_wakeup(appctx);
|
||||
}
|
||||
|
||||
void cli_ocsp_res_end_cb(struct httpclient *hc)
|
||||
{
|
||||
struct appctx *appctx = hc->caller;
|
||||
struct ocsp_cli_ctx *ctx;
|
||||
|
||||
if (!appctx)
|
||||
return;
|
||||
|
||||
ctx = appctx->svcctx;
|
||||
ctx->flags |= HC_F_RES_END;
|
||||
appctx_wakeup(appctx);
|
||||
}
|
||||
|
||||
static int cli_parse_update_ocsp_response(char **args, char *payload, struct appctx *appctx, void *private)
|
||||
{
|
||||
int errcode = 0;
|
||||
char *err = NULL;
|
||||
struct ckch_store *ckch_store = NULL;
|
||||
X509 *cert = NULL;
|
||||
struct ocsp_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
|
||||
struct httpclient *hc = NULL;
|
||||
struct buffer *req_url = NULL;
|
||||
struct buffer *req_body = NULL;
|
||||
OCSP_CERTID *certid = NULL;
|
||||
|
||||
if (!*args[3]) {
|
||||
memprintf(&err, "'update ssl ocsp-response' expects a filename\n");
|
||||
return cli_dynerr(appctx, err);
|
||||
}
|
||||
|
||||
req_url = alloc_trash_chunk();
|
||||
if (!req_url) {
|
||||
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
|
||||
errcode |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
req_body = alloc_trash_chunk();
|
||||
if (!req_body) {
|
||||
memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
|
||||
errcode |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* The operations on the CKCH architecture are locked so we can
|
||||
* manipulate ckch_store and ckch_inst */
|
||||
if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
|
||||
memprintf(&err, "%sCan't update the certificate!\nOperations on certificates are currently locked!\n", err ? err : "");
|
||||
errcode |= ERR_ALERT | ERR_FATAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ckch_store = ckchs_lookup(args[3]);
|
||||
|
||||
if (!ckch_store) {
|
||||
memprintf(&err, "%sCkch_store not found!\n", err ? err : "");
|
||||
errcode |= ERR_ALERT | ERR_FATAL;
|
||||
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ctx->ckch_data = ckch_store->data;
|
||||
|
||||
cert = ckch_store->data->cert;
|
||||
|
||||
if (ssl_ocsp_get_uri_from_cert(cert, req_url, &err)) {
|
||||
errcode |= ERR_ALERT | ERR_FATAL;
|
||||
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
|
||||
goto end;
|
||||
}
|
||||
|
||||
certid = OCSP_cert_to_id(NULL, ctx->ckch_data->cert, ctx->ckch_data->ocsp_issuer);
|
||||
if (certid == NULL) {
|
||||
memprintf(&err, "%sOCSP_cert_to_id() error\n", err ? err : "");
|
||||
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* From here on the lock is not needed anymore. */
|
||||
HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
|
||||
|
||||
/* Create ocsp request */
|
||||
if (ssl_ocsp_create_request_details(certid, req_url, req_body, &err) != 0) {
|
||||
memprintf(&err, "%sCreate ocsp request error\n", err ? err : "");
|
||||
goto end;
|
||||
}
|
||||
|
||||
hc = httpclient_new(appctx, b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, ist2(b_orig(req_url), b_data(req_url)));
|
||||
if (!hc) {
|
||||
memprintf(&err, "%sCan't allocate httpclient\n", err ? err : "");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, b_data(req_body) ? ocsp_request_hdrs : NULL,
|
||||
ist2(b_orig(req_body), b_data(req_body))) != ERR_NONE) {
|
||||
memprintf(&err, "%shttpclient_req_gen() error\n", err ? err : "");
|
||||
goto end;
|
||||
}
|
||||
|
||||
hc->ops.res_stline = cli_ocsp_res_stline_cb;
|
||||
hc->ops.res_headers = cli_ocsp_res_headers_cb;
|
||||
hc->ops.res_payload = cli_ocsp_res_body_cb;
|
||||
hc->ops.res_end = cli_ocsp_res_end_cb;
|
||||
|
||||
ctx->hc = hc; /* store the httpclient ptr in the applet */
|
||||
ctx->flags = 0;
|
||||
|
||||
if (!httpclient_start(hc)) {
|
||||
memprintf(&err, "%shttpclient_start() error\n", err ? err : "");
|
||||
goto end;
|
||||
}
|
||||
|
||||
free_trash_chunk(req_url);
|
||||
|
||||
return 0;
|
||||
|
||||
end:
|
||||
free_trash_chunk(req_url);
|
||||
|
||||
if (errcode & ERR_CODE) {
|
||||
return cli_dynerr(appctx, memprintf(&err, "%sCan't send ocsp request for %s!\n", err ? err : "", args[3]));
|
||||
}
|
||||
return cli_dynmsg(appctx, LOG_NOTICE, err);
|
||||
}
|
||||
|
||||
static int cli_io_handler_update_ocsp_response(struct appctx *appctx)
|
||||
{
|
||||
struct ocsp_cli_ctx *ctx = appctx->svcctx;
|
||||
struct httpclient *hc = ctx->hc;
|
||||
|
||||
if (ctx->flags & HC_F_RES_STLINE) {
|
||||
if (hc->res.status != 200) {
|
||||
chunk_printf(&trash, "OCSP response error (status %d)\n", hc->res.status);
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
goto more;
|
||||
goto end;
|
||||
}
|
||||
ctx->flags &= ~HC_F_RES_STLINE;
|
||||
}
|
||||
|
||||
if (ctx->flags & HC_F_RES_HDR) {
|
||||
struct http_hdr *hdr;
|
||||
int found = 0;
|
||||
/* Look for "Content-Type" header which should have
|
||||
* "application/ocsp-response" value. */
|
||||
for (hdr = hc->res.hdrs; isttest(hdr->v); hdr++) {
|
||||
if (isteqi(hdr->n, ist("Content-Type")) &&
|
||||
isteqi(hdr->v, ist("application/ocsp-response"))) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "Missing 'Content-Type: application/ocsp-response' header\n");
|
||||
goto end;
|
||||
}
|
||||
ctx->flags &= ~HC_F_RES_HDR;
|
||||
}
|
||||
|
||||
if (ctx->flags & HC_F_RES_BODY) {
|
||||
/* Wait until the full body is received and HC_F_RES_END flag is
|
||||
* set. */
|
||||
}
|
||||
|
||||
/* we must close only if F_END is the last flag */
|
||||
if (ctx->flags & HC_F_RES_END) {
|
||||
char *err = NULL;
|
||||
|
||||
if (ssl_ocsp_check_response(ctx->ckch_data->chain, ctx->ckch_data->ocsp_issuer, &hc->res.buf, &err)) {
|
||||
chunk_printf(&trash, "%s", err);
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
goto more;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ssl_sock_update_ocsp_response(&hc->res.buf, &err) != 0) {
|
||||
chunk_printf(&trash, "%s", err);
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
goto more;
|
||||
goto end;
|
||||
}
|
||||
|
||||
chunk_reset(&trash);
|
||||
|
||||
if (ssl_ocsp_response_print(&hc->res.buf, &trash))
|
||||
goto end;
|
||||
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
goto more;
|
||||
ctx->flags &= ~HC_F_RES_BODY;
|
||||
ctx->flags &= ~HC_F_RES_END;
|
||||
goto end;
|
||||
}
|
||||
|
||||
more:
|
||||
if (!ctx->flags)
|
||||
applet_have_no_more_data(appctx);
|
||||
return 0;
|
||||
end:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cli_release_update_ocsp_response(struct appctx *appctx)
|
||||
{
|
||||
struct ocsp_cli_ctx *ctx = appctx->svcctx;
|
||||
struct httpclient *hc = ctx->hc;
|
||||
|
||||
/* Everything possible was printed on the CLI, we can destroy the client */
|
||||
httpclient_stop_and_destroy(hc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* register cli keywords */
|
||||
static struct cli_kw_list cli_kws = {{ },{
|
||||
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
||||
{ { "show", "tls-keys", NULL }, "show tls-keys [id|*] : show tls keys references or dump tls ticket keys when id specified", cli_parse_show_tlskeys, cli_io_handler_tlskeys_files },
|
||||
{ { "set", "ssl", "tls-key", NULL }, "set ssl tls-key [id|file] <key> : set the next TLS key for the <id> or <file> listener to <key>", cli_parse_set_tlskeys, NULL },
|
||||
{ { "show", "tls-keys", NULL }, "show tls-keys [id|*] : show tls keys references or dump tls ticket keys when id specified", cli_parse_show_tlskeys, cli_io_handler_tlskeys_files },
|
||||
{ { "set", "ssl", "tls-key", NULL }, "set ssl tls-key [id|file] <key> : set the next TLS key for the <id> or <file> listener to <key>", cli_parse_set_tlskeys, NULL },
|
||||
#endif
|
||||
{ { "set", "ssl", "ocsp-response", NULL }, "set ssl ocsp-response <resp|payload> : update a certificate's OCSP Response from a base64-encode DER", cli_parse_set_ocspresponse, NULL },
|
||||
{ { "set", "ssl", "ocsp-response", NULL }, "set ssl ocsp-response <resp|payload> : update a certificate's OCSP Response from a base64-encode DER", cli_parse_set_ocspresponse, NULL },
|
||||
|
||||
{ { "show", "ssl", "ocsp-response", NULL },"show ssl ocsp-response [id] : display the IDs of the OCSP responses used in memory, or the details of a single OCSP response", cli_parse_show_ocspresponse, cli_io_handler_show_ocspresponse, NULL },
|
||||
{ { "show", "ssl", "ocsp-response", NULL }, "show ssl ocsp-response [id] : display the IDs of the OCSP responses used in memory, or the details of a single OCSP response", cli_parse_show_ocspresponse, cli_io_handler_show_ocspresponse, NULL },
|
||||
#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
|
||||
{ { "update", "ssl", "ocsp-response", NULL }, "update ssl ocsp-response <certfile> : send ocsp request and update stored ocsp response", cli_parse_update_ocsp_response, cli_io_handler_update_ocsp_response, cli_release_update_ocsp_response },
|
||||
#endif
|
||||
#ifdef HAVE_SSL_PROVIDERS
|
||||
{ { "show", "ssl", "providers", NULL }, "show ssl providers : show loaded SSL providers", NULL, cli_io_handler_show_providers },
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user