Browse Source

curl 2024-07-31 (83bedbd7)

Code extracted from:

    https://github.com/curl/curl.git

at commit 83bedbd730d62b83744cc26fa0433d3f6e2e4cd6 (curl-8_9_1).
Curl Upstream 1 year ago
parent
commit
7a4f719d78
20 changed files with 330 additions and 127 deletions
  1. 75 0
      CMake/FindNettle.cmake
  2. 17 6
      CMakeLists.txt
  3. 1 1
      include/curl/curl.h
  4. 3 3
      include/curl/curlver.h
  5. 22 5
      lib/conncache.c
  6. 25 2
      lib/connect.c
  7. 6 1
      lib/connect.h
  8. 3 1
      lib/curl_setup.h
  9. 2 1
      lib/easy.c
  10. 2 2
      lib/escape.c
  11. 36 32
      lib/multi.c
  12. 9 0
      lib/setup-os400.h
  13. 16 0
      lib/sigpipe.h
  14. 2 2
      lib/transfer.c
  15. 2 2
      lib/vtls/mbedtls.c
  16. 11 10
      lib/vtls/openssl.c
  17. 13 17
      lib/vtls/vtls.c
  18. 38 27
      lib/vtls/wolfssl.c
  19. 36 15
      lib/vtls/x509asn1.c
  20. 11 0
      lib/vtls/x509asn1.h

+ 75 - 0
CMake/FindNettle.cmake

@@ -0,0 +1,75 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+# - Try to find the nettle library
+# Once done this will define
+#
+# NETTLE_FOUND - system has nettle
+# NETTLE_INCLUDE_DIRS - nettle include directories
+# NETTLE_LIBRARIES - nettle library names
+
+if(UNIX)
+  find_package(PkgConfig QUIET)
+  pkg_check_modules(NETTLE "nettle")
+endif()
+
+if(NETTLE_FOUND)
+  set(NETTLE_LIBRARIES ${NETTLE_LINK_LIBRARIES})
+else()
+  find_path(NETTLE_INCLUDE_DIR NAMES "nettle/sha2.h")
+  find_library(NETTLE_LIBRARY NAMES "nettle")
+
+  if(NETTLE_INCLUDE_DIR)
+    if(EXISTS "${NETTLE_INCLUDE_DIR}/nettle/version.h")
+      set(_version_regex_major "^#define[ \t]+NETTLE_VERSION_MAJOR[ \t]+([0-9]+).*")
+      set(_version_regex_minor "^#define[ \t]+NETTLE_VERSION_MINOR[ \t]+([0-9]+).*")
+      file(STRINGS "${NETTLE_INCLUDE_DIR}/nettle/version.h"
+        _version_major REGEX "${_version_regex_major}")
+      file(STRINGS "${NETTLE_INCLUDE_DIR}/nettle/version.h"
+        _version_minor REGEX "${_version_regex_minor}")
+      string(REGEX REPLACE "${_version_regex_major}" "\\1" _version_major "${_version_major}")
+      string(REGEX REPLACE "${_version_regex_minor}" "\\1" _version_minor "${_version_minor}")
+      unset(_version_regex_major)
+      unset(_version_regex_minor)
+      set(NETTLE_VERSION "${_version_major}.${_version_minor}")
+      unset(_version_major)
+      unset(_version_minor)
+    else()
+      set(NETTLE_VERSION "0.0")
+    endif()
+  endif()
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args("nettle"
+    REQUIRED_VARS
+      NETTLE_INCLUDE_DIR
+      NETTLE_LIBRARY
+    VERSION_VAR NETTLE_VERSION)
+
+  if(NETTLE_FOUND)
+    set(NETTLE_INCLUDE_DIRS ${NETTLE_INCLUDE_DIR})
+    set(NETTLE_LIBRARIES    ${NETTLE_LIBRARY})
+  endif()
+
+  mark_as_advanced(NETTLE_INCLUDE_DIR NETTLE_LIBRARY)
+endif()

+ 17 - 6
CMakeLists.txt

