From bbbba3d44f645297bc9ee2c599750d465208aced Mon Sep 17 00:00:00 2001 From: Saurabh Pandit Date: Tue, 2 Apr 2024 10:06:27 +0530 Subject: [PATCH] (PA-6291): Applied following curl patches to curl 7.88.1: 1. CVE-2024-2004 2. CVE-2024-2398 --- configs/components/curl.rb | 2 + resources/patches/curl/CVE-2024-2004.patch | 63 ++++++ resources/patches/curl/CVE-2024-2398.patch | 215 +++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 resources/patches/curl/CVE-2024-2004.patch create mode 100644 resources/patches/curl/CVE-2024-2398.patch diff --git a/configs/components/curl.rb b/configs/components/curl.rb index 7c0087552..660454ecb 100644 --- a/configs/components/curl.rb +++ b/configs/components/curl.rb @@ -33,6 +33,8 @@ pkg.apply_patch 'resources/patches/curl/CVE-2023-38545.patch' pkg.apply_patch 'resources/patches/curl/CVE-2023-38546.patch' pkg.apply_patch 'resources/patches/curl/CVE-2023-46218.patch' + pkg.apply_patch 'resources/patches/curl/CVE-2024-2004.patch' + pkg.apply_patch 'resources/patches/curl/CVE-2024-2398.patch' configure_options = [] configure_options << "--with-ssl=#{settings[:prefix]}" diff --git a/resources/patches/curl/CVE-2024-2004.patch b/resources/patches/curl/CVE-2024-2004.patch new file mode 100644 index 000000000..128b1da40 --- /dev/null +++ b/resources/patches/curl/CVE-2024-2004.patch @@ -0,0 +1,63 @@ +setopt: Fix disabling all protocols + +When disabling all protocols without enabling any, the resulting +set of allowed protocols remained the default set. Clearing the +allowed set before inspecting the passed value from --proto make +the set empty even in the errorpath of no protocols enabled. + +Co-authored-by: Dan Fandrich +Reported-by: Dan Fandrich +Reviewed-by: Daniel Stenberg +Closes: #13004 +--- +diff --git a/lib/setopt.c b/lib/setopt.c +index 604693ad9..d6b62c5c9 100644 +--- a/lib/setopt.c ++++ b/lib/setopt.c +@@ -150,6 +150,12 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) + + static CURLcode protocol2num(const char *str, curl_prot_t *val) + { ++ /* ++ * We are asked to cherry-pick protocols, so play it safe and disallow all ++ * protocols to start with, and re-add the wanted ones back in. ++ */ ++ *val = 0; ++ + if(!str) + return CURLE_BAD_FUNCTION_ARGUMENT; + +@@ -158,8 +164,6 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) + return CURLE_OK; + } + +- *val = 0; +- + do { + const char *token = str; + size_t tlen; +@@ -2668,22 +2672,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) + break; + + case CURLOPT_PROTOCOLS_STR: { +- curl_prot_t prot; + argptr = va_arg(param, char *); +- result = protocol2num(argptr, &prot); ++ result = protocol2num(argptr, &data->set.allowed_protocols); + if(result) + return result; +- data->set.allowed_protocols = prot; + break; + } + + case CURLOPT_REDIR_PROTOCOLS_STR: { +- curl_prot_t prot; + argptr = va_arg(param, char *); +- result = protocol2num(argptr, &prot); ++ result = protocol2num(argptr, &data->set.redir_protocols); + if(result) + return result; +- data->set.redir_protocols = prot; + break; + } + \ No newline at end of file diff --git a/resources/patches/curl/CVE-2024-2398.patch b/resources/patches/curl/CVE-2024-2398.patch new file mode 100644 index 000000000..4c4e36cd7 --- /dev/null +++ b/resources/patches/curl/CVE-2024-2398.patch @@ -0,0 +1,215 @@ +http2: push headers better cleanup + +provide common cleanup method for push headers + +Co-authored-by: Stefan Eissing <@icing@chaos.social> +Reviewed-by: Daniel Stenberg + +Closes #13054 +--- +diff --git a/lib/http2.c b/lib/http2.c +index bdb5e7378..f2c02da7c 100644 +--- a/lib/http2.c ++++ b/lib/http2.c +@@ -144,6 +144,161 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) + } + } + ++static CURLcode h2_progress_egress(struct Curl_cfilter *cf, ++ struct Curl_easy *data); ++ ++/** ++ * All about the H2 internals of a stream ++ */ ++struct h2_stream_ctx { ++ int32_t id; /* HTTP/2 protocol identifier for stream */ ++ struct bufq recvbuf; /* response buffer */ ++ struct bufq sendbuf; /* request buffer */ ++ struct h1_req_parser h1; /* parsing the request */ ++ struct dynhds resp_trailers; /* response trailer fields */ ++ size_t resp_hds_len; /* amount of response header bytes in recvbuf */ ++ size_t upload_blocked_len; ++ curl_off_t upload_left; /* number of request bytes left to upload */ ++ curl_off_t nrcvd_data; /* number of DATA bytes received */ ++ ++ char **push_headers; /* allocated array */ ++ size_t push_headers_used; /* number of entries filled in */ ++ size_t push_headers_alloc; /* number of entries allocated */ ++ ++ int status_code; /* HTTP response status code */ ++ uint32_t error; /* stream error code */ ++ uint32_t local_window_size; /* the local recv window size */ ++ bool resp_hds_complete; /* we have a complete, final response */ ++ bool closed; /* TRUE on stream close */ ++ bool reset; /* TRUE on stream reset */ ++ bool close_handled; /* TRUE if stream closure is handled by libcurl */ ++ bool bodystarted; ++ bool send_closed; /* transfer is done sending, we might have still ++ buffered data in stream->sendbuf to upload. */ ++}; ++ ++#define H2_STREAM_CTX(d) ((struct h2_stream_ctx *)(((d) && \ ++ (d)->req.p.http)? \ ++ ((struct HTTP *)(d)->req.p.http)->h2_ctx \ ++ : NULL)) ++#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx ++#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \ ++ H2_STREAM_CTX(d)->id : -2) ++ ++/* ++ * Mark this transfer to get "drained". ++ */ ++static void drain_stream(struct Curl_cfilter *cf, ++ struct Curl_easy *data, ++ struct h2_stream_ctx *stream) ++{ ++ unsigned char bits; ++ ++ (void)cf; ++ bits = CURL_CSELECT_IN; ++ if(!stream->send_closed && ++ (stream->upload_left || stream->upload_blocked_len)) ++ bits |= CURL_CSELECT_OUT; ++ if(data->state.select_bits != bits) { ++ CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", ++ stream->id, bits); ++ data->state.select_bits = bits; ++ Curl_expire(data, 0, EXPIRE_RUN_NOW); ++ } ++} ++ ++static CURLcode http2_data_setup(struct Curl_cfilter *cf, ++ struct Curl_easy *data, ++ struct h2_stream_ctx **pstream) ++{ ++ struct cf_h2_ctx *ctx = cf->ctx; ++ struct h2_stream_ctx *stream; ++ ++ (void)cf; ++ DEBUGASSERT(data); ++ if(!data->req.p.http) { ++ failf(data, "initialization failure, transfer not http initialized"); ++ return CURLE_FAILED_INIT; ++ } ++ stream = H2_STREAM_CTX(data); ++ if(stream) { ++ *pstream = stream; ++ return CURLE_OK; ++ } ++ ++ stream = calloc(1, sizeof(*stream)); ++ if(!stream) ++ return CURLE_OUT_OF_MEMORY; ++ ++ stream->id = -1; ++ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, ++ H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); ++ Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); ++ Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); ++ stream->resp_hds_len = 0; ++ stream->bodystarted = FALSE; ++ stream->status_code = -1; ++ stream->closed = FALSE; ++ stream->close_handled = FALSE; ++ stream->error = NGHTTP2_NO_ERROR; ++ stream->local_window_size = H2_STREAM_WINDOW_SIZE; ++ stream->upload_left = 0; ++ stream->nrcvd_data = 0; ++ ++ H2_STREAM_LCTX(data) = stream; ++ *pstream = stream; ++ return CURLE_OK; ++} ++ ++static void free_push_headers(struct h2_stream_ctx *stream) ++{ ++ size_t i; ++ for(i = 0; ipush_headers_used; i++) ++ free(stream->push_headers[i]); ++ Curl_safefree(stream->push_headers); ++ stream->push_headers_used = 0; ++} ++ ++static void http2_data_done(struct Curl_cfilter *cf, ++ struct Curl_easy *data, bool premature) ++{ ++ struct cf_h2_ctx *ctx = cf->ctx; ++ struct h2_stream_ctx *stream = H2_STREAM_CTX(data); ++ ++ DEBUGASSERT(ctx); ++ (void)premature; ++ if(!stream) ++ return; ++ ++ if(ctx->h2) { ++ bool flush_egress = FALSE; ++ /* returns error if stream not known, which is fine here */ ++ (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL); ++ ++ if(!stream->closed && stream->id > 0) { ++ /* RST_STREAM */ ++ CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream", ++ stream->id); ++ stream->closed = TRUE; ++ stream->reset = TRUE; ++ stream->send_closed = TRUE; ++ nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, ++ stream->id, NGHTTP2_STREAM_CLOSED); ++ flush_egress = TRUE; ++ } ++ ++ if(flush_egress) ++ nghttp2_session_send(ctx->h2); ++ } ++ ++ Curl_bufq_free(&stream->sendbuf); ++ Curl_h1_req_parse_free(&stream->h1); ++ Curl_dynhds_free(&stream->resp_trailers); ++ free_push_headers(stream); ++ free(stream); ++ H2_STREAM_LCTX(data) = NULL; ++} ++ + static int h2_client_new(struct Curl_cfilter *cf, + nghttp2_session_callbacks *cbs) + { +@@ -702,6 +857,7 @@ static int push_promise(struct Curl_cfilter *cf, + struct HTTP *newstream; + struct curl_pushheaders heads; + CURLMcode rc; ++ CURLcode result; + size_t i; + /* clone the parent */ + struct Curl_easy *newhandle = h2_duphandle(cf, data); +@@ -738,11 +894,7 @@ static int push_promise(struct Curl_cfilter *cf, + Curl_set_in_callback(data, false); + + /* free the headers again */ +- for(i = 0; ipush_headers_used; i++) +- free(stream->push_headers[i]); +- free(stream->push_headers); +- stream->push_headers = NULL; +- stream->push_headers_used = 0; ++ free_push_headers(stream); + + if(rv) { + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); +@@ -1198,14 +1350,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, + if(stream->push_headers_alloc > 1000) { + /* this is beyond crazy many headers, bail out */ + failf(data_s, "Too many PUSH_PROMISE headers"); +- Curl_safefree(stream->push_headers); ++ free_push_headers(stream); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + stream->push_headers_alloc *= 2; +- headp = Curl_saferealloc(stream->push_headers, +- stream->push_headers_alloc * sizeof(char *)); ++ headp = realloc(stream->push_headers, ++ stream->push_headers_alloc * sizeof(char *)); + if(!headp) { +- stream->push_headers = NULL; ++ free_push_headers(stream); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + stream->push_headers = headp;