From: Remi Tricot-Le Breton Date: Thu, 1 Oct 2026 14:59:27 +0000 (+0200) Subject: MEDIUM: ocsp: Create new do-ocsp-request action X-Git-Tag: v3.5-dev9~220 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=27f1221e3a2677ebd98354806e5cafdd4d854d16;p=haproxy.git MEDIUM: ocsp: Create new do-ocsp-request action Create a new 'do-ocsp-request' http-request and "tcp-request content" action. When an SSL handshake occurred and was successful, the action checks for a client certificate. If any is provided and it contains an OCSP URI, we build an OCSP request and send it to the responder. No other action is performed until a response is processed or a timeout occurs. When the response is received, we check its signature thanks to an issuer certificate that should be in the issuer tree (see 'issers-chain-path' option), check its expiration dates and the client certificate's status, and put the status and error reason (if any) in the user provided "status-var" and "reason-var". By default the action denies the request is the received status is not 'good' but this can be tuned thanks to the 'no-deny' option. --- diff --git a/include/haproxy/ssl_ocsp-t.h b/include/haproxy/ssl_ocsp-t.h index a6ca02871..ed682d745 100644 --- a/include/haproxy/ssl_ocsp-t.h +++ b/include/haproxy/ssl_ocsp-t.h @@ -84,6 +84,17 @@ struct ocsp_cbk_arg { }; }; +struct ocsp_clt_check_ctx { + struct task *task; + struct httpclient *hc; + SSL *ssl; + OCSP_CERTID *cid; + X509 *issuer; + int state; + unsigned int ocsp_error; + int ocsp_status; +}; + extern struct eb_root cert_ocsp_tree; extern struct eb_root ocsp_update_tree; extern struct task *ocsp_update_task; diff --git a/include/haproxy/ssl_ocsp.h b/include/haproxy/ssl_ocsp.h index d60f51080..94c55e303 100644 --- a/include/haproxy/ssl_ocsp.h +++ b/include/haproxy/ssl_ocsp.h @@ -59,6 +59,8 @@ int __ssl_ocsp_update_insert_unlocked(struct certificate_ocsp *ocsp); int ocsp_update_init(void *value, char *buf, struct ckch_store *s, int cli, const char *filename, int linenum, char **err); +void clear_ocsp_clt_check_ctx(struct ocsp_clt_check_ctx **ctx); + #endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */ #endif /* USE_OPENSSL */ diff --git a/include/haproxy/stream-t.h b/include/haproxy/stream-t.h index e39167ba0..24bc09fae 100644 --- a/include/haproxy/stream-t.h +++ b/include/haproxy/stream-t.h @@ -357,6 +357,8 @@ struct stream { } resolv_ctx; /* context information for DNS resolution */ struct be_counters_shared_tg *be_tgcounters; /* pointer to current thread group shared backend counters */ struct be_counters_shared_tg *sv_tgcounters; /* pointer to current thread group shared server counters */ + + struct ocsp_clt_check_ctx *ocsp_clt_check_ctx; /* used for client ssl certificate's ocsp check */ }; #endif /* _HAPROXY_STREAM_T_H */ diff --git a/src/ssl_ocsp.c b/src/ssl_ocsp.c index adb224025..4b8592457 100644 --- a/src/ssl_ocsp.c +++ b/src/ssl_ocsp.c @@ -66,6 +66,10 @@ #include #include #include +#include +#include +#include +#include #ifdef HAVE_SSL_OCSP @@ -97,7 +101,8 @@ typedef enum { OCSP_CHECK_ERR_NO_RESPONSE, OCSP_CHECK_ERR_UNABLE_TO_GET_ISSUER_CERT, OCSP_CHECK_ERR_WRONG_CID, - OCSP_CHECK_ERR_MEMORY + OCSP_CHECK_ERR_MEMORY, + OCSP_CHECK_ERR_TIMEOUT } ocsp_check_error; #ifndef OPENSSL_NO_OCSP @@ -1005,6 +1010,8 @@ end: struct task *ocsp_update_task __read_mostly = NULL; static struct proxy *httpclient_ocsp_update_px; +static struct proxy *httpclient_clt_ocsp_px; + static struct ssl_ocsp_task_ctx { struct certificate_ocsp *cur_ocsp; @@ -1352,7 +1359,7 @@ static struct task *ssl_ocsp_update_responses(struct task *task, void *context, goto http_error; } - if (ssl_ocsp_check_response(ocsp->chain, ocsp->issuer, &hc->res.buf, &err)) { + if (ssl_ocsp_check_response(ocsp->chain, ocsp->issuer, &hc->res.buf, NULL, &err)) { ctx->update_status = OCSP_UPDT_ERR_CHECK; goto http_error; } @@ -1492,6 +1499,7 @@ static struct task *ssl_ocsp_update_responses(struct task *task, void *context, hc->ops.res_headers = ocsp_update_response_headers_cb; hc->ops.res_end = ocsp_update_response_end_cb; + if (!httpclient_start(hc)) { goto leave; } @@ -1582,9 +1590,24 @@ static int ssl_ocsp_update_precheck() return ERR_NONE; } + +/* + * Initialize the proxy for the client certificate OCSP check. + */ +static int ssl_clt_ocsp_precheck() +{ + httpclient_clt_ocsp_px = httpclient_create_proxy(""); + if (!httpclient_clt_ocsp_px) + return ERR_RETRYABLE; + httpclient_clt_ocsp_px->logformat.str = httpclient_log_format; + + return ERR_NONE; +} + /* initialize the proxy and servers for the HTTP client */ REGISTER_PRE_CHECK(ssl_ocsp_update_precheck); +REGISTER_PRE_CHECK(ssl_clt_ocsp_precheck); static int cli_parse_update_ocsp_response(char **args, char *payload, struct appctx *appctx, void *private) @@ -1670,6 +1693,531 @@ end: #endif /* !defined OPENSSL_IS_BORINGSSL */ +/* OCSP check statemachine states */ +enum { + OCSP_CLT_INIT, + OCSP_CLT_WAITING, + OCSP_CLT_DONE, + OCSP_CLT_ERR, + OCSP_CLT_NO_OCSP +}; + +/* Struct used to store "do-ocsp-request" action parameters */ +struct ocsp_check_conf { + struct ist status_var; + struct ist reason_var; + int deny; +}; + +/* Convert OCSP error to string */ +static const char *ssl_ocsp_check_err_to_str(int errcode) +{ + switch(errcode) { + case OCSP_CHECK_ERR_OK: + return ""; + case OCSP_CHECK_ERR_VERIFY_FAILED: + return "OCSP verification failed"; + case OCSP_CHECK_ERR_CERT_UNKNOWN: + return "OCSP unknown cert"; + case OCSP_CHECK_ERR_RESP_INVALID: + return "OCSP response(s) invalid"; + case OCSP_CHECK_ERR_SIGNATURE_FAILURE: + return "OCSP response signature verification failure"; + case OCSP_CHECK_ERR_NOT_YET_VALID: + return "OCSP response not yet valid (contains a date in the future)"; + case OCSP_CHECK_ERR_HAS_EXPIRED: + return "OCSP response has expired"; + case OCSP_CHECK_ERR_NO_RESPONSE: + return "no OCSP response available for certificate or responder unreachable"; + case OCSP_CHECK_ERR_UNABLE_TO_GET_ISSUER_CERT: + return "unable to get issuer certificate"; + case OCSP_CHECK_ERR_WRONG_CID: + return "CID mismatch"; + case OCSP_CHECK_ERR_MEMORY: + return "Memory allocation failed"; + case OCSP_CHECK_ERR_TIMEOUT: + return "Timeout"; + default: + return ""; + } +} + +/* + * Look for the issuer certificate for in the issuer tree built out of + * the "issuer-chain-path" parameters. The found issuer is set in + * if found. + * Return 0 in case of success, 1 otherwise. + */ +static int get_issuer_from_chain(X509 *cert, X509 **issuer_cert) +{ + struct issuer_chain *issuer_chain = NULL; + STACK_OF(X509) *chain = NULL; + int i = 0; + + if (!issuer_cert) + return 1; + *issuer_cert = NULL; + + issuer_chain = ssl_get0_issuer_chain(cert); + if (issuer_chain) + chain = issuer_chain->chain; + + if (chain) { + /* check if one of the certificate of the chain is the issuer */ + for (i = 0; i < sk_X509_num(chain); i++) { + X509 *ti = sk_X509_value(chain, i); + if (X509_check_issued(ti, cert) == X509_V_OK) { + *issuer_cert = ti; + break; + } + } + } + + return (*issuer_cert == NULL); +} + +/* + * Free the contents of without freeing the structure itself. + */ +void clear_ocsp_clt_check_ctx(struct ocsp_clt_check_ctx **ctx) +{ + if (!ctx || !*ctx) + return; + + (*ctx)->task = NULL; + + if ((*ctx)->hc) { + httpclient_stop_and_destroy((*ctx)->hc); + (*ctx)->hc = NULL; + } + + SSL_free((*ctx)->ssl); + (*ctx)->ssl = NULL; + + OCSP_CERTID_free((*ctx)->cid); + (*ctx)->cid = NULL; + + X509_free((*ctx)->issuer); + (*ctx)->issuer = NULL; + + (*ctx)->state = OCSP_CLT_INIT; + (*ctx)->ocsp_status = V_OCSP_CERTSTATUS_UNKNOWN; +} + + +/* + * Main http client callback used in OCSP client certificate check. + */ +static void ocsp_clt_httpclient_end(struct httpclient *hc) +{ + struct ocsp_clt_check_ctx *ctx = hc->caller; + + if (!ctx) + return; + + ctx->state = OCSP_CLT_DONE; + + task_wakeup(ctx->task, TASK_WOKEN_MSG); +} + +/* + * Create an OCSP request for client certificate extracted from . + * The OCSP responder uri is extracted from the certificate. The certificate's + * issuer should already be in the issuer tree (see 'issuers-chain-path' + * option). + * Set as a limit for the http_client used to send the request. + * Return 0 in case of success, 1 otherwise. + */ +int ssl_ocsp_send_request(struct ocsp_clt_check_ctx **ctx, SSL *ssl, int timeout) +{ + struct buffer *ocsp_uri = NULL; + struct buffer *req_body = NULL; + struct httpclient *hc = NULL; + X509 *cert = NULL; + char *err = NULL; + + int retval = 1; + + if (!ssl) + goto end; + + cert = SSL_get_peer_certificate(ssl); + if (!cert) { + retval = 0; /* Not an error, nothing to do */ + (*ctx)->state = OCSP_CLT_NO_OCSP; + goto end; + } + + ocsp_uri = alloc_trash_chunk(); + if (!ocsp_uri) + goto end; + + if (ssl_ocsp_get_uri_from_cert(cert, ocsp_uri, &err)) { + retval = 0; /* Not an error, nothing to do */ + (*ctx)->state = OCSP_CLT_NO_OCSP; + goto end; + } + + if ((*ctx)->issuer) { + X509_free((*ctx)->issuer); + (*ctx)->issuer = NULL; + } + + if (get_issuer_from_chain(cert, &(*ctx)->issuer)) { + (*ctx)->ocsp_error = OCSP_CHECK_ERR_UNABLE_TO_GET_ISSUER_CERT; + goto end; + } + X509_up_ref((*ctx)->issuer); + + OCSP_CERTID_free((*ctx)->cid); + (*ctx)->cid = OCSP_cert_to_id(0, cert, (*ctx)->issuer); + if (!(*ctx)->cid) + goto end; + + req_body = alloc_trash_chunk(); + if (!req_body) + goto end; + + /* Create ocsp request */ + if (ssl_ocsp_create_request_details((*ctx)->cid, ocsp_uri, req_body, NULL) != 0) + goto end; + + /* Depending on the processing that occurred in + * ssl_ocsp_create_request_details we could either have to send + * a GET or a POST request. */ + hc = httpclient_new_from_proxy(httpclient_clt_ocsp_px, (*ctx), + b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, + ist2(b_orig(ocsp_uri), b_data(ocsp_uri))); + if (!hc) + goto end; + + httpclient_set_timeout(hc, timeout); + + (*ctx)->hc = hc; + + if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, + b_data(req_body) ? ocsp_request_hdrs : NULL, + b_data(req_body) ? ist2(b_orig(req_body), b_data(req_body)) : IST_NULL) != ERR_NONE) { + goto end; + } + + hc->ops.res_end = ocsp_clt_httpclient_end; + + if (!httpclient_start(hc)) + goto end; + + retval = 0; + +end: + free_trash_chunk(ocsp_uri); + free_trash_chunk(req_body); + if (retval) + clear_ocsp_clt_check_ctx(ctx); + free(err); + X509_free(cert); + return retval; +} + +/* + * Check the contents of an OCSP response found in the response buffer of the + * http_client stored in . The response is not stored in the OCSP response + * tree, the 'ssl_sock_load_ocsp_response' is called with a "fake" ocsp_response + * in order to make use of the validity checks and parsing already performed + * inside. + * Return 0 in case of success, 1 otherwise. + */ +int ssl_ocsp_check_ocsp_response(struct ocsp_clt_check_ctx *ctx) +{ + struct httpclient *hc = ctx->hc; + struct certificate_ocsp ocsp = {}; + struct http_hdr *hdr; + int found = 0; + int retval = 1; + char *err = NULL; + + if (!hc || hc->res.status != 200) { + ctx->ocsp_error = OCSP_CHECK_ERR_NO_RESPONSE; + goto leave; + } + + /* 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) { + ctx->ocsp_error = OCSP_CHECK_ERR_NO_RESPONSE; + goto leave; + } + + /* + * Verify the ocsp_response against the issuer certificate. + * The actual parsing of the response is done in + * ssl_sock_load_ocsp_response. + */ + if (ssl_ocsp_check_response(NULL, ctx->issuer, &hc->res.buf, + &ctx->ocsp_error, &err)) + goto leave; + + /* + * Provide a local ocsp_response structure in order to use the validity + * checks and parsing of the response done in this function without + * trying to insert the response in the tree. + */ + if (ssl_sock_load_ocsp_response(&hc->res.buf, &ocsp, ctx->cid, + &ctx->ocsp_status, + &ctx->ocsp_error, &err)) + goto leave; + + retval = 0; + +leave: + if (hc) + httpclient_stop_and_destroy(hc); + + if (retval && !ctx->ocsp_error) + ctx->ocsp_error = OCSP_CHECK_ERR_VERIFY_FAILED; + + ctx->hc = NULL; + free(err); + ssl_sock_free_ocsp_data(&ocsp); + return retval; +} + +/* + * Check the validity of the client certificate from by sending an OCSP + * request in 'ssl_ocsp_send_request' and checking the received OCSP response + * (if any) in 'ssl_ocsp_check_ocsp_response'. The task will be woken up by the + * http_client main callback or in case a timeout is reached. + * Returns 0 in case of success, 1 if the caller needs to yield (ongoing ocsp + * request), -1 in case of error. + */ +int ssl_ocsp_check_client_cert(struct task *task, SSL *ssl, struct ocsp_clt_check_ctx **ctx, int timeout) +{ + int retval = 1; + + if (!ctx) + return -1; + + if (!*ctx) { + *ctx = calloc(1, sizeof(**ctx)); + if (!*ctx) { + retval = -1; + goto end; + } + if (!ssl) { + retval = -1; + goto end; + } + } + + (*ctx)->ocsp_status = V_OCSP_CERTSTATUS_UNKNOWN; + + (*ctx)->task = task; + + switch((*ctx)->state) { + case OCSP_CLT_INIT: + if (ssl_ocsp_send_request(ctx, ssl, timeout)) { + if (!(*ctx)->ocsp_error) + (*ctx)->ocsp_error = OCSP_CHECK_ERR_VERIFY_FAILED; + (*ctx)->state = OCSP_CLT_ERR; + retval = -1; + goto end; + } + else if ((*ctx)->state != OCSP_CLT_NO_OCSP) + (*ctx)->state = OCSP_CLT_WAITING; + break; + case OCSP_CLT_WAITING: + /* Nothing to do, OCSP response was not received yet */ + break; + case OCSP_CLT_DONE: + if (ssl_ocsp_check_ocsp_response(*ctx)) { + (*ctx)->state = OCSP_CLT_ERR; + retval = -1; + goto end; + } + break; + case OCSP_CLT_ERR: + retval = -1; + goto end; + case OCSP_CLT_NO_OCSP: + /* No OCSP uri in the client certificate, nothing to do */ + break; + } + + retval = 0; + +end: + if (!retval) { + if (*ctx) + return ((*ctx)->state == OCSP_CLT_WAITING); + } + return retval; +} + +/* + * Set the OCSP status in the optional 'status-var' variable specified by the + * user, and the error reason (if any) in the optional 'reason-var' variable. + * Returns 0 in case of success, 1 otherwise. + */ +static int ssl_ocsp_set_status_var(struct stream *s, struct session *sess, struct ocsp_check_conf *conf) +{ + struct sample smp; + struct buffer *value = get_trash_chunk(); + struct ocsp_clt_check_ctx *ctx = s->ocsp_clt_check_ctx; + + smp_set_owner(&smp, sess->fe, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL); + + if (chunk_printf(value, "%s", ctx ? OCSP_cert_status_str(ctx->ocsp_status) : "unknown") <= 0) + return 1; + + smp.data.type = SMP_T_STR; + smp.data.u.str.area = b_orig(value); + smp.data.u.str.data = b_data(value); + vars_set_by_name(istptr(conf->status_var), istlen(conf->status_var), &smp); + + if (istlen(conf->reason_var)) { + smp_set_owner(&smp, sess->fe, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL); + + smp.data.type = SMP_T_SINT; + smp.data.u.sint = ctx ? ctx->ocsp_error : OCSP_CHECK_ERR_MEMORY; + vars_set_by_name(istptr(conf->reason_var), istlen(conf->reason_var), &smp); + } + + return 0; +} + + +/* + * Main function for 'do-ocsp-request' action which pauses the action processing + * until an OCSP response is recovered from the proper OCSP responder for the + * client's certificate. + * The status of the OCSP response and the whole client OCSP check status can be + * stored in user provided variables (status-var and reason-var). + * This action will yield after the OCSP request is sent until the response is + * retrieved (or a timeout is reached). + */ +static enum act_return ocsp_action_do_ocsp_request(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + struct connection *conn; + SSL *ssl; + int ret = 0; + int retval = ACT_RET_CONT; + struct ocsp_check_conf *conf = rule->arg.act.p[0]; + + struct ocsp_clt_check_ctx **ctx = &s->ocsp_clt_check_ctx; + + if (flags & ACT_OPT_FINAL) + goto end; + + if (!tick_isset(s->req.analyse_exp)) + s->req.analyse_exp = tick_add_ifset(now_ms, s->be->timeout.httpreq); + + if (tick_is_expired(s->req.analyse_exp, now_ms)) { + if (*ctx && !(*ctx)->ocsp_error) + (*ctx)->ocsp_error = OCSP_CHECK_ERR_TIMEOUT; + ret = -1; + } else { + conn = objt_conn(sess->origin); + ssl = ssl_sock_get_ssl_object(conn); + + ret = ssl_ocsp_check_client_cert(s->task, ssl, ctx, s->be->timeout.httpreq); + } + + if (ret == 1) + /* yield and come back to process ocsp response later */ + return ACT_RET_YIELD; + + ssl_ocsp_set_status_var(s, sess, conf); + +end: + clear_ocsp_clt_check_ctx(ctx); + ha_free(ctx); + return retval; +} + +static void clear_ocsp_check_conf(struct ocsp_check_conf *conf) +{ + if (!conf) + return; + + istfree(&conf->status_var); + istfree(&conf->reason_var); +} + +/* + * Clear ocsp_clt_check_ctx and ocsp_check_conf. + */ +static inline void release_do_ocsp_request_action(struct act_rule *rule) +{ + clear_ocsp_check_conf(rule->arg.act.p[0]); + ha_free(&rule->arg.act.p[0]); +} + +/* + * Parse the 'do-ocsp-request' action. + * do-ocsp-request [ status-var ] [ reason-var ] [ no-deny | deny ] + */ +static enum act_parse_ret parse_do_ocsp_request(const char **args, int *orig_arg, struct proxy *px, + struct act_rule *rule, char **err) +{ + struct ocsp_check_conf *conf = NULL; + int cur_arg; + + rule->action = ACT_CUSTOM; + rule->action_ptr = ocsp_action_do_ocsp_request; + rule->release_ptr = release_do_ocsp_request_action; + + cur_arg = *orig_arg; + + conf = calloc(1, sizeof(*conf)); + if (!conf) + goto parse_err; + rule->arg.act.p[0] = conf; + + while (*args[cur_arg]) { + + if (strcmp(args[cur_arg], "status-var") == 0) { + ++cur_arg; + if (!*args[cur_arg]) + goto parse_err; + conf->status_var = ist(strdup(args[cur_arg])); + if (!istlen(conf->status_var)) + goto parse_err; + } else if (strcmp(args[cur_arg], "reason-var") == 0) { + ++cur_arg; + if (!*args[cur_arg]) + goto parse_err; + conf->reason_var = ist(strdup(args[cur_arg])); + if (!istlen(conf->reason_var)) + goto parse_err; + } else + goto parse_err; + + ++cur_arg; + } + + if (!istlen(conf->status_var)) + /* A status-var must be provided */ + goto parse_err; + + *orig_arg = cur_arg; + return ACT_RET_PRS_OK; + +parse_err: + memprintf(err, "Can't parse '%s'%s%s. Expects 'status-var [reason-var ]'.", + args[cur_arg], *err ? ": " : "", *err ? *err : ""); + + return ACT_RET_PRS_ERR; +} + + + #endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */ @@ -2059,6 +2607,20 @@ static void cli_release_show_ocsp_updates(struct appctx *appctx) HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock); } + +static int +sample_conv_ocsp_check_err_str(const struct arg *arg_p, struct sample *smp, void *private) +{ + const char *res = ssl_ocsp_check_err_to_str(smp->data.u.sint); + + smp->data.u.str.area = (char *)res; + smp->data.u.str.data = strlen(res); + smp->data.type = SMP_T_STR; + smp->flags |= SMP_F_CONST; + + return 1; +} + static int ssl_parse_global_ocsp_maxdelay(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, char **err) @@ -2280,8 +2842,34 @@ static struct cfg_kw_list cfg_kws = {ILH, { INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); +static struct action_kw_list http_req_actions = { + .kw = { + { "do-ocsp-request", parse_do_ocsp_request, 0 }, + { /* END */ } + } +}; + +INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions); + +static struct action_kw_list tcp_req_cont_actions = {ILH, { + { "do-ocsp-request", parse_do_ocsp_request, 0 }, + { /* END */ } +}}; + +INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions); + REGISTER_CONFIG_POSTPARSER("ocsp-update", ocsp_update_postparser_init); + +/* Note: must not be declared as its list will be overwritten */ +static struct sample_conv_kw_list sample_conv_kws = {ILH, { + { "ocsp_check_err_str", sample_conv_ocsp_check_err_str, 0, NULL, SMP_T_SINT, SMP_T_STR }, + { NULL, NULL, 0, 0, 0 }, +}}; + +INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws); + + #endif /* HAVE_SSL_OCSP */ /* * Local variables: diff --git a/src/stream.c b/src/stream.c index c76415ce7..fba7a2591 100644 --- a/src/stream.c +++ b/src/stream.c @@ -61,6 +61,7 @@ #include #include #include +#include DECLARE_TYPED_POOL(pool_head_stream, "stream", struct stream); @@ -557,6 +558,8 @@ void *stream_new(struct session *sess, struct stconn *sc, struct buffer *input) s->resolv_ctx.hostname_dn_len = 0; s->resolv_ctx.parent = NULL; + s->ocsp_clt_check_ctx = NULL; + s->connect_timeout = TICK_ETERNITY; s->queue_timeout = TICK_ETERNITY; s->tarpit_timeout = TICK_ETERNITY; @@ -716,6 +719,9 @@ void stream_free(struct stream *s) s->resolv_ctx.requester = NULL; } + clear_ocsp_clt_check_ctx(&s->ocsp_clt_check_ctx); + ha_free(&s->ocsp_clt_check_ctx); + if (fe) { if (s->req_cap) { struct cap_hdr *h;