File curl-CVE-2023-23914-23915.patch of Package curl.27750
From 873b0a13946c6d373d2f5c445134abe70a91e8ed Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 27 Dec 2022 11:50:20 +0100
Subject: [PATCH 1/5] share: add sharing of HSTS cache among handles
Closes #10138
---
docs/libcurl/opts/CURLSHOPT_SHARE.3 | 4 +++
docs/libcurl/symbols-in-versions | 1 +
include/curl/curl.h | 1 +
lib/hsts.c | 15 +++++++++
lib/hsts.h | 2 ++
lib/setopt.c | 48 ++++++++++++++++++++++++-----
lib/share.c | 32 +++++++++++++++++--
lib/share.h | 6 +++-
lib/transfer.c | 3 ++
lib/url.c | 6 +++-
lib/urldata.h | 2 ++
11 files changed, 109 insertions(+), 11 deletions(-)
Index: curl-7.79.1/docs/libcurl/symbols-in-versions
===================================================================
--- curl-7.79.1.orig/docs/libcurl/symbols-in-versions
+++ curl-7.79.1/docs/libcurl/symbols-in-versions
@@ -943,6 +943,7 @@ CURL_LOCK_ACCESS_SINGLE 7.10.3
CURL_LOCK_DATA_CONNECT 7.10.3
CURL_LOCK_DATA_COOKIE 7.10.3
CURL_LOCK_DATA_DNS 7.10.3
+CURL_LOCK_DATA_HSTS 7.88.0
CURL_LOCK_DATA_NONE 7.10.3
CURL_LOCK_DATA_PSL 7.61.0
CURL_LOCK_DATA_SHARE 7.10.4
Index: curl-7.79.1/include/curl/curl.h
===================================================================
--- curl-7.79.1.orig/include/curl/curl.h
+++ curl-7.79.1/include/curl/curl.h
@@ -2821,6 +2821,7 @@ typedef enum {
CURL_LOCK_DATA_SSL_SESSION,
CURL_LOCK_DATA_CONNECT,
CURL_LOCK_DATA_PSL,
+ CURL_LOCK_DATA_HSTS,
CURL_LOCK_DATA_LAST
} curl_lock_data;
Index: curl-7.79.1/lib/hsts.c
===================================================================
--- curl-7.79.1.orig/lib/hsts.c
+++ curl-7.79.1/lib/hsts.c
@@ -38,6 +38,7 @@
#include "rand.h"
#include "rename.h"
#include "strtoofft.h"
+#include "share.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -414,14 +415,23 @@ static CURLcode hsts_add(struct hsts *h,
if(2 == rc) {
time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
TIME_T_MAX;
- CURLcode result;
+ CURLcode result = CURLE_OK;
char *p = host;
bool subdomain = FALSE;
+ struct stsentry *e;
if(p[0] == '.') {
p++;
subdomain = TRUE;
}
- result = hsts_create(h, p, subdomain, expires);
+ /* only add it if not already present */
+ e = Curl_hsts(h, p, subdomain);
+ if(!e)
+ result = hsts_create(h, p, subdomain, expires);
+ else {
+ /* the same host name, use the largest expire time */
+ if(expires > e->expires)
+ e->expires = expires;
+ }
if(result)
return result;
}
@@ -541,4 +551,18 @@ CURLcode Curl_hsts_loadcb(struct Curl_ea
return CURLE_OK;
}
+void Curl_hsts_loadfiles(struct Curl_easy *data)
+{
+ struct curl_slist *l = data->set.hstslist;
+ if(l) {
+ Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
+
+ while(l) {
+ (void)Curl_hsts_loadfile(data, data->hsts, l->data);
+ l = l->next;
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_HSTS);
+ }
+}
+
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
Index: curl-7.79.1/lib/hsts.h
===================================================================
--- curl-7.79.1.orig/lib/hsts.h
+++ curl-7.79.1/lib/hsts.h
@@ -57,9 +57,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_
struct hsts *h, const char *file);
CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
struct hsts *h);
+void Curl_hsts_loadfiles(struct Curl_easy *data);
#else
#define Curl_hsts_cleanup(x)
#define Curl_hsts_loadcb(x,y) CURLE_OK
#define Curl_hsts_save(x,y,z)
+#define Curl_hsts_loadfiles(x)
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
#endif /* HEADER_CURL_HSTS_H */
Index: curl-7.79.1/lib/setopt.c
===================================================================
--- curl-7.79.1.orig/lib/setopt.c
+++ curl-7.79.1/lib/setopt.c
@@ -2230,9 +2230,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *
data->cookies = NULL;
#endif
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts == data->hsts)
+ data->hsts = NULL;
+#endif
+#ifdef USE_SSL
if(data->share->sslsession == data->state.session)
data->state.session = NULL;
-
+#endif
#ifdef USE_LIBPSL
if(data->psl == &data->share->psl)
data->psl = data->multi? &data->multi->psl: NULL;
@@ -2266,10 +2271,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *
data->cookies = data->share->cookies;
}
#endif /* CURL_DISABLE_HTTP */
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts) {
+ /* first free the private one if any */
+ Curl_hsts_cleanup(&data->hsts);
+ data->hsts = data->share->hsts;
+ }
+#endif /* CURL_DISABLE_HTTP */
+#ifdef USE_SSL
if(data->share->sslsession) {
data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
data->state.session = data->share->sslsession;
}
+#endif
#ifdef USE_LIBPSL
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
data->psl = &data->share->psl;
@@ -2958,19 +2972,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *
case CURLOPT_HSTSWRITEDATA:
data->set.hsts_write_userp = va_arg(param, void *);
break;
- case CURLOPT_HSTS:
+ case CURLOPT_HSTS: {
+ struct curl_slist *h;
if(!data->hsts) {
data->hsts = Curl_hsts_init();
if(!data->hsts)
return CURLE_OUT_OF_MEMORY;
}
argptr = va_arg(param, char *);
- result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
- if(result)
- return result;
- if(argptr)
- (void)Curl_hsts_loadfile(data, data->hsts, argptr);
+ if(argptr) {
+ result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
+ if(result)
+ return result;
+ /* this needs to build a list of file names to read from, so that it can
+ read them later, as we might get a shared HSTS handle to load them
+ into */
+ h = curl_slist_append(data->set.hstslist, argptr);
+ if(!h) {
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->set.hstslist = h; /* store the list for later use */
+ }
+ else {
+ /* clear the list of HSTS files */
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ if(!data->share || !data->share->hsts)
+ /* throw away the HSTS cache unless shared */
+ Curl_hsts_cleanup(&data->hsts);
+ }
break;
+ }
case CURLOPT_HSTS_CTRL:
arg = va_arg(param, long);
if(arg & CURLHSTS_ENABLE) {
Index: curl-7.79.1/lib/share.c
===================================================================
--- curl-7.79.1.orig/lib/share.c
+++ curl-7.79.1/lib/share.c
@@ -27,9 +27,11 @@
#include "share.h"
#include "psl.h"
#include "vtls/vtls.h"
-#include "curl_memory.h"
+#include "hsts.h"
-/* The last #include file should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
#include "memdebug.h"
struct Curl_share *
@@ -91,6 +93,18 @@ curl_share_setopt(struct Curl_share *sha
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(!share->hsts) {
+ share->hsts = Curl_hsts_init();
+ if(!share->hsts)
+ res = CURLSHE_NOMEM;
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
if(!share->sslsession) {
@@ -143,6 +157,16 @@ curl_share_setopt(struct Curl_share *sha
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(share->hsts) {
+ Curl_hsts_cleanup(&share->hsts);
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
Curl_safefree(share->sslsession);
@@ -209,6 +233,10 @@ curl_share_cleanup(struct Curl_share *sh
Curl_cookie_cleanup(share->cookies);
#endif
+#ifndef CURL_DISABLE_HSTS
+ Curl_hsts_cleanup(&share->hsts);
+#endif
+
#ifdef USE_SSL
if(share->sslsession) {
size_t i;
Index: curl-7.79.1/lib/share.h
===================================================================
--- curl-7.79.1.orig/lib/share.h
+++ curl-7.79.1/lib/share.h
@@ -57,10 +57,14 @@ struct Curl_share {
#ifdef USE_LIBPSL
struct PslCache psl;
#endif
-
+#ifndef CURL_DISABLE_HSTS
+ struct hsts *hsts;
+#endif
+#ifdef USE_SSL
struct Curl_ssl_session *sslsession;
size_t max_ssl_sessions;
long sessionage;
+#endif
};
CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
Index: curl-7.79.1/lib/transfer.c
===================================================================
--- curl-7.79.1.orig/lib/transfer.c
+++ curl-7.79.1/lib/transfer.c
@@ -1468,6 +1468,9 @@ CURLcode Curl_pretransfer(struct Curl_ea
if(data->state.resolve)
result = Curl_loadhostpairs(data);
+ /* If there is a list of hsts files to read */
+ Curl_hsts_loadfiles(data);
+
if(!result) {
/* Allow data->set.use_port to set which port to use. This needs to be
* disabled for example when we follow Location: headers to URLs using
Index: curl-7.79.1/lib/url.c
===================================================================
--- curl-7.79.1.orig/lib/url.c
+++ curl-7.79.1/lib/url.c
@@ -427,7 +427,11 @@ CURLcode Curl_close(struct Curl_easy **d
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);
Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
- Curl_hsts_cleanup(&data->hsts);
+#ifndef CURL_DISABLE_HSTS
+ if(!data->share || !data->share->hsts)
+ Curl_hsts_cleanup(&data->hsts);
+ curl_slist_free_all(data->set.hstslist); /* clean up list */
+#endif
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
Index: curl-7.79.1/lib/urldata.h
===================================================================
--- curl-7.79.1.orig/lib/urldata.h
+++ curl-7.79.1/lib/urldata.h
@@ -1670,6 +1670,8 @@ struct UserDefined {
/* function to convert from UTF-8 encoding: */
curl_conv_callback convfromutf8;
#ifndef CURL_DISABLE_HSTS
+ struct curl_slist *hstslist; /* list of HSTS files set by
+ curl_easy_setopt(HSTS) calls */
curl_hstsread_callback hsts_read;
void *hsts_read_userp;
curl_hstswrite_callback hsts_write;
Index: curl-7.79.1/src/tool_operate.c
===================================================================
--- curl-7.79.1.orig/src/tool_operate.c
+++ curl-7.79.1/src/tool_operate.c
@@ -2595,6 +2595,7 @@ CURLcode operate(struct GlobalConfig *gl
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
/* Get the required arguments for each operation */
do {
Index: curl-7.79.1/tests/FILEFORMAT.md
===================================================================
--- curl-7.79.1.orig/tests/FILEFORMAT.md
+++ curl-7.79.1/tests/FILEFORMAT.md
@@ -527,14 +527,14 @@ changing protocol data such as port numb
One perl op per line that operates on the protocol dump. This is pretty
advanced. Example: `s/^EPRT .*/EPRT stripped/`.
-### `<protocol [nonewline="yes"]>`
+### `<protocol [nonewline="yes"][crlf="yes"]>`
the protocol dump curl should transmit, if 'nonewline' is set, we will cut off
the trailing newline of this given data before comparing with the one actually
sent by the client The `<strip>` and `<strippart>` rules are applied before
comparisons are made.
-### `<proxy [nonewline="yes"]>`
+### `<proxy [nonewline="yes"][crlf="yes"]>`
The protocol dump curl should transmit to a HTTP proxy (when the http-proxy
server is used), if 'nonewline' is set, we will cut off the trailing newline
Index: curl-7.79.1/tests/runtests.pl
===================================================================
--- curl-7.79.1.orig/tests/runtests.pl
+++ curl-7.79.1/tests/runtests.pl
@@ -4486,6 +4486,11 @@ sub singletest {
}
}
+ if($hash{'crlf'} ||
+ ($has_hyper && ($keywords{"HTTP"} || $keywords{"HTTPS"}))) {
+ map subNewlines(0, \$_), @protstrip;
+ }
+
$res = compare($testnum, $testname, "proxy", \@out, \@protstrip);
if($res) {
return $errorreturncode;
Index: curl-7.79.1/tests/data/Makefile.inc
===================================================================
--- curl-7.79.1.orig/tests/data/Makefile.inc
+++ curl-7.79.1/tests/data/Makefile.inc
@@ -68,6 +68,7 @@ test400 test401 test402 test403 test404
test409 test410 \
\
test430 test431 test432 test433 test434 test435 \
+test446 \
\
test490 test491 test492 test493 test494 \
\
Index: curl-7.79.1/tests/data/test446
===================================================================
--- /dev/null
+++ curl-7.79.1/tests/data/test446
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP proxy
+HSTS
+trailing-dot
+</keywords>
+</info>
+
+<reply>
+
+# we use this as response to a CONNECT
+<connect nocheck="yes">
+HTTP/1.1 200 OK
+
+</connect>
+<data crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Strict-Transport-Security: max-age=604800
+
+-foo-
+</data>
+<data2 crlf="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+Strict-Transport-Security: max-age=6048000
+
+-baa-
+</data2>
+</reply>
+
+<client>
+<server>
+https
+http-proxy
+</server>
+<features>
+HSTS
+proxy
+https
+debug
+</features>
+<setenv>
+CURL_HSTS_HTTP=yes
+CURL_TIME=2000000000
+</setenv>
+
+<name>
+HSTS with two URLs
+</name>
+<command>
+-x http://%HOSTIP:%PROXYPORT --hsts log/hsts%TESTNUMBER http://this.hsts.example./%TESTNUMBER http://another.example.com/%TESTNUMBER0002
+</command>
+</client>
+
+<verify>
+# we let it CONNECT to the server to confirm HSTS but deny from there
+<proxy crlf="yes">
+GET http://this.hsts.example./%TESTNUMBER HTTP/1.1
+Host: this.hsts.example.
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://another.example.com/%TESTNUMBER0002 HTTP/1.1
+Host: another.example.com
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</proxy>
+
+<file name="log/hsts%TESTNUMBER" mode="text">
+# Your HSTS cache. https://curl.se/docs/hsts.html
+# This file was generated by libcurl! Edit at your own risk.
+this.hsts.example "20330525 03:33:20"
+another.example.com "20330727 03:33:20"
+</file>
+
+</verify>
+</testcase>
Index: curl-7.79.1/docs/libcurl/curl_share_setopt.3
===================================================================
--- curl-7.79.1.orig/docs/libcurl/curl_share_setopt.3
+++ curl-7.79.1/docs/libcurl/curl_share_setopt.3
@@ -99,6 +99,9 @@ refreshed, this avoids updates in too ma
Note that when you use the multi interface, all easy handles added to the same
multi handle will share PSL cache by default without using this option.
+.IP CURL_LOCK_DATA_HSTS
+The in-memory HSTS cache.
+
.RE
.IP CURLSHOPT_UNSHARE
This option does the opposite of \fICURLSHOPT_SHARE\fP. It specifies that