From 076a2f629119222aeeb50f5a03bf9f9052fabb9a Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 27 Dec 2022 11:50:20 +0100
Subject: [PATCH] share: add sharing of HSTS cache among handles

Closes #10138

CVE: CVE-2023-23914 CVE-2023-23915
Upstream-Status: Backport [https://github.com/curl/curl/commit/076a2f629119222aeeb50f5a03bf9f9052fabb9a]
Comment: Refreshed hunk from hsts.c and urldata.h
Signed-off-by: Pawan Badganchi <Pawan.Badganchi@kpit.com>
Signed-off-by: Mingli Yu <mingli.yu@windriver.com>
---
 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 ++
 9 files changed, 109 insertions(+), 11 deletions(-)

--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -2953,6 +2953,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;

--- a/lib/hsts.c
+++ b/lib/hsts.c
@@ -37,6 +37,7 @@
 #include "parsedate.h"
 #include "rand.h"
 #include "rename.h"
+#include "share.h"
 #include "strtoofft.h"

 /* The last 3 #include files should be in this order */
@@ -561,4 +562,18 @@
   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 */
--- a/lib/hsts.h
+++ b/lib/hsts.h
@@ -59,9 +59,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 */
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2260,9 +2260,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;
@@ -2296,10 +2301,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;
@@ -3049,19 +3063,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) {
--- a/lib/share.c
+++ b/lib/share.c
@@ -29,9 +29,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 *
@@ -89,6 +91,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) {
@@ -141,6 +155,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);
@@ -207,6 +231,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;
--- a/lib/share.h
+++ b/lib/share.h
@@ -59,10 +59,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,
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -1398,6 +1398,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
--- a/lib/url.c
+++ b/lib/url.c
@@ -434,7 +434,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
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1670,6 +1670,8 @@

   void *seek_client;    /* pointer to pass to the seek callback */
 #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;
