Skip to content

Commit

Permalink
Merge pull request puppetlabs#810 from span786/PUP-12030-puppet-agent…
Browse files Browse the repository at this point in the history
…s-packaged-curl-is-vulnerable-to-cve-2024-2004-and-cve-2024-2398

(PA-6291): Applied curl patches to curl 7.88.1
  • Loading branch information
joshcooper authored Apr 2, 2024
2 parents 68650ed + bbbba3d commit 4ac018d
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 0 deletions.
2 changes: 2 additions & 0 deletions configs/components/curl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]}"
Expand Down
63 changes: 63 additions & 0 deletions resources/patches/curl/CVE-2024-2004.patch
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
Reported-by: Dan Fandrich <[email protected]>
Reviewed-by: Daniel Stenberg <[email protected]>
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;
}

215 changes: 215 additions & 0 deletions resources/patches/curl/CVE-2024-2398.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
http2: push headers better cleanup

provide common cleanup method for push headers

Co-authored-by: Stefan Eissing <@[email protected]>
Reviewed-by: Daniel Stenberg <[email protected]>

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; i<stream->push_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; i<stream->push_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;

0 comments on commit 4ac018d

Please sign in to comment.