@@ -541,11 +541,12 @@ endif()
 
 if(CURL_USE_GNUTLS)
   find_package(GnuTLS REQUIRED)
+  find_package(nettle REQUIRED)
   set(SSL_ENABLED ON)
   set(USE_GNUTLS ON)
-  list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} "nettle")
-  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls")
-  include_directories(${GNUTLS_INCLUDE_DIRS})
+  list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES})
+  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" "nettle")
+  include_directories(${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS})
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls")
     set(valid_default_ssl_backend TRUE)
@@ -993,11 +994,21 @@ endif()
 option(CURL_USE_LIBSSH "Use libssh" OFF)
 mark_as_advanced(CURL_USE_LIBSSH)
 if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH)
-  find_package(libssh CONFIG)
+  find_package(libssh CONFIG QUIET)
   if(libssh_FOUND)
     message(STATUS "Found libssh ${libssh_VERSION}")
-    # Use imported target for include and library paths.
-    list(APPEND CURL_LIBS ssh)
+  else()
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(LIBSSH "libssh")
+    if(LIBSSH_FOUND)
+      include_directories(${LIBSSH_INCLUDE_DIRS})
+    endif()
+  endif()
+  if(libssh_FOUND OR LIBSSH_FOUND)
+    if(NOT DEFINED LIBSSH_LINK_LIBRARIES)
+      set(LIBSSH_LINK_LIBRARIES "ssh")  # for find_package() with broken pkg-config (e.g. linux-old CI workflow)
+    endif()
+    list(APPEND CURL_LIBS ${LIBSSH_LINK_LIBRARIES})
     list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh")
     set(USE_LIBSSH ON)
   endif()

+ 1 - 1
include/curl/curl.h

@@ -2133,7 +2133,7 @@ typedef enum {
 
   /* the EC curves requested by the TLS client (RFC 8422, 5.1);
    * OpenSSL support via 'set_groups'/'set_curves':
-   * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+   * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/
    */
   CURLOPT(CURLOPT_SSL_EC_CURVES, CURLOPTTYPE_STRINGPOINT, 298),
 

+ 3 - 3
include/curl/curlver.h

@@ -32,13 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.9.0-DEV"
+#define LIBCURL_VERSION "8.9.1-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
 #define LIBCURL_VERSION_MINOR 9
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_PATCH 1
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080900
+#define LIBCURL_VERSION_NUM 0x080901
 
 /*
  * This is the date and time when the full source package was created. The

+ 22 - 5
lib/conncache.c

@@ -584,15 +584,15 @@ static void connc_close_all(struct conncache *connc)
     return;
 
   /* Move all connections to the shutdown list */
+  sigpipe_init(&pipe_st);
   conn = connc_find_first_connection(connc);
   while(conn) {
     connc_remove_conn(connc, conn);
-    sigpipe_ignore(data, &pipe_st);
+    sigpipe_apply(data, &pipe_st);
     /* This will remove the connection from the cache */
     connclose(conn, "kill all");
     Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE);
     connc_discard_conn(connc, connc->closure_handle, conn, FALSE);
-    sigpipe_restore(&pipe_st);
 
     conn = connc_find_first_connection(connc);
   }
@@ -613,7 +613,7 @@ static void connc_close_all(struct conncache *connc)
   /* discard all connections in the shutdown list */
   connc_shutdown_discard_all(connc);
 
-  sigpipe_ignore(data, &pipe_st);
+  sigpipe_apply(data, &pipe_st);
   Curl_hostcache_clean(data, data->dns.hostcache);
   Curl_close(&data);
   sigpipe_restore(&pipe_st);
@@ -628,7 +628,6 @@ static void connc_shutdown_discard_oldest(struct conncache *connc)
 {
   struct Curl_llist_element *e;
   struct connectdata *conn;
-  SIGPIPE_VARIABLE(pipe_st);
 
   DEBUGASSERT(!connc->shutdowns.iter_locked);
   if(connc->shutdowns.iter_locked)
@@ -636,9 +635,11 @@ static void connc_shutdown_discard_oldest(struct conncache *connc)
 
   e = connc->shutdowns.conn_list.head;
   if(e) {
+    SIGPIPE_VARIABLE(pipe_st);
     conn = e->ptr;
     Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
-    sigpipe_ignore(connc->closure_handle, &pipe_st);
+    sigpipe_init(&pipe_st);
+    sigpipe_apply(connc->closure_handle, &pipe_st);
     connc_disconnect(NULL, conn, connc, FALSE);
     sigpipe_restore(&pipe_st);
   }
@@ -900,6 +901,9 @@ static void connc_perform(struct conncache *connc)
   struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
   struct Curl_llist_element *enext;
   struct connectdata *conn;
+  struct curltime *nowp = NULL;
+  struct curltime now;
+  timediff_t next_from_now_ms = 0, ms;
   bool done;
 
   if(!e)
@@ -922,9 +926,22 @@ static void connc_perform(struct conncache *connc)
       Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
       connc_disconnect(NULL, conn, connc, FALSE);
     }
+    else {
+      /* Not done, when does this connection time out? */
+      if(!nowp) {
+        now = Curl_now();
+        nowp = &now;
+      }
+      ms = Curl_conn_shutdown_timeleft(conn, nowp);
+      if(ms && ms < next_from_now_ms)
+        next_from_now_ms = ms;
+    }
     e = enext;
   }
   connc->shutdowns.iter_locked = FALSE;
+
+  if(next_from_now_ms)
+    Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
 }
 
 void Curl_conncache_multi_perform(struct Curl_multi *multi)

+ 25 - 2
lib/connect.c

@@ -161,6 +161,7 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
                                   struct curltime *nowp)
 {
   struct curltime now;
+  timediff_t left_ms;
 
   if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms)
     return 0; /* not started or no limits */
@@ -169,8 +170,30 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
     now = Curl_now();
     nowp = &now;
   }
-  return conn->shutdown.timeout_ms -
-         Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
+  left_ms = conn->shutdown.timeout_ms -
+            Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
+  return left_ms? left_ms : -1;
+}
+
+timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
+                                       struct curltime *nowp)
+{
+  timediff_t left_ms = 0, ms;
+  struct curltime now;
+  int i;
+
+  for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
+    if(!conn->shutdown.start[i].tv_sec)
+      continue;
+    if(!nowp) {
+      now = Curl_now();
+      nowp = &now;
+    }
+    ms = Curl_shutdown_timeleft(conn, i, nowp);
+    if(ms && (!left_ms || ms < left_ms))
+      left_ms = ms;
+  }
+  return left_ms;
 }
 
 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)

+ 6 - 1
lib/connect.h

@@ -46,10 +46,15 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
                          struct curltime *nowp);
 
 /* return how much time there is left to shutdown the connection at
- * sockindex. */
+ * sockindex. Returns 0 if there is no limit or shutdown has not started. */
 timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
                                   struct curltime *nowp);
 
+/* return how much time there is left to shutdown the connection.
+ * Returns 0 if there is no limit or shutdown has not started. */
+timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
+                                       struct curltime *nowp);
+
 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);
 
 /* TRUE iff shutdown has been started */

+ 3 - 1
lib/curl_setup.h

@@ -71,7 +71,9 @@
    the necessary dynamic detection features, so the SDK falls back to
    a codepath that sets both the old and new macro to 1. */
 #if defined(TARGET_OS_MAC) && TARGET_OS_MAC && \
-  defined(TARGET_OS_OSX) && !TARGET_OS_OSX
+  defined(TARGET_OS_OSX) && !TARGET_OS_OSX && \
+  (!defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE) && \
+  (!defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR)
 #undef TARGET_OS_OSX
 #define TARGET_OS_OSX TARGET_OS_MAC
 #endif

+ 2 - 1
lib/easy.c

@@ -764,7 +764,8 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
   /* assign this after curl_multi_add_handle() */
   data->multi_easy = multi;
 
-  sigpipe_ignore(data, &pipe_st);
+  sigpipe_init(&pipe_st);
+  sigpipe_apply(data, &pipe_st);
 
   /* run the transfer */
   result = events ? easy_events(multi) : easy_transfer(multi);

+ 2 - 2
lib/escape.c

@@ -60,7 +60,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
   struct dynbuf d;
   (void)data;
 
-  if(inlength < 0)
+  if(!string || (inlength < 0))
     return NULL;
 
   Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3);
@@ -181,7 +181,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string,
 {
   char *str = NULL;
   (void)data;
-  if(length >= 0) {
+  if(string && (length >= 0)) {
     size_t inputlen = (size_t)length;
     size_t outputlen;
     CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen,

+ 36 - 32
lib/multi.c

@@ -2714,6 +2714,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
   CURLMcode returncode = CURLM_OK;
   struct Curl_tree *t;
   struct curltime now = Curl_now();
+  SIGPIPE_VARIABLE(pipe_st);
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -2721,12 +2722,10 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
+  sigpipe_init(&pipe_st);
   data = multi->easyp;
   if(data) {
     CURLMcode result;
-    bool nosig = data->set.no_signal;
-    SIGPIPE_VARIABLE(pipe_st);
-    sigpipe_ignore(data, &pipe_st);
     /* Do the loop and only alter the signal ignore state if the next handle
        has a different NO_SIGNAL state than the previous */
     do {
@@ -2734,22 +2733,23 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
          pointer now */
       struct Curl_easy *datanext = data->next;
 
-      if(data->set.no_signal != nosig) {
-        sigpipe_restore(&pipe_st);
-        sigpipe_ignore(data, &pipe_st);
-        nosig = data->set.no_signal;
+    if(data != multi->conn_cache.closure_handle) {
+        /* connection cache handle is processed below */
+        sigpipe_apply(data, &pipe_st);
+        result = multi_runsingle(multi, &now, data);
+        if(result)
+          returncode = result;
       }
-      result = multi_runsingle(multi, &now, data);
-      if(result)
-        returncode = result;
 
       data = datanext; /* operate on next handle */
     } while(data);
-    sigpipe_restore(&pipe_st);
   }
 
+  sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st);
   Curl_conncache_multi_perform(multi);
 
+  sigpipe_restore(&pipe_st);
+
   /*
    * Simply remove all expired timers from the splay since handles are dealt
    * with unconditionally by this function and curl_multi_timeout() requires
@@ -2778,7 +2778,8 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
     }
   } while(t);
 
-  *running_handles = (int)multi->num_alive;
+  if(running_handles)
+    *running_handles = (int)multi->num_alive;
 
   if(CURLM_OK >= returncode)
     returncode = Curl_update_timer(multi);
@@ -3188,8 +3189,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
   struct Curl_easy *data = NULL;
   struct Curl_tree *t;
   struct curltime now = Curl_now();
-  bool first = FALSE;
-  bool nosig = FALSE;
+  bool run_conn_cache = FALSE;
   SIGPIPE_VARIABLE(pipe_st);
 
   if(checkall) {
@@ -3234,11 +3234,15 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
         DEBUGASSERT(data);
         DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
 
-        if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
-          /* set socket event bitmask if they are not locked */
-          data->state.select_bits |= (unsigned char)ev_bitmask;
+        if(data == multi->conn_cache.closure_handle)
+          run_conn_cache = TRUE;
+        else {
+          if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
+            /* set socket event bitmask if they are not locked */
+            data->state.select_bits |= (unsigned char)ev_bitmask;
 
-        Curl_expire(data, 0, EXPIRE_RUN_NOW);
+          Curl_expire(data, 0, EXPIRE_RUN_NOW);
+        }
       }
 
       /* Now we fall-through and do the timer-based stuff, since we do not want
@@ -3265,19 +3269,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
    * to process in the splay and 'data' will be re-assigned for every expired
    * handle we deal with.
    */
+  sigpipe_init(&pipe_st);
   do {
+    if(data == multi->conn_cache.closure_handle)
+      run_conn_cache = TRUE;
     /* the first loop lap 'data' can be NULL */
-    if(data) {
-      if(!first) {
-        first = TRUE;
-        nosig = data->set.no_signal; /* initial state */
-        sigpipe_ignore(data, &pipe_st);
-      }
-      else if(data->set.no_signal != nosig) {
-        sigpipe_restore(&pipe_st);
-        sigpipe_ignore(data, &pipe_st);
-        nosig = data->set.no_signal; /* remember new state */
-      }
+    else if(data) {
+      sigpipe_apply(data, &pipe_st);
       result = multi_runsingle(multi, &now, data);
 
       if(CURLM_OK >= result) {
@@ -3299,10 +3297,16 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
     }
 
   } while(t);
-  if(first)
-    sigpipe_restore(&pipe_st);
 
-  *running_handles = (int)multi->num_alive;
+  if(run_conn_cache) {
+    sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st);
+    Curl_conncache_multi_perform(multi);
+  }
+
+  sigpipe_restore(&pipe_st);
+
+  if(running_handles)
+    *running_handles = (int)multi->num_alive;
   return result;
 }
 

+ 9 - 0
lib/setup-os400.h

@@ -38,6 +38,15 @@ typedef unsigned long   u_int32_t;
 #define isatty(fd)      0
 
 
+/* Workaround bug in IBM QADRT runtime library:
+ * function puts() does not output the implicit trailing newline.
+ */
+
+#include <stdio.h>      /* Be sure it is loaded. */
+#undef puts
+#define puts(s) (fputs((s), stdout) == EOF? EOF: putchar('\n'))
+
+
 /* System API wrapper prototypes & definitions to support ASCII parameters. */
 
 #include <sys/socket.h>

+ 16 - 0
lib/sigpipe.h

@@ -36,6 +36,11 @@ struct sigpipe_ignore {
 
 #define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
 
+static void sigpipe_init(struct sigpipe_ignore *ig)
+{
+  memset(ig, 0, sizeof(*ig));
+}
+
 /*
  * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl
  * internals, and then sigpipe_restore() will restore the situation when we
@@ -70,9 +75,20 @@ static void sigpipe_restore(struct sigpipe_ignore *ig)
     sigaction(SIGPIPE, &ig->old_pipe_act, NULL);
 }
 
+static void sigpipe_apply(struct Curl_easy *data,
+                          struct sigpipe_ignore *ig)
+{
+  if(data->set.no_signal != ig->no_signal) {
+    sigpipe_restore(ig);
+    sigpipe_ignore(data, ig);
+  }
+}
+
 #else
 /* for systems without sigaction */
 #define sigpipe_ignore(x,y) Curl_nop_stmt
+#define sigpipe_apply(x,y) Curl_nop_stmt
+#define sigpipe_init(x)  Curl_nop_stmt
 #define sigpipe_restore(x)  Curl_nop_stmt
 #define SIGPIPE_VARIABLE(x)
 #endif

+ 2 - 2
lib/transfer.c

@@ -281,13 +281,13 @@ static CURLcode readwrite_data(struct Curl_easy *data,
     buf = xfer_buf;
     bytestoread = xfer_blen;
 
-    if(bytestoread && data->set.max_recv_speed) {
+    if(bytestoread && data->set.max_recv_speed > 0) {
       /* In case of speed limit on receiving: if this loop already got
        * data, break out. If not, limit the amount of bytes to receive.
        * The overall, timed, speed limiting is done in multi.c */
       if(total_received)
         break;
-      if((size_t)data->set.max_recv_speed < bytestoread)
+      if(data->set.max_recv_speed < (curl_off_t)bytestoread)
         bytestoread = (size_t)data->set.max_recv_speed;
     }
 

+ 2 - 2
lib/vtls/mbedtls.c

@@ -249,8 +249,8 @@ static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr =
   1024,      /* RSA min key len */
 };
 
-/* See https://tls.mbed.org/discussions/generic/
-   howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der
+/* See https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/
+   generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der
 */
 #define RSA_PUB_DER_MAX_BYTES   (38 + 2 * MBEDTLS_MPI_MAX_SIZE)
 #define ECP_PUB_DER_MAX_BYTES   (30 + 2 * MBEDTLS_ECP_MAX_BYTES)

+ 11 - 10
lib/vtls/openssl.c

@@ -231,7 +231,7 @@
 /*
  * Whether SSL_CTX_set1_curves_list is available.
  * OpenSSL: supported since 1.0.2, see
- *   https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+ *   https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/
  * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
  * LibreSSL: since 2.5.3 (April 12, 2017)
  */
@@ -3244,7 +3244,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
        problems with server-sent legacy intermediates. Newer versions of
        OpenSSL do alternate chain checking by default but we do not know how to
        determine that in a reliable manner.
-       https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+       https://web.archive.org/web/20190422050538/
+       rt.openssl.org/Ticket/Display.html?id=3621
     */
 #if defined(X509_V_FLAG_TRUSTED_FIRST)
     X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
@@ -3567,12 +3568,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
      CVE-2010-4180 when using previous OpenSSL versions we no longer enable
      this option regardless of OpenSSL version and SSL_OP_ALL definition.
 
-     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
-     (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
-     SSL_OP_ALL that _disables_ that work-around despite the fact that
-     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
-     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
-     must not be set.
+     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability:
+     https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt.
+     In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around
+     despite the fact that SSL_OP_ALL is documented to do "rather harmless"
+     workarounds. In order to keep the secure work-around, the
+     SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set.
   */
 
   ctx_options = SSL_OP_ALL;
@@ -4363,7 +4364,7 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
     if(!buff1)
       break; /* failed */
 
-    /* https://www.openssl.org/docs/crypto/d2i_X509.html */
+    /* https://docs.openssl.org/master/man3/d2i_X509/ */
     len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);
 
     /*
@@ -4975,7 +4976,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
     default:
       /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
          value/errno" */
-      /* https://www.openssl.org/docs/crypto/ERR_get_error.html */
+      /* https://docs.openssl.org/master/man3/ERR_get_error/ */
       if(octx->io_result == CURLE_AGAIN) {
         *curlcode = CURLE_AGAIN;
         nread = -1;

+ 13 - 17
lib/vtls/vtls.c

@@ -413,23 +413,6 @@ int Curl_ssl_init(void)
   return Curl_ssl->init();
 }
 
-#if defined(CURL_WITH_MULTI_SSL)
-static const struct Curl_ssl Curl_ssl_multi;
-#endif
-
-/* Global cleanup */
-void Curl_ssl_cleanup(void)
-{
-  if(init_ssl) {
-    /* only cleanup if we did a previous init */
-    Curl_ssl->cleanup();
-#if defined(CURL_WITH_MULTI_SSL)
-    Curl_ssl = &Curl_ssl_multi;
-#endif
-    init_ssl = FALSE;
-  }
-}
-
 static bool ssl_prefs_check(struct Curl_easy *data)
 {
   /* check for CURLOPT_SSLVERSION invalid parameter value */
@@ -1404,6 +1387,19 @@ static const struct Curl_ssl *available_backends[] = {
   NULL
 };
 
+/* Global cleanup */
+void Curl_ssl_cleanup(void)
+{
+  if(init_ssl) {
+    /* only cleanup if we did a previous init */
+    Curl_ssl->cleanup();
+#if defined(CURL_WITH_MULTI_SSL)
+    Curl_ssl = &Curl_ssl_multi;
+#endif
+    init_ssl = FALSE;
+  }
+}
+
 static size_t multissl_version(char *buffer, size_t size)
 {
   static const struct Curl_ssl *selected;

+ 38 - 27
lib/vtls/wolfssl.c

@@ -212,7 +212,7 @@ static int do_file_type(const char *type)
   return -1;
 }
 
-#ifdef HAVE_LIBOQS
+#ifdef WOLFSSL_HAVE_KYBER
 struct group_name_map {
   const word16 group;
   const char   *name;
@@ -434,10 +434,10 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
     }
     infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
     infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
-    wssl->x509_store_setup = TRUE;
   }
 #endif
   (void)store;
+  wssl->x509_store_setup = TRUE;
   return CURLE_OK;
 }
 
@@ -571,7 +571,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
   bool cache_criteria_met;
 
   /* Consider the X509 store cacheable if it comes exclusively from a CAfile,
-     or no source is provided and we are falling back to OpenSSL's built-in
+     or no source is provided and we are falling back to wolfSSL's built-in
      default. */
   cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) &&
     conn_config->verifypeer &&
@@ -580,19 +580,30 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
     !ssl_config->primary.CRLfile &&
     !ssl_config->native_ca_store;
 
-  cached_store = get_cached_x509_store(cf, data);
-  if(cached_store && cache_criteria_met
-     && wolfSSL_X509_STORE_up_ref(cached_store)) {
+  cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL;
+  if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
     wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store);
   }
-  else {
-    X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
+  else if(cache_criteria_met) {
+    /* wolfSSL's initial store in CTX is not shareable by default.
+     * Make a new one, suitable for adding to the cache. See #14278 */
+    X509_STORE *store = wolfSSL_X509_STORE_new();
+    if(!store) {
+      failf(data, "SSL: could not create a X509 store");
+      return CURLE_OUT_OF_MEMORY;
+    }
+    wolfSSL_CTX_set_cert_store(wssl->ctx, store);
 
     result = populate_x509_store(cf, data, store, wssl);
-    if(result == CURLE_OK && cache_criteria_met) {
+    if(!result) {
       set_cached_x509_store(cf, data, store);
     }
   }
+  else {
+   /* We never share the CTX's store, use it. */
+   X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
+   result = populate_x509_store(cf, data, store, wssl);
+  }
 
   return result;
 }
@@ -611,8 +622,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   WOLFSSL_METHOD* req_method = NULL;
-#ifdef HAVE_LIBOQS
-  word16 oqsAlg = 0;
+#ifdef WOLFSSL_HAVE_KYBER
+  word16 pqkem = 0;
   size_t idx = 0;
 #endif
 #ifdef HAVE_SNI
@@ -739,15 +750,15 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   curves = conn_config->curves;
   if(curves) {
 
-#ifdef HAVE_LIBOQS
+#ifdef WOLFSSL_HAVE_KYBER
     for(idx = 0; gnm[idx].name != NULL; idx++) {
       if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
-        oqsAlg = gnm[idx].group;
+        pqkem = gnm[idx].group;
         break;
       }
     }
 
-    if(oqsAlg == 0)
+    if(pqkem == 0)
 #endif
     {
       if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
@@ -821,8 +832,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   /* give application a chance to interfere with SSL set up. */
   if(data->set.ssl.fsslctx) {
-    CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx,
-                                               data->set.ssl.fsslctxp);
+    CURLcode result;
+    if(!backend->x509_store_setup) {
+      result = Curl_wssl_setup_x509_store(cf, data, backend);
+      if(result)
+        return result;
+    }
+    result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+                                      data->set.ssl.fsslctxp);
     if(result) {
       failf(data, "error signaled by ssl ctx callback");
       return result;
@@ -847,10 +864,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     return CURLE_OUT_OF_MEMORY;
   }
 
-#ifdef HAVE_LIBOQS
-  if(oqsAlg) {
-    if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) {
-      failf(data, "unable to use oqs KEM");
+#ifdef WOLFSSL_HAVE_KYBER
+  if(pqkem) {
+    if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) {
+      failf(data, "unable to use PQ KEM");
     }
   }
 #endif
@@ -1059,15 +1076,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     /* After having send off the ClientHello, we prepare the x509
      * store to verify the coming certificate from the server */
     CURLcode result;
-    struct wolfssl_ctx wssl;
-    wssl.ctx = backend->ctx;
-    wssl.handle = backend->handle;
-    wssl.io_result = CURLE_OK;
-    wssl.x509_store_setup = FALSE;
-    result = Curl_wssl_setup_x509_store(cf, data, &wssl);
+    result = Curl_wssl_setup_x509_store(cf, data, backend);
     if(result)
       return result;
-    backend->x509_store_setup = wssl.x509_store_setup;
   }
 
   connssl->io_need = CURL_SSL_IO_NEED_NONE;

+ 36 - 15
lib/vtls/x509asn1.c

@@ -494,7 +494,7 @@ static CURLcode GTime2str(struct dynbuf *store,
   /* Convert an ASN.1 Generalized time to a printable string.
      Return the dynamically allocated string, or NULL if an error occurs. */
 
-  for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++)
+  for(fracp = beg; fracp < end && ISDIGIT(*fracp); fracp++)
     ;
 
   /* Get seconds digits. */
@@ -513,32 +513,44 @@ static CURLcode GTime2str(struct dynbuf *store,
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
-  /* Scan for timezone, measure fractional seconds. */
+  /* timezone follows optional fractional seconds. */
   tzp = fracp;
-  fracl = 0;
+  fracl = 0; /* no fractional seconds detected so far */
   if(fracp < end && (*fracp == '.' || *fracp == ',')) {
-    fracp++;
-    do
+    /* Have fractional seconds, e.g. "[.,]\d+". How many? */
+    fracp++; /* should be a digit char or BAD ARGUMENT */
+    tzp = fracp;
+    while(tzp < end && ISDIGIT(*tzp))
       tzp++;
-    while(tzp < end && *tzp >= '0' && *tzp <= '9');
-    /* Strip leading zeroes in fractional seconds. */
-    for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--)
-      ;
+    if(tzp == fracp) /* never looped, no digit after [.,] */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    fracl = tzp - fracp; /* number of fractional sec digits */
+    DEBUGASSERT(fracl > 0);
+    /* Strip trailing zeroes in fractional seconds.
+     * May reduce fracl to 0 if only '0's are present. */
+    while(fracl && fracp[fracl - 1] == '0')
+      fracl--;
   }
 
   /* Process timezone. */
-  if(tzp >= end)
-    ;           /* Nothing to do. */
+  if(tzp >= end) {
+    tzp = "";
+    tzl = 0;
+  }
   else if(*tzp == 'Z') {
-    tzp = " GMT";
-    end = tzp + 4;
+    sep = " ";
+    tzp = "GMT";
+    tzl = 3;
+  }
+  else if((*tzp == '+') || (*tzp == '-')) {
+    sep = " UTC";
+    tzl = end - tzp;
   }
   else {
     sep = " ";
-    tzp++;
+    tzl = end - tzp;
   }
 
-  tzl = end - tzp;
   return Curl_dyn_addf(store,
                        "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
                        beg, beg + 4, beg + 6,
@@ -547,6 +559,15 @@ static CURLcode GTime2str(struct dynbuf *store,
                        sep, (int)tzl, tzp);
 }
 
+#ifdef UNITTESTS
+/* used by unit1656.c */
+CURLcode Curl_x509_GTime2str(struct dynbuf *store,
+                             const char *beg, const char *end)
+{
+  return GTime2str(store, beg, end);
+}
+#endif
+
 /*
  * Convert an ASN.1 UTC time to a printable string.
  *

+ 11 - 0
lib/vtls/x509asn1.h

@@ -77,5 +77,16 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum,
                                const char *beg, const char *end);
 CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
                          const char *beg, const char *end);
+
+#ifdef UNITTESTS
+#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
+  defined(USE_MBEDTLS)
+
+/* used by unit1656.c */
+CURLcode Curl_x509_GTime2str(struct dynbuf *store,
+                             const char *beg, const char *end);
+#endif
+#endif
+
 #endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
 #endif /* HEADER_CURL_X509ASN1_H */