diff options
author | Brad King <brad.king@kitware.com> | 2022-01-10 22:12:32 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-01-10 22:12:38 (GMT) |
commit | 5dd655d5e7d86b8651981b9033303df7ef8b4f35 (patch) | |
tree | fd4c16ec1cb8f537033ebd52c438f27e656c9ca5 | |
parent | 39eabc17c466d79c5e16cef236194d55a152a832 (diff) | |
parent | 9341e63233b539be10572ffdb621e63f73582573 (diff) | |
download | CMake-5dd655d5e7d86b8651981b9033303df7ef8b4f35.zip CMake-5dd655d5e7d86b8651981b9033303df7ef8b4f35.tar.gz CMake-5dd655d5e7d86b8651981b9033303df7ef8b4f35.tar.bz2 |
Merge topic 'update-curl'
9341e63233 curl: Set build options the way we need for CMake
697e8871a1 Merge branch 'upstream-curl' into update-curl
a1f6ec647c curl 2022-01-05 (801bd513)
bdb3a8203e curl: Update script to get curl 7.81.0
e9260cf4b0 Merge branch 'upstream-curl' into update-curl
a4ad12d843 curl 2021-09-22 (c7aef0a9)
520809b5ae curl: Update script for new curl mailing list address
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !6845
102 files changed, 3572 insertions, 2019 deletions
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index dd8e7a8..50d8029 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -5,10 +5,10 @@ set -x shopt -s dotglob readonly name="curl" -readonly ownership="Curl Upstream <curl-library@cool.haxx.se>" +readonly ownership="Curl Upstream <curl-library@lists.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-7_79_1" +readonly tag="curl-7_81_0" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index e418146..8666da0 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -229,10 +229,6 @@ int main () { ; return 0; } # include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif #endif @@ -258,10 +254,6 @@ main () # include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif #endif @@ -285,10 +277,6 @@ main () # include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif #endif @@ -313,10 +301,6 @@ main () # include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif #endif @@ -403,10 +387,6 @@ main () # include <windows.h> # ifdef HAVE_WINSOCK2_H # include <winsock2.h> -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif #endif /* includes start */ diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index 1746093..7bdb197 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. +# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -28,7 +28,7 @@ find_library(MBEDCRYPTO_LIBRARY mbedcrypto) set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MBEDTLS DEFAULT_MSG +find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index 7fda7ac..d06b3ed 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -33,7 +33,6 @@ set(signature_call_conv) if(HAVE_WINDOWS_H) add_header_include(HAVE_WINSOCK2_H "winsock2.h") add_header_include(HAVE_WINDOWS_H "windows.h") - add_header_include(HAVE_WINSOCK_H "winsock.h") set(_source_epilogue "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif") set(signature_call_conv "PASCAL") diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index cd5d354..7cb497b 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -3,10 +3,17 @@ set(BUILD_CURL_EXE OFF CACHE INTERNAL "No curl exe") set(BUILD_DASHBOARD_REPORTS OFF CACHE INTERNAL "No curl dashboard reports") set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs") set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build shared libraries") -set(CMAKE_USE_GSSAPI OFF CACHE INTERNAL "Disable curl gssapi") -set(CMAKE_USE_LIBSSH2 OFF CACHE INTERNAL "Disable curl libssh2") -set(CMAKE_USE_LIBSSH OFF) -set(CMAKE_USE_OPENLDAP OFF CACHE INTERNAL "No curl OpenLDAP") +set(CURL_USE_BEARSSL OFF) +set(CURL_USE_GSSAPI OFF) +set(CURL_USE_LIBSSH2 OFF) +set(CURL_USE_LIBSSH OFF) +set(CURL_USE_MBEDTLS OFF) +set(CURL_USE_NSS OFF) +set(CURL_USE_OPENLDAP OFF) +set(CURL_USE_OPENSSL "${CMAKE_USE_OPENSSL}") +set(CURL_USE_SCHANNEL OFF) +set(CURL_USE_SECTRANSP OFF) +set(CURL_USE_WOLFSSL OFF) set(CURL_BROTLI OFF) set(CURL_DISABLE_ALTSVC ON) set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support") @@ -66,11 +73,10 @@ set(USE_NGTCP2 OFF) set(USE_QUICHE OFF) set(USE_WIN32_IDN OFF) set(USE_WIN32_LDAP OFF CACHE INTERNAL "No curl Windows LDAP") -if(CMAKE_USE_OPENSSL) +if(CURL_USE_OPENSSL) elseif(WIN32) - unset(CMAKE_USE_WINSSL CACHE) - set(CMAKE_USE_SCHANNEL ON) - set(CURL_WINDOWS_SSPI ON CACHE INTERNAL "Use windows libraries to allow NTLM authentication without openssl") + set(CURL_USE_SCHANNEL ON) + set(CURL_WINDOWS_SSPI ON) elseif(APPLE) # Use OS X SSL/TLS native implementation if available on target version. if(CMAKE_OSX_DEPLOYMENT_TARGET) @@ -84,16 +90,11 @@ elseif(APPLE) endif() if(NOT OSX_VERSION VERSION_LESS 10.6 AND CMAKE_C_COMPILER_ID MATCHES "GNU|LCC|Clang|AppleClang") - set(CMAKE_USE_SECTRANSP ON CACHE INTERNAL "enable Apple OS native SSL/TLS") + set(CURL_USE_SECTRANSP ON) else() - set(CMAKE_USE_SECTRANSP OFF CACHE INTERNAL "enable Apple OS native SSL/TLS") + set(CURL_USE_SECTRANSP OFF) endif() - unset(CMAKE_USE_DARWINSSL CACHE) endif() -set(CMAKE_USE_MBEDTLS OFF CACHE INTERNAL "Enable mbedTLS for SSL/TLS") -set(CMAKE_USE_BEARSSL OFF CACHE INTERNAL "Enable BearSSL for SSL/TLS") -set(CMAKE_USE_NSS OFF CACHE INTERNAL "Enable NSS for SSL/TLS") -set(CMAKE_USE_WOLFSSL OFF) # Windows Vista and above have inet_pton, but this will link on # older versions and then the executable will fail to launch at @@ -387,6 +388,17 @@ if(ENABLE_IPV6 AND NOT WIN32) set(ENABLE_IPV6 OFF CACHE BOOL "Define if you want to enable IPv6 support" FORCE) endif() + + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT ENABLE_ARES) + set(use_core_foundation ON) + + find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") + if(NOT SYSTEMCONFIGURATION_FRAMEWORK) + message(FATAL_ERROR "SystemConfiguration framework not found") + endif() + + list(APPEND CURL_LIBS "-framework SystemConfiguration") + endif() endif() if(0) # This code not needed for building within CMake. @@ -479,84 +491,96 @@ if(WIN32) check_library_exists_concat("winmm" getch HAVE_LIBWINMM) endif() -# check SSL libraries -# TODO support GnuTLS +if(0) # This code not needed for building within CMake. +# This check below for use of deprecated symbols is only temporary and is to +# be removed again after a year's service. Remove after November 25, 2022. +set(CURL_RECONFIG_REQUIRED 0) +foreach(_LIB GSSAPI OPENLDAP LIBSSH LIBSSH2 BEARSSL MBEDTLS NSS OPENSSL + SCHANNEL SECTRANSP WOLFSSL) + if(CMAKE_USE_${_LIB}) + set(CURL_RECONFIG_REQUIRED 1) + message(SEND_ERROR "The option CMAKE_USE_${_LIB} was renamed to CURL_USE_${_LIB}.") + endif() +endforeach() if(CMAKE_USE_WINSSL) - message(FATAL_ERROR "The cmake option CMAKE_USE_WINSSL was renamed to CMAKE_USE_SCHANNEL.") + set(CURL_RECONFIG_REQUIRED 1) + message(SEND_ERROR "The option CMAKE_USE_WINSSL was renamed to CURL_USE_SCHANNEL.") endif() +if(CURL_RECONFIG_REQUIRED) + message(FATAL_ERROR "Reconfig required") +endif() + +# check SSL libraries +# TODO support GnuTLS +option(CURL_ENABLE_SSL "Enable SSL support" ON) if(APPLE) - option(CMAKE_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF) + cmake_dependent_option(CURL_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF) endif() if(WIN32) - option(CMAKE_USE_SCHANNEL "enable Windows native SSL/TLS" OFF) + cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without openssl" ON - CMAKE_USE_SCHANNEL OFF) + CURL_USE_SCHANNEL OFF) endif() -option(CMAKE_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF) -option(CMAKE_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF) -option(CMAKE_USE_NSS "Enable NSS for SSL/TLS" OFF) -option(CMAKE_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF) +cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_NSS "Enable NSS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) set(openssl_default ON) -if(WIN32 OR CMAKE_USE_SECTRANSP OR CMAKE_USE_SCHANNEL OR CMAKE_USE_MBEDTLS OR CMAKE_USE_NSS OR CMAKE_USE_WOLFSSL) +if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_NSS OR CURL_USE_WOLFSSL) set(openssl_default OFF) endif() +cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF) option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) +endif() count_true(enabled_ssl_options_count - CMAKE_USE_SCHANNEL - CMAKE_USE_SECTRANSP - CMAKE_USE_OPENSSL - CMAKE_USE_MBEDTLS - CMAKE_USE_BEARSSL - CMAKE_USE_NSS - CMAKE_USE_WOLFSSL + CURL_USE_SCHANNEL + CURL_USE_SECTRANSP + CURL_USE_OPENSSL + CURL_USE_MBEDTLS + CURL_USE_BEARSSL + CURL_USE_NSS + CURL_USE_WOLFSSL ) if(enabled_ssl_options_count GREATER "1") set(CURL_WITH_MULTI_SSL ON) endif() -if(CMAKE_USE_SCHANNEL) +if(CURL_USE_SCHANNEL) set(SSL_ENABLED ON) set(USE_SCHANNEL ON) # Windows native SSL/TLS support - set(USE_WINDOWS_SSPI ON) # CMAKE_USE_SCHANNEL implies CURL_WINDOWS_SSPI + set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI endif() if(CURL_WINDOWS_SSPI) set(USE_WINDOWS_SSPI ON) set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DSECURITY_WIN32") endif() -if(CMAKE_USE_DARWINSSL) - message(FATAL_ERROR "The cmake option CMAKE_USE_DARWINSSL was renamed to CMAKE_USE_SECTRANSP.") +if(CURL_USE_SECTRANSP) + set(use_core_foundation ON) + + find_library(SECURITY_FRAMEWORK "Security") + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Security framework not found") + endif() + + set(SSL_ENABLED ON) + set(USE_SECTRANSP ON) + list(APPEND CURL_LIBS "-framework Security") endif() -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +if(use_core_foundation) find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation") if(NOT COREFOUNDATION_FRAMEWORK) message(FATAL_ERROR "CoreFoundation framework not found") endif() - find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") - if(NOT SYSTEMCONFIGURATION_FRAMEWORK) - message(FATAL_ERROR "SystemConfiguration framework not found") - endif() - - list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework SystemConfiguration") - - if(CMAKE_USE_SECTRANSP) - find_library(SECURITY_FRAMEWORK "Security") - if(NOT SECURITY_FRAMEWORK) - message(FATAL_ERROR "Security framework not found") - endif() - - set(SSL_ENABLED ON) - set(USE_SECTRANSP ON) - list(APPEND CURL_LIBS "-framework Security") - endif() + list(APPEND CURL_LIBS "-framework CoreFoundation") endif() -if(CMAKE_USE_OPENSSL) +if(CURL_USE_OPENSSL) find_package(OpenSSL) if(NOT OpenSSL_FOUND) message(FATAL_ERROR @@ -588,9 +612,11 @@ if(CMAKE_USE_OPENSSL) if(CURL_CA_PATH) add_definitions(-DCURL_CA_PATH="${CURL_CA_PATH}") endif() + + add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED) endif() -if(CMAKE_USE_MBEDTLS) +if(CURL_USE_MBEDTLS) find_package(MbedTLS REQUIRED) set(SSL_ENABLED ON) set(USE_MBEDTLS ON) @@ -598,7 +624,7 @@ if(CMAKE_USE_MBEDTLS) include_directories(${MBEDTLS_INCLUDE_DIRS}) endif() -if(CMAKE_USE_BEARSSL) +if(CURL_USE_BEARSSL) find_package(BearSSL REQUIRED) set(SSL_ENABLED ON) set(USE_BEARSSL ON) @@ -606,7 +632,7 @@ if(CMAKE_USE_BEARSSL) include_directories(${BEARSSL_INCLUDE_DIRS}) endif() -if(CMAKE_USE_WOLFSSL) +if(CURL_USE_WOLFSSL) find_package(WolfSSL REQUIRED) set(SSL_ENABLED ON) set(USE_WOLFSSL ON) @@ -614,7 +640,7 @@ if(CMAKE_USE_WOLFSSL) include_directories(${WolfSSL_INCLUDE_DIRS}) endif() -if(CMAKE_USE_NSS) +if(CURL_USE_NSS) find_package(NSS REQUIRED) include_directories(${NSS_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NSS_LIBRARIES}) @@ -695,13 +721,13 @@ if(NOT CURL_DISABLE_LDAP) endif() endif() - option(CMAKE_USE_OPENLDAP "Use OpenLDAP code." OFF) - mark_as_advanced(CMAKE_USE_OPENLDAP) + option(CURL_USE_OPENLDAP "Use OpenLDAP code." OFF) + mark_as_advanced(CURL_USE_OPENLDAP) set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library") set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library") - if(CMAKE_USE_OPENLDAP AND USE_WIN32_LDAP) - message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CMAKE_USE_OPENLDAP at the same time") + if(CURL_USE_OPENLDAP AND USE_WIN32_LDAP) + message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CURL_USE_OPENLDAP at the same time") endif() # Now that we know, we're not using windows LDAP... @@ -731,7 +757,7 @@ if(NOT CURL_DISABLE_LDAP) set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) #LDAP includes won't be used else() - if(CMAKE_USE_OPENLDAP) + if(CURL_USE_OPENLDAP) set(USE_OPENLDAP ON) endif() if(CMAKE_LDAP_INCLUDE_DIR) @@ -873,13 +899,13 @@ if(CURL_ZSTD) endif() #libSSH2 -option(CMAKE_USE_LIBSSH2 "Use libSSH2" ON) -mark_as_advanced(CMAKE_USE_LIBSSH2) +option(CURL_USE_LIBSSH2 "Use libSSH2" ON) +mark_as_advanced(CURL_USE_LIBSSH2) set(USE_LIBSSH2 OFF) set(HAVE_LIBSSH2 OFF) set(HAVE_LIBSSH2_H OFF) -if(CMAKE_USE_LIBSSH2) +if(CURL_USE_LIBSSH2) find_package(LibSSH2) if(LIBSSH2_FOUND) list(APPEND CURL_LIBS ${LIBSSH2_LIBRARY}) @@ -898,9 +924,9 @@ if(CMAKE_USE_LIBSSH2) endif() # libssh -option(CMAKE_USE_LIBSSH "Use libSSH" OFF) -mark_as_advanced(CMAKE_USE_LIBSSH) -if(NOT HAVE_LIBSSH2 AND CMAKE_USE_LIBSSH) +option(CURL_USE_LIBSSH "Use libSSH" OFF) +mark_as_advanced(CURL_USE_LIBSSH) +if(NOT HAVE_LIBSSH2 AND CURL_USE_LIBSSH) find_package(libssh CONFIG) if(libssh_FOUND) message(STATUS "Found libssh ${libssh_VERSION}") @@ -911,10 +937,10 @@ if(NOT HAVE_LIBSSH2 AND CMAKE_USE_LIBSSH) endif() endif() -option(CMAKE_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF) -mark_as_advanced(CMAKE_USE_GSSAPI) +option(CURL_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF) +mark_as_advanced(CURL_USE_GSSAPI) -if(CMAKE_USE_GSSAPI) +if(CURL_USE_GSSAPI) find_package(GSS) set(HAVE_GSSAPI ${GSS_FOUND}) @@ -1065,7 +1091,6 @@ endif() # Check for header files if(NOT UNIX) check_include_file_concat("windows.h" HAVE_WINDOWS_H) - check_include_file_concat("winsock.h" HAVE_WINSOCK_H) check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) check_include_file_concat("winsock2.h" HAVE_WINSOCK2_H) check_include_file_concat("wincrypt.h" HAVE_WINCRYPT_H) @@ -1643,12 +1668,10 @@ set(libdir "${CMAKE_INSTALL_PREFIX}/lib") foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) if(TARGET "${_lib}") set(_libname "${_lib}") - get_target_property(_libtype "${_libname}" TYPE) - if(_libtype STREQUAL INTERFACE_LIBRARY) - # Interface libraries can occur when an external project embeds curl and - # defined targets such as ZLIB::ZLIB by themselves. Ignore these as - # reading the LOCATION property will error out. Assume the user won't need - # this information in the .pc file. + get_target_property(_imported "${_libname}" IMPORTED) + if(NOT _imported) + # Reading the LOCATION property on non-imported target will error out. + # Assume the user won't need this information in the .pc file. continue() endif() get_target_property(_lib "${_libname}" LOCATION) diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 7b50b7c..ec2e245 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -46,8 +46,8 @@ #include <stdio.h> #include <limits.h> -#if defined(__FreeBSD__) && (__FreeBSD__ >= 2) -/* Needed for __FreeBSD_version symbol definition */ +#if (defined(__FreeBSD__) && (__FreeBSD__ >= 2)) || defined(__MidnightBSD__) +/* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */ #include <osreldate.h> #endif @@ -73,6 +73,7 @@ defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ + (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ defined(__VXWORKS__) #include <sys/select.h> #endif @@ -470,6 +471,20 @@ typedef int (*curl_debug_callback) size_t size, /* size of the data pointed to */ void *userptr); /* whatever the user please */ +/* This is the CURLOPT_PREREQFUNCTION callback prototype. */ +typedef int (*curl_prereq_callback)(void *clientp, + char *conn_primary_ip, + char *conn_local_ip, + int conn_primary_port, + int conn_local_port); + +/* Return code for when the pre-request callback has terminated without + any errors */ +#define CURL_PREREQFUNC_OK 0 +/* Return code for when the pre-request callback wants to abort the + request */ +#define CURL_PREREQFUNC_ABORT 1 + /* All possible error codes from all sorts of curl functions. Future versions may return other values, stay prepared. @@ -2043,7 +2058,8 @@ typedef enum { /* alt-svc cache file name to possibly read from/write to */ CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287), - /* maximum age of a connection to consider it for reuse (in seconds) */ + /* maximum age (idle time) of a connection to consider it for reuse + * (in seconds) */ CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288), /* SASL authorisation identity */ @@ -2102,6 +2118,23 @@ typedef enum { this option is used only if PROXY_SSL_VERIFYPEER is true */ CURLOPT(CURLOPT_PROXY_CAINFO_BLOB, CURLOPTTYPE_BLOB, 310), + /* used by scp/sftp to verify the host's public key */ + CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), + + /* Function that will be called immediately before the initial request + is made on a connection (after any protocol negotiation step). */ + CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), + + /* Data passed to the CURLOPT_PREREQFUNCTION callback */ + CURLOPT(CURLOPT_PREREQDATA, CURLOPTTYPE_CBPOINT, 313), + + /* maximum age (since creation) of a connection to consider it for reuse + * (in seconds) */ + CURLOPT(CURLOPT_MAXLIFETIME_CONN, CURLOPTTYPE_LONG, 314), + + /* Set MIME option flags. */ + CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2261,6 +2294,9 @@ CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); typedef struct curl_mime curl_mime; /* Mime context. */ typedef struct curl_mimepart curl_mimepart; /* Mime part context. */ +/* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */ +#define CURLMIMEOPT_FORMESCAPE (1<<0) /* Use backslash-escaping for forms. */ + /* * NAME curl_mime_init() * diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 9019868..1f268fa 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -30,13 +30,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.79.1" +#define LIBCURL_VERSION "7.81.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 79 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 81 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -57,7 +57,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 0x074f01 +#define LIBCURL_VERSION_NUM 0x075100 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index 37f9829..91cd95d 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -73,7 +73,8 @@ typedef enum { CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a callback */ CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */ - CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */ + CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */ + CURLM_ABORTED_BY_CALLBACK, CURLM_LAST } CURLMcode; diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index 34d0267..9e14d8a 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h @@ -317,6 +317,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_SERVICE_NAME || \ (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \ (option) == CURLOPT_SSH_KNOWNHOSTS || \ (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ @@ -363,6 +364,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_INTERLEAVEDATA || \ (option) == CURLOPT_IOCTLDATA || \ (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PREREQDATA || \ (option) == CURLOPT_PROGRESSDATA || \ (option) == CURLOPT_READDATA || \ (option) == CURLOPT_SEEKDATA || \ diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index 1d70880..a475f91 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -47,7 +47,20 @@ typedef enum { CURLUE_NO_HOST, /* 14 */ CURLUE_NO_PORT, /* 15 */ CURLUE_NO_QUERY, /* 16 */ - CURLUE_NO_FRAGMENT /* 17 */ + CURLUE_NO_FRAGMENT, /* 17 */ + CURLUE_NO_ZONEID, /* 18 */ + CURLUE_BAD_FILE_URL, /* 19 */ + CURLUE_BAD_FRAGMENT, /* 20 */ + CURLUE_BAD_HOSTNAME, /* 21 */ + CURLUE_BAD_IPV6, /* 22 */ + CURLUE_BAD_LOGIN, /* 23 */ + CURLUE_BAD_PASSWORD, /* 24 */ + CURLUE_BAD_PATH, /* 25 */ + CURLUE_BAD_QUERY, /* 26 */ + CURLUE_BAD_SCHEME, /* 27 */ + CURLUE_BAD_SLASHES, /* 28 */ + CURLUE_BAD_USER, /* 29 */ + CURLUE_LAST } CURLUcode; typedef enum { @@ -118,6 +131,12 @@ CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, const char *part, unsigned int flags); +/* + * curl_url_strerror() turns a CURLUcode value into the equivalent human + * readable error string. This is useful for printing meaningful error + * messages. + */ +CURL_EXTERN const char *curl_url_strerror(CURLUcode); #ifdef __cplusplus } /* end of extern "C" */ diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index d8fd7bd..6cf45ad 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -82,7 +82,7 @@ endif() # For windows we want to install OPENSSL_LIBRARIES dlls # and also copy them into the build tree so that testing # can find them. -if(CMAKE_USE_OPENSSL AND OPENSSL_FOUND AND WIN32) +if(CURL_USE_OPENSSL AND OPENSSL_FOUND AND WIN32) find_file(CMAKE_EAY_DLL NAME libeay32.dll HINTS ${OPENSSL_INCLUDE_DIR}/..) find_file(CMAKE_SSL_DLL NAME ssleay32.dll HINTS ${OPENSSL_INCLUDE_DIR}/..) mark_as_advanced(CMAKE_EAY_DLL CMAKE_SSL_DLL) @@ -120,12 +120,6 @@ endif() target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS}) -if(0) # This code not needed for building within CMake. -if(WIN32) - add_definitions(-D_USRDLL) -endif() -endif() - set_target_properties(${LIB_NAME} PROPERTIES COMPILE_DEFINITIONS BUILDING_LIBCURL OUTPUT_NAME ${LIBCURL_OUTPUT_NAME} @@ -149,6 +143,7 @@ endif() if(WIN32) if(BUILD_SHARED_LIBS) + set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "_USRDLL") if(MSVC) # Add "_imp" as a suffix before the extension to avoid conflicting with # the statically linked "libcurl.lib" diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 763a4aa..fd0bb6c 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -109,7 +109,9 @@ struct thread_data { struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */ int last_status; +#ifndef HAVE_CARES_GETADDRINFO struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ +#endif }; /* How long we are willing to wait for additional parallel responses after @@ -341,7 +343,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) nfds = 0; if(!nfds) - /* Call ares_process() unconditonally here, even if we simply timed out + /* Call ares_process() unconditionally here, even if we simply timed out above, as otherwise the ares name resolve won't timeout! */ ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, ARES_SOCKET_BAD); @@ -375,6 +377,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, waitperform(data, 0); +#ifndef HAVE_CARES_GETADDRINFO /* Now that we've checked for any last minute results above, see if there are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer expires. */ @@ -397,6 +400,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, ares_cancel((ares_channel)data->state.async.resolver); DEBUGASSERT(res->num_pending == 0); } +#endif if(res && !res->num_pending) { (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai); diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index 26635cd..c253cd3 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -156,13 +156,15 @@ static int hyper_each_header(void *userdata, Curl_debug(data, CURLINFO_HEADER_IN, headp, len); - writetype = CLIENTWRITE_HEADER; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(data, writetype, headp, len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; + if(!data->state.hconnect || !data->set.suppress_connect_headers) { + writetype = CLIENTWRITE_HEADER; + if(data->set.include_header) + writetype |= CLIENTWRITE_BODY; + result = Curl_client_write(data, writetype, headp, len); + if(result) { + data->state.hresult = CURLE_ABORTED_BY_CALLBACK; + return HYPER_ITER_BREAK; + } } data->info.header_size += (long)len; @@ -205,7 +207,8 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) k->exp100 = EXP100_FAILED; } } - if(data->state.hconnect && (data->req.httpcode/100 != 2)) { + if(data->state.hconnect && (data->req.httpcode/100 != 2) && + data->state.authproxy.done) { done = TRUE; result = CURLE_OK; } @@ -260,6 +263,12 @@ static CURLcode status_line(struct Curl_easy *data, if(http_version == HYPER_HTTP_VERSION_1_0) data->state.httpwant = CURL_HTTP_VERSION_1_0; + if(data->state.hconnect) + /* CONNECT */ + data->info.httpproxycode = http_status; + + /* We need to set 'httpcodeq' for functions that check the response code in + a single place. */ data->req.httpcode = http_status; result = Curl_http_statusline(data, conn); @@ -277,16 +286,18 @@ static CURLcode status_line(struct Curl_easy *data, len = Curl_dyn_len(&data->state.headerb); Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), len); - writetype = CLIENTWRITE_HEADER; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; - } + if(!data->state.hconnect || !data->set.suppress_connect_headers) { + writetype = CLIENTWRITE_HEADER; + if(data->set.include_header) + writetype |= CLIENTWRITE_BODY; + result = Curl_client_write(data, writetype, + Curl_dyn_ptr(&data->state.headerb), len); + if(result) { + data->state.hresult = CURLE_ABORTED_BY_CALLBACK; + return HYPER_ITER_BREAK; + } + } data->info.header_size += (long)len; data->req.headerbytecount += (long)len; data->req.httpcode = http_status; @@ -299,8 +310,14 @@ static CURLcode status_line(struct Curl_easy *data, */ static CURLcode empty_header(struct Curl_easy *data) { - return hyper_each_header(data, NULL, 0, NULL, 0) ? - CURLE_WRITE_ERROR : CURLE_OK; + CURLcode result = Curl_http_size(data); + if(!result) { + result = hyper_each_header(data, NULL, 0, NULL, 0) ? + CURLE_WRITE_ERROR : CURLE_OK; + if(result) + failf(data, "hyperstream: couldn't pass blank header"); + } + return result; } CURLcode Curl_hyper_stream(struct Curl_easy *data, @@ -443,11 +460,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, break; } - if(empty_header(data)) { - failf(data, "hyperstream: couldn't pass blank header"); - result = CURLE_OUT_OF_MEMORY; + result = empty_header(data); + if(result) break; - } /* Curl_http_auth_act() checks what authentication methods that are * available and decides which one (if any) to use. It will set 'newurl' @@ -584,9 +599,22 @@ static CURLcode request_target(struct Curl_easy *data, if(result) return result; - if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting path"); + if(h2 && hyper_request_set_uri_parts(req, + /* scheme */ + (uint8_t *)data->state.up.scheme, + strlen(data->state.up.scheme), + /* authority */ + (uint8_t *)conn->host.name, + strlen(conn->host.name), + /* path_and_query */ + (uint8_t *)Curl_dyn_uptr(&r), + Curl_dyn_len(&r))) { + failf(data, "error setting uri parts to hyper"); + result = CURLE_OUT_OF_MEMORY; + } + else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), + Curl_dyn_len(&r))) { + failf(data, "error setting uri to hyper"); result = CURLE_OUT_OF_MEMORY; } else @@ -850,6 +878,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) io = hyper_io_new(); if(!io) { failf(data, "Couldn't create hyper IO"); + result = CURLE_OUT_OF_MEMORY; goto error; } /* tell Hyper how to read/write network data */ @@ -862,6 +891,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) h->exec = hyper_executor_new(); if(!h->exec) { failf(data, "Couldn't create hyper executor"); + result = CURLE_OUT_OF_MEMORY; goto error; } } @@ -869,6 +899,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) options = hyper_clientconn_options_new(); if(!options) { failf(data, "Couldn't create hyper client options"); + result = CURLE_OUT_OF_MEMORY; goto error; } if(conn->negnpn == CURL_HTTP_VERSION_2) { @@ -882,6 +913,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) handshake = hyper_clientconn_handshake(io, options); if(!handshake) { failf(data, "Couldn't create hyper client handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } io = NULL; @@ -889,6 +921,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { failf(data, "Couldn't hyper_executor_push the handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } handshake = NULL; /* ownership passed on */ @@ -896,6 +929,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) task = hyper_executor_poll(h->exec); if(!task) { failf(data, "Couldn't hyper_executor_poll the handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -905,6 +939,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) req = hyper_request_new(); if(!req) { failf(data, "Couldn't hyper_request_new"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -912,12 +947,14 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(HYPERE_OK != hyper_request_set_version(req, HYPER_HTTP_VERSION_1_0)) { failf(data, "error setting HTTP version"); + result = CURLE_OUT_OF_MEMORY; goto error; } } if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { failf(data, "error setting method"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -928,51 +965,81 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) headers = hyper_request_headers(req); if(!headers) { failf(data, "hyper_request_headers"); + result = CURLE_OUT_OF_MEMORY; goto error; } rc = hyper_request_on_informational(req, http1xx_cb, data); - if(rc) - return CURLE_OUT_OF_MEMORY; + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } result = Curl_http_body(data, conn, httpreq, &te); if(result) - return result; - - if(data->state.aptr.host && - Curl_hyper_header(data, headers, data->state.aptr.host)) goto error; - if(data->state.aptr.proxyuserpwd && - Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd)) - goto error; + if(!h2) { + if(data->state.aptr.host) { + result = Curl_hyper_header(data, headers, data->state.aptr.host); + if(result) + goto error; + } + } + else { + /* For HTTP/2, we show the Host: header as if we sent it, to make it look + like for HTTP/1 but it isn't actually sent since :authority is then + used. */ + result = Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host, + strlen(data->state.aptr.host)); + if(result) + goto error; + } - if(data->state.aptr.userpwd && - Curl_hyper_header(data, headers, data->state.aptr.userpwd)) - goto error; + if(data->state.aptr.proxyuserpwd) { + result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); + if(result) + goto error; + } - if((data->state.use_range && data->state.aptr.rangeline) && - Curl_hyper_header(data, headers, data->state.aptr.rangeline)) - goto error; + if(data->state.aptr.userpwd) { + result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); + if(result) + goto error; + } + + if((data->state.use_range && data->state.aptr.rangeline)) { + result = Curl_hyper_header(data, headers, data->state.aptr.rangeline); + if(result) + goto error; + } if(data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent && - Curl_hyper_header(data, headers, data->state.aptr.uagent)) - goto error; + data->state.aptr.uagent) { + result = Curl_hyper_header(data, headers, data->state.aptr.uagent); + if(result) + goto error; + } p_accept = Curl_checkheaders(data, "Accept")?NULL:"Accept: */*\r\n"; - if(p_accept && Curl_hyper_header(data, headers, p_accept)) - goto error; - - if(te && Curl_hyper_header(data, headers, te)) - goto error; + if(p_accept) { + result = Curl_hyper_header(data, headers, p_accept); + if(result) + goto error; + } + if(te) { + result = Curl_hyper_header(data, headers, te); + if(result) + goto error; + } #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy && !Curl_checkheaders(data, "Proxy-Connection") && !Curl_checkProxyheaders(data, conn, "Proxy-Connection")) { - if(Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive")) + result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); + if(result) goto error; } #endif @@ -981,8 +1048,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(data->state.referer && !Curl_checkheaders(data, "Referer")) { data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); if(!data->state.aptr.ref) - return CURLE_OUT_OF_MEMORY; - if(Curl_hyper_header(data, headers, data->state.aptr.ref)) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_hyper_header(data, headers, data->state.aptr.ref); + if(result) goto error; } @@ -992,8 +1061,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) data->state.aptr.accept_encoding = aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); if(!data->state.aptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - if(Curl_hyper_header(data, headers, data->state.aptr.accept_encoding)) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_hyper_header(data, headers, + data->state.aptr.accept_encoding); + if(result) goto error; } else @@ -1003,38 +1075,43 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* we only consider transfer-encoding magic if libz support is built-in */ result = Curl_transferencode(data); if(result) - return result; - if(Curl_hyper_header(data, headers, data->state.aptr.te)) + goto error; + result = Curl_hyper_header(data, headers, data->state.aptr.te); + if(result) goto error; #endif result = cookies(data, conn, headers); if(result) - return result; + goto error; result = Curl_add_timecondition(data, headers); if(result) - return result; + goto error; result = Curl_add_custom_headers(data, FALSE, headers); if(result) - return result; + goto error; result = bodysend(data, conn, headers, req, httpreq); if(result) - return result; + goto error; - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); + result = Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); + if(result) + goto error; data->req.upload_chunky = FALSE; sendtask = hyper_clientconn_send(client, req); if(!sendtask) { failf(data, "hyper_clientconn_send"); + result = CURLE_OUT_OF_MEMORY; goto error; } if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { failf(data, "Couldn't hyper_executor_push the send"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -1057,7 +1134,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) Curl_safefree(data->state.aptr.proxyuserpwd); return CURLE_OK; error: - + DEBUGASSERT(result); if(io) hyper_io_free(io); @@ -1067,7 +1144,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(handshake) hyper_task_free(handshake); - return CURLE_OUT_OF_MEMORY; + return result; } void Curl_hyper_done(struct Curl_easy *data) diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index f5ba8ff..fec1937 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -113,21 +113,16 @@ static void free_bundle_hash_entry(void *freethis) int Curl_conncache_init(struct conncache *connc, int size) { - int rc; - /* allocate a new easy handle to use when closing cached connections */ connc->closure_handle = curl_easy_init(); if(!connc->closure_handle) return 1; /* bad */ - rc = Curl_hash_init(&connc->hash, size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); - if(rc) - Curl_close(&connc->closure_handle); - else - connc->closure_handle->state.conn_cache = connc; + Curl_hash_init(&connc->hash, size, Curl_hash_str, + Curl_str_key_compare, free_bundle_hash_entry); + connc->closure_handle->state.conn_cache = connc; - return rc; + return 0; /* good */ } void Curl_conncache_destroy(struct conncache *connc) diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index d61b037..5252f97 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -85,7 +85,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error); -#if defined(__DragonFly__) || defined(HAVE_WINSOCK_H) +#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) /* DragonFlyBSD and Windows use millisecond units */ #define KEEPALIVE_FACTOR(x) (x *= 1000) #else @@ -629,7 +629,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, #ifdef ENABLE_IPV6 struct sockaddr_in6 *si6 = NULL; #endif -#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) +#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) struct sockaddr_un *su = NULL; #else (void)salen; @@ -656,7 +656,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, } break; #endif -#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX) +#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) case AF_UNIX: if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { su = (struct sockaddr_un*)sa; @@ -894,6 +894,8 @@ CURLcode Curl_is_connected(struct Curl_easy *data, connkeep(conn, "HTTP/3 default"); return CURLE_OK; } + /* When a QUIC connect attempt fails, the better error explanation is in + 'result' and not in errno */ if(result) { conn->tempsock[i] = CURL_SOCKET_BAD; error = SOCKERRNO; @@ -977,6 +979,13 @@ CURLcode Curl_is_connected(struct Curl_easy *data, char buffer[STRERROR_LEN]; Curl_printable_address(conn->tempaddr[i], ipaddress, sizeof(ipaddress)); +#ifdef ENABLE_QUIC + if(conn->transport == TRNSPRT_QUIC) { + infof(data, "connect to %s port %u failed: %s", + ipaddress, conn->port, curl_easy_strerror(result)); + } + else +#endif infof(data, "connect to %s port %u failed: %s", ipaddress, conn->port, Curl_strerror(error, buffer, sizeof(buffer))); @@ -988,9 +997,11 @@ CURLcode Curl_is_connected(struct Curl_easy *data, ainext(conn, i, TRUE); status = trynextip(data, conn, sockindex, i); if((status != CURLE_COULDNT_CONNECT) || - conn->tempsock[other] == CURL_SOCKET_BAD) + conn->tempsock[other] == CURL_SOCKET_BAD) { /* the last attempt failed and no other sockets remain open */ - result = status; + if(!result) + result = status; + } } } } @@ -1016,6 +1027,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data, /* no more addresses to try */ const char *hostname; char buffer[STRERROR_LEN]; + CURLcode failreason = result; /* if the first address family runs out of addresses to try before the happy eyeball timeout, go ahead and try the next family now */ @@ -1023,6 +1035,8 @@ CURLcode Curl_is_connected(struct Curl_easy *data, if(!result) return result; + result = failreason; + #ifndef CURL_DISABLE_PROXY if(conn->bits.socksproxy) hostname = conn->socks_proxy.host.name; @@ -1036,10 +1050,14 @@ CURLcode Curl_is_connected(struct Curl_easy *data, hostname = conn->host.name; failf(data, "Failed to connect to %s port %u after " - "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", - hostname, conn->port, - Curl_timediff(now, data->progress.t_startsingle), - Curl_strerror(error, buffer, sizeof(buffer))); + "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", + hostname, conn->port, + Curl_timediff(now, data->progress.t_startsingle), +#ifdef ENABLE_QUIC + (conn->transport == TRNSPRT_QUIC) ? + curl_easy_strerror(result) : +#endif + Curl_strerror(error, buffer, sizeof(buffer))); Curl_quic_disconnect(data, conn, 0); Curl_quic_disconnect(data, conn, 1); @@ -1127,7 +1145,7 @@ void Curl_sndbufset(curl_socket_t sockfd) static int detectOsState = DETECT_OS_NONE; if(detectOsState == DETECT_OS_NONE) { - if(curlx_verify_windows_version(6, 0, PLATFORM_WINNT, + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) detectOsState = DETECT_OS_VISTA_OR_LATER; else diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index a84ff54..c03637a 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -240,7 +240,8 @@ static CURLcode inflate_stream(struct Curl_easy *data, } zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } - /* FALLTHROUGH */ + result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); + break; default: result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index b7531f7..d418efa 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -1164,7 +1164,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, bool fromfile = TRUE; char *line = NULL; - if(NULL == inc) { + if(!inc) { /* we didn't get a struct, create one */ c = calloc(1, sizeof(struct CookieInfo)); if(!c) diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index de03550..6349ec3 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -702,9 +702,6 @@ /* Define to 1 if you have the winsock2.h header file. */ #cmakedefine HAVE_WINSOCK2_H 1 -/* Define to 1 if you have the winsock.h header file. */ -#cmakedefine HAVE_WINSOCK_H 1 - /* Define this symbol if your OS supports changing the contents of argv */ #cmakedefine HAVE_WRITABLE_ARGV 1 diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index 8c5af19..76185cb 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2020, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2015 - 2021, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ #include "curl_setup.h" -#if defined(USE_NTLM) && !defined(USE_OPENSSL) +#if defined(USE_NTLM) && !defined(USE_OPENSSL) && !defined(USE_WOLFSSL) #include "curl_des.h" diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c index 5810dad..8f34056 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.c +++ b/Utilities/cmcurl/lib/curl_gssapi.c @@ -32,10 +32,12 @@ #include "curl_memory.h" #include "memdebug.h" -static char spnego_oid_bytes[] = "\x2b\x06\x01\x05\x05\x02"; -gss_OID_desc Curl_spnego_mech_oid = { 6, &spnego_oid_bytes }; -static char krb5_oid_bytes[] = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"; -gss_OID_desc Curl_krb5_mech_oid = { 9, &krb5_oid_bytes }; +gss_OID_desc Curl_spnego_mech_oid = { + 6, (char *)"\x2b\x06\x01\x05\x05\x02" +}; +gss_OID_desc Curl_krb5_mech_oid = { + 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" +}; OM_uint32 Curl_gss_init_sec_context( struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 84c7312..5755655 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,7 +26,7 @@ #define HMAC_MD5_LENGTH 16 -typedef void (* HMAC_hinit_func)(void *context); +typedef CURLcode (* HMAC_hinit_func)(void *context); typedef void (* HMAC_hupdate_func)(void *context, const unsigned char *data, unsigned int len); diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index 5739c89..b7d7c1f 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,7 +27,7 @@ #define MD5_DIGEST_LEN 16 -typedef void (* Curl_MD5_init_func)(void *context); +typedef CURLcode (* Curl_MD5_init_func)(void *context); typedef void (* Curl_MD5_update_func)(void *context, const unsigned char *data, unsigned int len); @@ -49,8 +49,8 @@ struct MD5_context { extern const struct MD5_params Curl_DIGEST_MD5[1]; extern const struct HMAC_params Curl_HMAC_MD5[1]; -void Curl_md5it(unsigned char *output, const unsigned char *input, - const size_t len); +CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, + const size_t len); struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); CURLcode Curl_MD5_update(struct MD5_context *context, diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 749b44e..ed123d0 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -49,7 +49,14 @@ in NTLM type-3 messages. */ -#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL) + #include <openssl/opensslconf.h> + #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0) + #define USE_OPENSSL_DES + #endif +#endif + +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) #ifdef USE_WOLFSSL #include <wolfssl/options.h> @@ -97,7 +104,7 @@ #elif defined(USE_WIN32_CRYPTO) # include <wincrypt.h> #else -# error "Can't compile NTLM support without a crypto library." +# error "Can't compile NTLM support without a crypto library with DES." #endif #include "urldata.h" @@ -133,7 +140,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); } -#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) /* * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * key schedule ks is also set. @@ -150,7 +157,7 @@ static void setup_des_key(const unsigned char *key_56, DES_set_odd_parity(&key); /* Set the key */ - DES_set_key(&key, ks); + DES_set_key_unchecked(&key, ks); } #elif defined(USE_GNUTLS) @@ -362,7 +369,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { -#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); @@ -420,7 +427,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data, { /* Create LanManager hashed password. */ -#if defined(USE_OPENSSL) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 4a24887..8d39e4f 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -56,8 +56,8 @@ /* Supported mechanisms */ static const struct { - const char *name; /* Name */ - size_t len; /* Name length */ + const char *name; /* Name */ + size_t len; /* Name length */ unsigned short bit; /* Flag bit */ } mechtable[] = { { "LOGIN", 5, SASL_MECH_LOGIN }, @@ -85,8 +85,11 @@ static const struct { * conn [in] - The connection data. * authused [in] - The authentication mechanism used. */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) +void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused) { + (void)conn; + (void)authused; + #if defined(USE_KERBEROS5) /* Cleanup the gssapi structure */ if(authused == SASL_MECH_GSSAPI) { @@ -107,12 +110,6 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) Curl_auth_cleanup_ntlm(&conn->ntlm); } #endif - -#if !defined(USE_KERBEROS5) && !defined(USE_NTLM) - /* Reserved for future use */ - (void)conn; - (void)authused; -#endif } /* @@ -189,16 +186,35 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, * * Initializes the SASL structure. */ -void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params) +void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, + const struct SASLproto *params) { + unsigned long auth = data->set.httpauth; + sasl->params = params; /* Set protocol dependent parameters */ sasl->state = SASL_STOP; /* Not yet running */ + sasl->curmech = NULL; /* No mechanism yet. */ sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */ - sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */ - sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */ + sasl->prefmech = params->defmechs; /* Default preferred mechanisms */ + sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */ sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */ sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */ sasl->force_ir = FALSE; /* Respect external option */ + + if(auth != CURLAUTH_BASIC) { + sasl->resetprefs = FALSE; + sasl->prefmech = SASL_AUTH_NONE; + if(auth & CURLAUTH_BASIC) + sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN; + if(auth & CURLAUTH_DIGEST) + sasl->prefmech |= SASL_MECH_DIGEST_MD5; + if(auth & CURLAUTH_NTLM) + sasl->prefmech |= SASL_MECH_NTLM; + if(auth & CURLAUTH_BEARER) + sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2; + if(auth & CURLAUTH_GSSAPI) + sasl->prefmech |= SASL_MECH_GSSAPI; + } } /* @@ -247,40 +263,45 @@ static void state(struct SASL *sasl, struct Curl_easy *data, static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data, struct bufref *out) { - unsigned char *msg; - size_t msglen; - char *serverdata = NULL; CURLcode result = CURLE_OK; - sasl->params->getmessage(data->state.buffer, &serverdata); - if(!serverdata) - result = CURLE_BAD_CONTENT_ENCODING; - else if(!*serverdata || *serverdata == '=') - Curl_bufref_set(out, NULL, 0, NULL); - else { - result = Curl_base64_decode(serverdata, &msg, &msglen); - if(!result) - Curl_bufref_set(out, msg, msglen, curl_free); + result = sasl->params->getmessage(data, out); + if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) { + unsigned char *msg; + size_t msglen; + const char *serverdata = (const char *) Curl_bufref_ptr(out); + + if(!*serverdata || *serverdata == '=') + Curl_bufref_set(out, NULL, 0, NULL); + else { + result = Curl_base64_decode(serverdata, &msg, &msglen); + if(!result) + Curl_bufref_set(out, msg, msglen, curl_free); + } } return result; } /* Encode the outgoing SASL message. */ -static CURLcode build_message(struct Curl_easy *data, struct bufref *msg) +static CURLcode build_message(struct SASL *sasl, struct Curl_easy *data, + struct bufref *msg) { CURLcode result = CURLE_OK; - char *base64; - size_t base64len; - if(!Curl_bufref_ptr(msg)) /* Empty mesage. */ - Curl_bufref_set(msg, "", 0, NULL); - else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ - Curl_bufref_set(msg, "=", 1, NULL); - else { - result = Curl_base64_encode(data, (const char *) Curl_bufref_ptr(msg), - Curl_bufref_len(msg), &base64, &base64len); - if(!result) - Curl_bufref_set(msg, base64, base64len, curl_free); + if(sasl->params->flags & SASL_FLAG_BASE64) { + if(!Curl_bufref_ptr(msg)) /* Empty message. */ + Curl_bufref_set(msg, "", 0, NULL); + else if(!Curl_bufref_len(msg)) /* Explicit empty response. */ + Curl_bufref_set(msg, "=", 1, NULL); + else { + char *base64; + size_t base64len; + + result = Curl_base64_encode(data, (const char *) Curl_bufref_ptr(msg), + Curl_bufref_len(msg), &base64, &base64len); + if(!result) + Curl_bufref_set(msg, base64, base64len, curl_free); + } } return result; @@ -310,11 +331,11 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn) * Calculate the required login details for SASL authentication. */ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, - struct connectdata *conn, bool force_ir, saslprogress *progress) { CURLcode result = CURLE_OK; - unsigned int enabledmechs; + struct connectdata *conn = data->conn; + unsigned short enabledmechs; const char *mech = NULL; struct bufref resp; saslstate state1 = SASL_STOP; @@ -471,16 +492,16 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, } if(!result && mech) { + sasl->curmech = mech; if(Curl_bufref_ptr(&resp)) - result = build_message(data, &resp); + result = build_message(sasl, data, &resp); if(sasl->params->maxirlen && strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen) Curl_bufref_free(&resp); if(!result) - result = sasl->params->sendauth(data, conn, mech, - (const char *) Curl_bufref_ptr(&resp)); + result = sasl->params->sendauth(data, mech, &resp); if(!result) { *progress = SASL_INPROGRESS; @@ -498,10 +519,10 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, * Continue the authentication. */ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, - struct connectdata *conn, int code, saslprogress *progress) { CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; saslstate newstate = SASL_FINAL; struct bufref resp; const char * const hostname = SSL_HOST_NAME(); @@ -574,7 +595,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, result = Curl_auth_create_digest_md5_message(data, &serverdata, conn->user, conn->passwd, service, &resp); - newstate = SASL_DIGESTMD5_RESP; + if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) + newstate = SASL_DIGESTMD5_RESP; break; case SASL_DIGESTMD5_RESP: /* Keep response NULL to output an empty line. */ @@ -691,7 +713,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, sasl->authmechs ^= sasl->authused; /* Start an alternative SASL authentication */ - return Curl_sasl_start(sasl, data, conn, sasl->force_ir, progress); + return Curl_sasl_start(sasl, data, sasl->force_ir, progress); default: failf(data, "Unsupported SASL authentication mechanism"); result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */ @@ -703,14 +725,13 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, switch(result) { case CURLE_BAD_CONTENT_ENCODING: /* Cancel dialog */ - result = sasl->params->sendcont(data, conn, "*"); + result = sasl->params->cancelauth(data, sasl->curmech); newstate = SASL_CANCEL; break; case CURLE_OK: - result = build_message(data, &resp); + result = build_message(sasl, data, &resp); if(!result) - result = sasl->params->sendcont(data, conn, - (const char *) Curl_bufref_ptr(&resp)); + result = sasl->params->contauth(data, sasl->curmech, &resp); break; default: newstate = SASL_STOP; /* Stop on error */ diff --git a/Utilities/cmcurl/lib/curl_sasl.h b/Utilities/cmcurl/lib/curl_sasl.h index e17d323..91458c7 100644 --- a/Utilities/cmcurl/lib/curl_sasl.h +++ b/Utilities/cmcurl/lib/curl_sasl.h @@ -24,6 +24,8 @@ #include <curl/curl.h> +#include "bufref.h" + struct Curl_easy; struct connectdata; @@ -46,17 +48,20 @@ struct connectdata; #define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL) /* Authentication mechanism strings */ -#define SASL_MECH_STRING_LOGIN "LOGIN" -#define SASL_MECH_STRING_PLAIN "PLAIN" -#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5" -#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5" -#define SASL_MECH_STRING_GSSAPI "GSSAPI" -#define SASL_MECH_STRING_EXTERNAL "EXTERNAL" -#define SASL_MECH_STRING_NTLM "NTLM" -#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2" -#define SASL_MECH_STRING_OAUTHBEARER "OAUTHBEARER" -#define SASL_MECH_STRING_SCRAM_SHA_1 "SCRAM-SHA-1" -#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256" +#define SASL_MECH_STRING_LOGIN "LOGIN" +#define SASL_MECH_STRING_PLAIN "PLAIN" +#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5" +#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5" +#define SASL_MECH_STRING_GSSAPI "GSSAPI" +#define SASL_MECH_STRING_EXTERNAL "EXTERNAL" +#define SASL_MECH_STRING_NTLM "NTLM" +#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2" +#define SASL_MECH_STRING_OAUTHBEARER "OAUTHBEARER" +#define SASL_MECH_STRING_SCRAM_SHA_1 "SCRAM-SHA-1" +#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256" + +/* SASL flags */ +#define SASL_FLAG_BASE64 0x0001 /* Messages are base64-encoded */ /* SASL machine states */ typedef enum { @@ -90,30 +95,37 @@ typedef enum { /* Protocol dependent SASL parameters */ struct SASLproto { const char *service; /* The service name */ - int contcode; /* Code to receive when continuation is expected */ - int finalcode; /* Code to receive upon authentication success */ - size_t maxirlen; /* Maximum initial response length */ - CURLcode (*sendauth)(struct Curl_easy *data, - struct connectdata *conn, - const char *mech, const char *ir); + CURLcode (*sendauth)(struct Curl_easy *data, const char *mech, + const struct bufref *ir); /* Send authentication command */ - CURLcode (*sendcont)(struct Curl_easy *data, - struct connectdata *conn, const char *contauth); + CURLcode (*contauth)(struct Curl_easy *data, const char *mech, + const struct bufref *contauth); /* Send authentication continuation */ - void (*getmessage)(char *buffer, char **outptr); + CURLcode (*cancelauth)(struct Curl_easy *data, const char *mech); + /* Cancel authentication. */ + CURLcode (*getmessage)(struct Curl_easy *data, struct bufref *out); /* Get SASL response message */ + size_t maxirlen; /* Maximum initial response + mechanism length, + or zero if no max. This is normally the max + command length - other characters count. + This has to be zero for non-base64 protocols. */ + int contcode; /* Code to receive when continuation is expected */ + int finalcode; /* Code to receive upon authentication success */ + unsigned short defmechs; /* Mechanisms enabled by default */ + unsigned short flags; /* Configuration flags. */ }; /* Per-connection parameters */ struct SASL { const struct SASLproto *params; /* Protocol dependent parameters */ - saslstate state; /* Current machine state */ + saslstate state; /* Current machine state */ + const char *curmech; /* Current mechanism id. */ unsigned short authmechs; /* Accepted authentication mechanisms */ unsigned short prefmech; /* Preferred authentication mechanism */ unsigned short authused; /* Auth mechanism used for the connection */ - bool resetprefs; /* For URL auth option parsing. */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ - bool force_ir; /* Protocol always supports initial response */ + bool resetprefs; /* For URL auth option parsing. */ + bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ + bool force_ir; /* Protocol always supports initial response */ }; /* This is used to test whether the line starts with the given mechanism */ @@ -123,7 +135,7 @@ struct SASL { /* This is used to cleanup any libraries or curl modules used by the sasl functions */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused); +void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused); /* Convert a mechanism name to a token */ unsigned short Curl_sasl_decode_mech(const char *ptr, @@ -134,19 +146,18 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, const char *value, size_t len); /* Initializes an SASL structure */ -void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params); +void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data, + const struct SASLproto *params); /* Check if we have enough auth data and capabilities to authenticate */ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn); /* Calculate the required login details for SASL authentication */ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, - struct connectdata *conn, bool force_ir, saslprogress *progress); /* Continue an SASL authentication */ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, - struct connectdata *conn, int code, saslprogress *progress); #endif /* HEADER_CURL_SASL_H */ diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 554dcc1..7421b67 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -732,7 +732,6 @@ int netware_init(void); #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) # if defined(SOCKET) || \ defined(USE_WINSOCK) || \ - defined(HAVE_WINSOCK_H) || \ defined(HAVE_WINSOCK2_H) || \ defined(HAVE_WS2TCPIP_H) # error "WinSock and lwIP TCP/IP stack definitions shall not coexist!" @@ -854,6 +853,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, ADDRESS_FAMILY sun_family; char sun_path[UNIX_PATH_MAX]; } SOCKADDR_UN, *PSOCKADDR_UN; +# define WIN32_SOCKADDR_UN # endif #endif diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index b4579d7..55dc30a 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) 2018 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,10 +28,17 @@ extern const struct HMAC_params Curl_HMAC_SHA256[1]; +#ifdef USE_WOLFSSL +/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from + * sha.h*/ +#include <wolfssl/options.h> +#include <openssl/sha.h> +#else #define SHA256_DIGEST_LENGTH 32 +#endif -void Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, - const size_t len); +CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, + const size_t len); #endif diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 06841dd..339bf54 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -83,7 +83,7 @@ CURLcode Curl_sspi_global_init(void) * have both these DLLs (security.dll forwards calls to secur32.dll) */ /* Load SSPI dll into the address space of the calling process */ - if(curlx_verify_windows_version(4, 0, PLATFORM_WINNT, VERSION_EQUAL)) + if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL)) s_hSecDll = Curl_load_library(TEXT("security.dll")); else s_hSecDll = Curl_load_library(TEXT("secur32.dll")); diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index de0c902..d6a2167 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -235,25 +235,6 @@ static CURLcode dohprobe(struct Curl_easy *data, p->dnstype = dnstype; Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); - /* Note: this is code for sending the DoH request with GET but there's still - no logic that actually enables this. We should either add that ability or - yank out the GET code. Discuss! */ - if(data->set.doh_get) { - char *b64; - size_t b64len; - result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen, - &b64, &b64len); - if(result) - goto error; - nurl = aprintf("%s?dns=%s", url, b64); - free(b64); - if(!nurl) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - url = nurl; - } - timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms <= 0) { result = CURLE_OPERATION_TIMEDOUT; @@ -268,10 +249,8 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); - if(!data->set.doh_get) { - ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); - ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); - } + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); #ifdef USE_NGHTTP2 ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 2aca938..20293a7 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -822,7 +822,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) { struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); - if(NULL == outcurl) + if(!outcurl) goto fail; /* @@ -1087,14 +1087,16 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) /* if not pausing again, force a recv/send check of this connection as the data might've been read off the socket already */ data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; - if(data->multi) - Curl_update_timer(data->multi); + if(data->multi) { + if(Curl_update_timer(data->multi)) + return CURLE_ABORTED_BY_CALLBACK; + } } if(!data->state.done) /* This transfer may have been moved in or out of the bundle, update the corresponding socket callback, if used */ - Curl_updatesocket(data); + result = Curl_updatesocket(data); return result; } diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c index 4e65e35..04871ad 100644 --- a/Utilities/cmcurl/lib/easyoptions.c +++ b/Utilities/cmcurl/lib/easyoptions.c @@ -165,10 +165,12 @@ struct curl_easyoption Curl_easyopts[] = { {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0}, + {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0}, {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0}, {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0}, {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0}, {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0}, + {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0}, {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0}, {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0}, {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0}, @@ -192,6 +194,8 @@ struct curl_easyoption Curl_easyopts[] = { {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0}, {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0}, {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0}, + {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0}, + {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0}, {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0}, {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0}, {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, @@ -271,6 +275,8 @@ struct curl_easyoption Curl_easyopts[] = { {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0}, {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CURLOT_STRING, 0}, + {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, + CURLOT_STRING, 0}, {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0}, {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0}, {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0}, @@ -354,6 +360,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (310 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (315 + 1)); } #endif diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index cad584f..5c39edb 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -876,11 +876,6 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, ftpc->count2 = 0; /* count2 counts failed CWDs */ - /* count3 is set to allow a MKD to fail once. In the case when first CWD - fails and then MKD fails (due to another session raced it to create the - dir) this then allows for a second try to CWD to it */ - ftpc->count3 = (data->set.ftp_create_missing_dirs == 2)?1:0; - if(conn->bits.reuse && ftpc->entrypath && /* no need to go to entrypath when we have an absolute path */ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { @@ -1009,7 +1004,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, } /* parse the port */ - if(ip_end != NULL) { + if(ip_end) { port_start = strchr(ip_end, ':'); if(port_start) { port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10)); @@ -3002,6 +2997,12 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, ftpc->cwdcount && !ftpc->count2) { /* try making it */ ftpc->count2++; /* counter to prevent CWD-MKD loops */ + + /* count3 is set to allow MKD to fail once per dir. In the case when + CWD fails and then MKD fails (due to another session raced it to + create the dir) this then allows for a second try to CWD to it. */ + ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0; + result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s", ftpc->dirs[ftpc->cwdcount - 1]); if(!result) @@ -4102,6 +4103,11 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, return CURLE_OK; } +#ifdef _MSC_VER +/* warning C4706: assignment within conditional expression */ +#pragma warning(disable:4706) +#endif + /*********************************************************************** * * ftp_parse_url_path() @@ -4190,7 +4196,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) } /* parse the URL path into separate path components */ - while((slashPos = strchr(curPos, '/')) != NULL) { + while((slashPos = strchr(curPos, '/'))) { size_t compLen = slashPos - curPos; /* path starts with a slash: add that as a directory */ @@ -4357,7 +4363,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, struct FTP *ftp; data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); - if(NULL == ftp) + if(!ftp) return CURLE_OUT_OF_MEMORY; ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index 12e7aa5..8848906 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c @@ -53,32 +53,25 @@ hash_element_dtor(void *user, void *element) * @unittest: 1602 * @unittest: 1603 */ -int +void Curl_hash_init(struct Curl_hash *h, int slots, hash_function hfunc, comp_function comparator, Curl_hash_dtor dtor) { - if(!slots || !hfunc || !comparator ||!dtor) { - return 1; /* failure */ - } + DEBUGASSERT(h); + DEBUGASSERT(slots); + DEBUGASSERT(hfunc); + DEBUGASSERT(comparator); + DEBUGASSERT(dtor); + h->table = NULL; h->hash_func = hfunc; h->comp_func = comparator; h->dtor = dtor; h->size = 0; h->slots = slots; - - h->table = malloc(slots * sizeof(struct Curl_llist)); - if(h->table) { - int i; - for(i = 0; i < slots; ++i) - Curl_llist_init(&h->table[i], (Curl_llist_dtor) hash_element_dtor); - return 0; /* fine */ - } - h->slots = 0; - return 1; /* failure */ } static struct Curl_hash_element * @@ -98,8 +91,9 @@ mk_hash_element(const void *key, size_t key_len, const void *p) #define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)] -/* Insert the data in the hash. If there already was a match in the hash, - * that data is replaced. +/* Insert the data in the hash. If there already was a match in the hash, that + * data is replaced. This function also "lazily" allocates the table if + * needed, as it isn't done in the _init function (anymore). * * @unittest: 1305 * @unittest: 1602 @@ -110,7 +104,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) { struct Curl_hash_element *he; struct Curl_llist_element *le; - struct Curl_llist *l = FETCH_LIST(h, key, key_len); + struct Curl_llist *l; + + DEBUGASSERT(h); + DEBUGASSERT(h->slots); + if(!h->table) { + int i; + h->table = malloc(h->slots * sizeof(struct Curl_llist)); + if(!h->table) + return NULL; /* OOM */ + for(i = 0; i < h->slots; ++i) + Curl_llist_init(&h->table[i], hash_element_dtor); + } + + l = FETCH_LIST(h, key, key_len); for(le = l->head; le; le = le->next) { he = (struct Curl_hash_element *) le->ptr; @@ -139,14 +146,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) { struct Curl_llist_element *le; - struct Curl_llist *l = FETCH_LIST(h, key, key_len); + struct Curl_llist *l; - for(le = l->head; le; le = le->next) { - struct Curl_hash_element *he = le->ptr; - if(h->comp_func(he->key, he->key_len, key, key_len)) { - Curl_llist_remove(l, le, (void *) h); - --h->size; - return 0; + DEBUGASSERT(h); + DEBUGASSERT(h->slots); + if(h->table) { + l = FETCH_LIST(h, key, key_len); + + for(le = l->head; le; le = le->next) { + struct Curl_hash_element *he = le->ptr; + if(h->comp_func(he->key, he->key_len, key, key_len)) { + Curl_llist_remove(l, le, (void *) h); + --h->size; + return 0; + } } } return 1; @@ -162,7 +175,9 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) struct Curl_llist_element *le; struct Curl_llist *l; - if(h) { + DEBUGASSERT(h); + if(h->table) { + DEBUGASSERT(h->slots); l = FETCH_LIST(h, key, key_len); for(le = l->head; le; le = le->next) { struct Curl_hash_element *he = le->ptr; @@ -204,13 +219,13 @@ Curl_hash_apply(Curl_hash *h, void *user, void Curl_hash_destroy(struct Curl_hash *h) { - int i; - - for(i = 0; i < h->slots; ++i) { - Curl_llist_destroy(&h->table[i], (void *) h); + if(h->table) { + int i; + for(i = 0; i < h->slots; ++i) { + Curl_llist_destroy(&h->table[i], (void *) h); + } + Curl_safefree(h->table); } - - Curl_safefree(h->table); h->size = 0; h->slots = 0; } @@ -235,7 +250,7 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, struct Curl_llist *list; int i; - if(!h) + if(!h || !h->table) return; for(i = 0; i < h->slots; ++i) { @@ -290,6 +305,9 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter) { struct Curl_hash *h = iter->hash; + if(!h->table) + return NULL; /* empty hash, nothing to return */ + /* Get the next element in the current list, if any */ if(iter->current_element) iter->current_element = iter->current_element->next; diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index b7f828e..e166916 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -69,11 +69,11 @@ struct Curl_hash_iterator { struct Curl_llist_element *current_element; }; -int Curl_hash_init(struct Curl_hash *h, - int slots, - hash_function hfunc, - comp_function comparator, - Curl_hash_dtor dtor); +void Curl_hash_init(struct Curl_hash *h, + int slots, + hash_function hfunc, + comp_function comparator, + Curl_hash_dtor dtor); void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); diff --git a/Utilities/cmcurl/lib/hostcheck.c b/Utilities/cmcurl/lib/hostcheck.c index cf267a7..cd45bd0 100644 --- a/Utilities/cmcurl/lib/hostcheck.c +++ b/Utilities/cmcurl/lib/hostcheck.c @@ -89,7 +89,7 @@ static int hostmatch(char *hostname, char *pattern) match. */ wildcard_enabled = 1; pattern_label_end = strchr(pattern, '.'); - if(!pattern_label_end || strchr(pattern_label_end + 1, '.') == NULL || + if(!pattern_label_end || !strchr(pattern_label_end + 1, '.') || pattern_wildcard > pattern_label_end || strncasecompare(pattern, "xn--", 4)) { wildcard_enabled = 0; diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 117caa2..911d5ed 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -507,9 +507,6 @@ static struct Curl_addrinfo *get_localhost(int port) struct sockaddr_in sa; unsigned int ipv4; unsigned short port16 = (unsigned short)(port & 0xffff); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); - if(!ca) - return NULL; /* memset to clear the sa.sin_zero field */ memset(&sa, 0, sizeof(sa)); @@ -519,6 +516,9 @@ static struct Curl_addrinfo *get_localhost(int port) return NULL; memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); + ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); + if(!ca) + return NULL; ca->ai_flags = 0; ca->ai_family = AF_INET; ca->ai_socktype = SOCK_STREAM; @@ -609,7 +609,11 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */ struct connectdata *conn = data->conn; *entry = NULL; +#ifndef CURL_DISABLE_DOH conn->bits.doh = FALSE; /* default is not */ +#else + (void)allowDOH; +#endif if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); @@ -630,11 +634,15 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, struct Curl_addrinfo *addr = NULL; int respwait = 0; +#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS) struct in_addr in; +#endif +#ifndef CURL_DISABLE_DOH #ifndef USE_RESOLVE_ON_IPS const #endif bool ipnum = FALSE; +#endif /* notify the resolver start callback */ if(data->set.resolver_start) { @@ -686,6 +694,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, #endif /* ENABLE_IPV6 */ #else /* if USE_RESOLVE_ON_IPS */ +#ifndef CURL_DISABLE_DOH /* First check if this is an IPv4 address string */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ @@ -699,6 +708,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, ipnum = TRUE; } #endif /* ENABLE_IPV6 */ +#endif /* CURL_DISABLE_DOH */ #endif /* !USE_RESOLVE_ON_IPS */ @@ -708,8 +718,10 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(strcasecompare(hostname, "localhost")) addr = get_localhost(port); +#ifndef CURL_DISABLE_DOH else if(allowDOH && data->set.doh && !ipnum) addr = Curl_doh(data, hostname, port, &respwait); +#endif else { /* Check what IP specifics the app has requested and if we can provide * it. If not, bail out. */ @@ -977,12 +989,12 @@ static void freednsentry(void *freethis) } /* - * Curl_mk_dnscache() inits a new DNS cache and returns success/failure. + * Curl_init_dnscache() inits a new DNS cache. */ -int Curl_mk_dnscache(struct Curl_hash *hash) +void Curl_init_dnscache(struct Curl_hash *hash) { - return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare, - freednsentry); + Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare, + freednsentry); } /* @@ -1210,9 +1222,10 @@ CURLcode Curl_resolv_check(struct Curl_easy *data, #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) (void)dns; #endif - +#ifndef CURL_DISABLE_DOH if(data->conn->bits.doh) return Curl_doh_is_resolved(data, dns); +#endif return Curl_resolver_is_resolved(data, dns); } @@ -1220,10 +1233,12 @@ int Curl_resolv_getsock(struct Curl_easy *data, curl_socket_t *socks) { #ifdef CURLRES_ASYNCH +#ifndef CURL_DISABLE_DOH if(data->conn->bits.doh) /* nothing to wait for during DoH resolve, those handles have their own sockets */ return GETSOCK_BLANK; +#endif return Curl_resolver_getsock(data, socks); #else (void)data; diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index 67a688a..1db5981 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -129,8 +129,8 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns); -/* init a new dns cache and return success */ -int Curl_mk_dnscache(struct Curl_hash *hash); +/* init a new dns cache */ +void Curl_init_dnscache(struct Curl_hash *hash); /* prune old entries from the DNS cache */ void Curl_hostcache_prune(struct Curl_easy *data); diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 648583c..f08a343 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -323,7 +323,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) pwd = data->state.aptr.passwd; } - out = aprintf("%s:%s", user, pwd ? pwd : ""); + out = aprintf("%s:%s", user ? user : "", pwd ? pwd : ""); if(!out) return CURLE_OUT_OF_MEMORY; @@ -1153,7 +1153,6 @@ static bool http_should_fail(struct Curl_easy *data) return data->state.authproblem; } -#ifndef USE_HYPER /* * readmoredata() is a "fread() emulation" to provide POST and/or request * data. It is used when a huge POST is to be made and the entire chunk wasn't @@ -1412,8 +1411,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in, return result; } -#endif - /* end of the add_buffer functions */ /* ------------------------------------------------------------------------- */ @@ -2375,6 +2372,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, #ifndef USE_HYPER /* Hyper always handles the body separately */ curl_off_t included_body = 0; +#else + /* from this point down, this function should not be used */ +#define Curl_buffer_send(a,b,c,d,e) CURLE_OK #endif CURLcode result = CURLE_OK; struct HTTP *http = data->req.p.http; @@ -2685,7 +2685,6 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, /* issue the request */ result = Curl_buffer_send(r, data, &data->info.request_size, 0, FIRSTSOCKET); - if(result) failf(data, "Failed sending HTTP request"); else @@ -2902,20 +2901,6 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, bool *done) { struct SingleRequest *k = &data->req; - DEBUGASSERT(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)); - if(data->req.ignore_cl) { - k->size = k->maxdownload = -1; - } - else if(k->size != -1) { - /* We wait until after all headers have been received to set this so that - we know for sure Content-Length is valid. */ - if(data->set.max_filesize && - k->size > data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - Curl_pgrsSetDownloadSize(data, k->size); - } if(data->req.newurl) { if(conn->bits.close) { @@ -3326,7 +3311,7 @@ checkhttpprefix(struct Curl_easy *data, #ifdef CURL_DOES_CONVERSIONS /* convert from the network encoding using a scratch area */ char *scratch = strdup(s); - if(NULL == scratch) { + if(!scratch) { failf(data, "Failed to allocate memory for conversion!"); return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */ } @@ -3366,7 +3351,7 @@ checkrtspprefix(struct Curl_easy *data, #ifdef CURL_DOES_CONVERSIONS /* convert from the network encoding using a scratch area */ char *scratch = strdup(s); - if(NULL == scratch) { + if(!scratch) { failf(data, "Failed to allocate memory for conversion!"); return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */ } @@ -3787,6 +3772,29 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, return CURLE_OK; } +/* Content-Length must be ignored if any Transfer-Encoding is present in the + response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is + figured out here after all headers have been received but before the final + call to the user's header callback, so that a valid content length can be + retrieved by the user in the final call. */ +CURLcode Curl_http_size(struct Curl_easy *data) +{ + struct SingleRequest *k = &data->req; + if(data->req.ignore_cl || k->chunk) { + k->size = k->maxdownload = -1; + } + else if(k->size != -1) { + if(data->set.max_filesize && + k->size > data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + Curl_pgrsSetDownloadSize(data, k->size); + k->maxdownload = k->size; + } + return CURLE_OK; +} + /* * Read any HTTP header lines from the server and pass them to the client app. */ @@ -3981,6 +3989,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } + if(!k->header) { + result = Curl_http_size(data); + if(result) + return result; + } + /* At this point we have some idea about the fate of the connection. If we are closing the connection it may result auth failure. */ #if defined(USE_NTLM) @@ -4137,31 +4151,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, reason */ *stop_reading = TRUE; #endif - else { - /* If we know the expected size of this document, we set the - maximum download size to the size of the expected - document or else, we won't know when to stop reading! - - Note that we set the download maximum even if we read a - "Connection: close" header, to make sure that - "Content-Length: 0" still prevents us from attempting to - read the (missing) response-body. - */ - /* According to RFC2616 section 4.4, we MUST ignore - Content-Length: headers if we are now receiving data - using chunked Transfer-Encoding. - */ - if(k->chunk) - k->maxdownload = k->size = -1; - } - if(-1 != k->size) { - /* We do this operation even if no_body is true, since this - data might be retrieved later with curl_easy_getinfo() - and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */ - - Curl_pgrsSetDownloadSize(data, k->size); - k->maxdownload = k->size; - } /* If max download size is *zero* (nothing) we already have nothing and can safely return ok now! But for HTTP/2, we'd @@ -4250,8 +4239,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* There can only be a 4th response code digit stored in 'digit4' if all the other fields were parsed and stored first, so nc is 5 when - digit4 a digit */ - else if(ISDIGIT(digit4)) { + digit4 a digit. + + The sscanf() line above will also allow zero-prefixed and negative + numbers, so we check for that too here. + */ + else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) { failf(data, "Unsupported response code in HTTP response"); return CURLE_UNSUPPORTED_PROTOCOL; } diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index e4ab466..b4aaba2 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -54,15 +54,11 @@ char *Curl_copy_header_value(const char *header); char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, const char *thisheader); -#ifndef USE_HYPER CURLcode Curl_buffer_send(struct dynbuf *in, struct Curl_easy *data, curl_off_t *bytes_written, curl_off_t included_body_bytes, int socketindex); -#else -#define Curl_buffer_send(a,b,c,d,e) CURLE_OK -#endif CURLcode Curl_add_timecondition(struct Curl_easy *data, #ifndef USE_HYPER @@ -289,6 +285,8 @@ struct http_conn { #endif }; +CURLcode Curl_http_size(struct Curl_easy *data); + CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, struct connectdata *conn, ssize_t *nread, diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index 6d63f43..e74400a 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -100,6 +100,7 @@ static int http2_getsock(struct Curl_easy *data, const struct http_conn *c = &conn->proto.httpc; struct SingleRequest *k = &data->req; int bitmap = GETSOCK_BLANK; + struct HTTP *stream = data->req.p.http; sock[0] = conn->sock[FIRSTSOCKET]; @@ -108,9 +109,13 @@ static int http2_getsock(struct Curl_easy *data, frame so we should always be ready for one */ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - /* we're still uploading or the HTTP/2 layer wants to send data */ - if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || - nghttp2_session_want_write(c->h2)) + /* we're (still uploading OR the HTTP/2 layer wants to send data) AND + there's a window to send data in */ + if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || + nghttp2_session_want_write(c->h2)) && + (nghttp2_session_get_remote_window_size(c->h2) && + nghttp2_session_get_stream_remote_window_size(c->h2, + stream->stream_id))) bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); return bitmap; @@ -500,10 +505,13 @@ static int set_transfer_url(struct Curl_easy *data, struct curl_pushheaders *hp) { const char *v; - CURLU *u = curl_url(); CURLUcode uc; char *url = NULL; int rc = 0; + CURLU *u = curl_url(); + + if(!u) + return 5; v = curl_pushheader_byname(hp, ":scheme"); if(v) { @@ -1953,8 +1961,19 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, nghttp2_session_resume_data(h2, stream->stream_id); } - H2BUGF(infof(data, "http2_send returns %zu for stream %u", len, - stream->stream_id)); +#ifdef DEBUG_HTTP2 + if(!len) { + infof(data, "http2_send: easy %p (stream %u) win %u/%u", + data, stream->stream_id, + nghttp2_session_get_remote_window_size(httpc->h2), + nghttp2_session_get_stream_remote_window_size(httpc->h2, + stream->stream_id) + ); + + } + infof(data, "http2_send returns %zu for stream %u", len, + stream->stream_id); +#endif return len; } diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 02663ab..751e5af 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -92,6 +92,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char *signed_headers = NULL; Curl_HttpReq httpreq; const char *method; + size_t post_data_len; const char *post_data = data->set.postfields ? data->set.postfields : ""; unsigned char sha_hash[32]; char sha_hex[65]; @@ -281,8 +282,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) goto fail; } - Curl_sha256it(sha_hash, - (const unsigned char *) post_data, strlen(post_data)); + if(data->set.postfieldsize < 0) + post_data_len = strlen(post_data); + else + post_data_len = (size_t)data->set.postfieldsize; + if(Curl_sha256it(sha_hash, (const unsigned char *) post_data, + post_data_len)) { + goto fail; + } + sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); Curl_http_method(data, conn, &method, &httpreq); @@ -315,13 +323,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) goto fail; } - Curl_sha256it(sha_hash, (unsigned char *) canonical_request, - strlen(canonical_request)); + if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request, + strlen(canonical_request))) { + goto fail; + } + sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); /* * Google allow to use rsa key instead of HMAC, so this code might change - * In the furure, but for now we support only HMAC version + * In the future, but for now we support only HMAC version */ str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ "%s\n" /* RequestDateTime */ diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index 627a11c..a6526db 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c @@ -198,6 +198,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #endif Curl_bufref_init(&ntlmmsg); + + /* connection is already authenticated, don't send a header in future + * requests so go directly to NTLMSTATE_LAST */ + if(*state == NTLMSTATE_TYPE3) + *state = NTLMSTATE_LAST; + switch(*state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ @@ -246,11 +252,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) } break; - case NTLMSTATE_TYPE3: - /* connection is already authenticated, - * don't send a header in future requests */ - *state = NTLMSTATE_LAST; - /* FALLTHROUGH */ case NTLMSTATE_LAST: Curl_safefree(*allocuserpwd); authp->done = TRUE; diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index 58489ab..e13f485 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -158,6 +158,10 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit) { struct http_connect_state *s; struct connectdata *conn = data->conn; + if(conn->handler->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); + return CURLE_UNSUPPORTED_PROTOCOL; + } if(!reinit) { CURLcode result; DEBUGASSERT(!conn->connect_state); @@ -198,18 +202,25 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit) return CURLE_OK; } -static void connect_done(struct Curl_easy *data) +void Curl_connect_done(struct Curl_easy *data) { struct connectdata *conn = data->conn; struct http_connect_state *s = conn->connect_state; - if(s->tunnel_state != TUNNEL_EXIT) { + if(s && (s->tunnel_state != TUNNEL_EXIT)) { s->tunnel_state = TUNNEL_EXIT; Curl_dyn_free(&s->rcvbuf); Curl_dyn_free(&s->req); - /* retore the protocol pointer */ - data->req.p.http = s->prot_save; + /* restore the protocol pointer, if not already done */ + if(s->prot_save) + data->req.p.http = s->prot_save; s->prot_save = NULL; + data->info.httpcode = 0; /* clear it as it might've been used for the + proxy */ + data->req.ignorebody = FALSE; +#ifdef USE_HYPER + data->state.hconnect = FALSE; +#endif infof(data, "CONNECT phase completed!"); } } @@ -284,8 +295,7 @@ static CURLcode CONNECT(struct Curl_easy *data, /* This only happens if we've looped here due to authentication reasons, and we don't really use the newly cloned URL here then. Just free() it. */ - free(data->req.newurl); - data->req.newurl = NULL; + Curl_safefree(data->req.newurl); /* initialize send-buffer */ Curl_dyn_init(req, DYN_HTTP_REQUEST); @@ -657,15 +667,13 @@ static CURLcode CONNECT(struct Curl_easy *data, if(s->close_connection && data->req.newurl) { conn->bits.proxy_connect_closed = TRUE; infof(data, "Connect me again please"); - connect_done(data); + Curl_connect_done(data); } else { free(data->req.newurl); data->req.newurl = NULL; /* failure, close this connection to avoid re-use */ streamclose(conn, "proxy CONNECT failure"); - Curl_closesocket(data, conn, conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; } /* to back to init state */ @@ -735,6 +743,7 @@ static CURLcode CONNECT(struct Curl_easy *data, io = hyper_io_new(); if(!io) { failf(data, "Couldn't create hyper IO"); + result = CURLE_OUT_OF_MEMORY; goto error; } /* tell Hyper how to read/write network data */ @@ -750,6 +759,7 @@ static CURLcode CONNECT(struct Curl_easy *data, h->exec = hyper_executor_new(); if(!h->exec) { failf(data, "Couldn't create hyper executor"); + result = CURLE_OUT_OF_MEMORY; goto error; } } @@ -757,6 +767,7 @@ static CURLcode CONNECT(struct Curl_easy *data, options = hyper_clientconn_options_new(); if(!options) { failf(data, "Couldn't create hyper client options"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -767,6 +778,7 @@ static CURLcode CONNECT(struct Curl_easy *data, handshake = hyper_clientconn_handshake(io, options); if(!handshake) { failf(data, "Couldn't create hyper client handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } io = NULL; @@ -774,6 +786,7 @@ static CURLcode CONNECT(struct Curl_easy *data, if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { failf(data, "Couldn't hyper_executor_push the handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } handshake = NULL; /* ownership passed on */ @@ -781,6 +794,7 @@ static CURLcode CONNECT(struct Curl_easy *data, task = hyper_executor_poll(h->exec); if(!task) { failf(data, "Couldn't hyper_executor_poll the handshake"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -789,14 +803,24 @@ static CURLcode CONNECT(struct Curl_easy *data, req = hyper_request_new(); if(!req) { failf(data, "Couldn't hyper_request_new"); + result = CURLE_OUT_OF_MEMORY; goto error; } if(hyper_request_set_method(req, (uint8_t *)"CONNECT", strlen("CONNECT"))) { failf(data, "error setting method"); + result = CURLE_OUT_OF_MEMORY; goto error; } + infof(data, "Establish HTTP proxy tunnel to %s:%d", + hostname, remote_port); + + /* This only happens if we've looped here due to authentication + reasons, and we don't really use the newly cloned URL here + then. Just free() it. */ + Curl_safefree(data->req.newurl); + result = CONNECT_host(data, conn, hostname, remote_port, &hostheader, &host); if(result) @@ -806,6 +830,16 @@ static CURLcode CONNECT(struct Curl_easy *data, strlen(hostheader))) { failf(data, "error setting path"); result = CURLE_OUT_OF_MEMORY; + goto error; + } + if(data->set.verbose) { + char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); + if(!se) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); + free(se); } /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, @@ -819,21 +853,29 @@ static CURLcode CONNECT(struct Curl_easy *data, (HYPERE_OK != hyper_request_set_version(req, HYPER_HTTP_VERSION_1_0))) { failf(data, "error setting HTTP version"); + result = CURLE_OUT_OF_MEMORY; goto error; } headers = hyper_request_headers(req); if(!headers) { failf(data, "hyper_request_headers"); + result = CURLE_OUT_OF_MEMORY; goto error; } - if(host && Curl_hyper_header(data, headers, host)) - goto error; - Curl_safefree(host); + if(host) { + result = Curl_hyper_header(data, headers, host); + if(result) + goto error; + Curl_safefree(host); + } - if(data->state.aptr.proxyuserpwd && - Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd)) - goto error; + if(data->state.aptr.proxyuserpwd) { + result = Curl_hyper_header(data, headers, + data->state.aptr.proxyuserpwd); + if(result) + goto error; + } if(!Curl_checkProxyheaders(data, conn, "User-Agent") && data->set.str[STRING_USERAGENT]) { @@ -843,26 +885,33 @@ static CURLcode CONNECT(struct Curl_easy *data, data->set.str[STRING_USERAGENT]); if(result) goto error; - if(Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua))) + result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); + if(result) goto error; Curl_dyn_free(&ua); } - if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") && - Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive")) - goto error; + if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection")) { + result = Curl_hyper_header(data, headers, + "Proxy-Connection: Keep-Alive"); + if(result) + goto error; + } - if(Curl_add_custom_headers(data, TRUE, headers)) + result = Curl_add_custom_headers(data, TRUE, headers); + if(result) goto error; sendtask = hyper_clientconn_send(client, req); if(!sendtask) { failf(data, "hyper_clientconn_send"); + result = CURLE_OUT_OF_MEMORY; goto error; } if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { failf(data, "Couldn't hyper_executor_push the send"); + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -875,8 +924,11 @@ static CURLcode CONNECT(struct Curl_easy *data, if(error) hypererr = hyper_task_value(task); hyper_task_free(task); - if(error) + if(error) { + /* this could probably use a better error code? */ + result = CURLE_OUT_OF_MEMORY; goto error; + } } } while(task); s->tunnel_state = TUNNEL_CONNECT; @@ -904,20 +956,28 @@ static CURLcode CONNECT(struct Curl_easy *data, h->write_waker = NULL; } } - /* FALLTHROUGH */ + break; + default: break; } + + /* If we are supposed to continue and request a new URL, which basically + * means the HTTP authentication is still going on so if the tunnel + * is complete we start over in INIT state */ + if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { + infof(data, "CONNECT request done, loop to make another"); + connect_init(data, TRUE); /* reinit */ + } } while(data->req.newurl); result = CURLE_OK; if(s->tunnel_state == TUNNEL_COMPLETE) { - data->info.httpproxycode = data->req.httpcode; if(data->info.httpproxycode/100 != 2) { if(conn->bits.close && data->req.newurl) { conn->bits.proxy_connect_closed = TRUE; infof(data, "Connect me again please"); - connect_done(data); + Curl_connect_done(data); } else { free(data->req.newurl); @@ -991,7 +1051,7 @@ CURLcode Curl_proxyCONNECT(struct Curl_easy *data, result = CONNECT(data, sockindex, hostname, remote_port); if(result || Curl_connect_complete(conn)) - connect_done(data); + Curl_connect_done(data); return result; } diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h index cdf8de4..2820e11 100644 --- a/Utilities/cmcurl/lib/http_proxy.h +++ b/Utilities/cmcurl/lib/http_proxy.h @@ -39,6 +39,7 @@ CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex); bool Curl_connect_complete(struct connectdata *conn); bool Curl_connect_ongoing(struct connectdata *conn); int Curl_connect_getsock(struct connectdata *conn); +void Curl_connect_done(struct Curl_easy *data); #else #define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN @@ -46,10 +47,10 @@ int Curl_connect_getsock(struct connectdata *conn); #define Curl_connect_complete(x) CURLE_OK #define Curl_connect_ongoing(x) FALSE #define Curl_connect_getsock(x) 0 +#define Curl_connect_done(x) #endif void Curl_connect_free(struct Curl_easy *data); -void Curl_connect_done(struct Curl_easy *data); /* struct for HTTP CONNECT state data */ struct http_connect_state { diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index 21e00b1..132b3ee 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -114,7 +114,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope, if(getifaddrs(&head) >= 0) { for(iface = head; iface != NULL; iface = iface->ifa_next) { - if(iface->ifa_addr != NULL) { + if(iface->ifa_addr) { if(iface->ifa_addr->sa_family == af) { if(strcasecompare(iface->ifa_name, interf)) { void *addr; diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 6163899..958ad14 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -78,6 +78,7 @@ #include "multiif.h" #include "url.h" #include "strcase.h" +#include "bufref.h" #include "curl_sasl.h" #include "warnless.h" @@ -101,19 +102,19 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); static CURLcode imap_setup_connection(struct Curl_easy *data, struct connectdata *conn); static char *imap_atom(const char *str, bool escape_only); -static CURLcode imap_sendf(struct Curl_easy *data, - struct connectdata *conn, const char *fmt, ...); +static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...); static CURLcode imap_parse_url_options(struct connectdata *conn); static CURLcode imap_parse_url_path(struct Curl_easy *data); static CURLcode imap_parse_custom_request(struct Curl_easy *data); static CURLcode imap_perform_authenticate(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp); + const struct bufref *initresp); static CURLcode imap_continue_authenticate(struct Curl_easy *data, - struct connectdata *conn, - const char *resp); -static void imap_get_message(char *buffer, char **outptr); + const char *mech, + const struct bufref *resp); +static CURLcode imap_cancel_authenticate(struct Curl_easy *data, + const char *mech); +static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out); /* * IMAP protocol handler. @@ -180,12 +181,15 @@ const struct Curl_handler Curl_handler_imaps = { /* SASL parameters for the imap protocol */ static const struct SASLproto saslimap = { "imap", /* The service name */ - '+', /* Code received when continuation is expected */ - IMAP_RESP_OK, /* Code to receive upon authentication success */ - 0, /* Maximum initial response length (no max) */ imap_perform_authenticate, /* Send authentication command */ imap_continue_authenticate, /* Send authentication continuation */ - imap_get_message /* Get SASL response message */ + imap_cancel_authenticate, /* Send authentication cancellation */ + imap_get_message, /* Get SASL response message */ + 0, /* No maximum initial response length */ + '+', /* Code received when continuation is expected */ + IMAP_RESP_OK, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ }; @@ -293,6 +297,7 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, !strcasecompare(imap->custom, "EXPUNGE") && !strcasecompare(imap->custom, "LSUB") && !strcasecompare(imap->custom, "UID") && + !strcasecompare(imap->custom, "GETQUOTAROOT") && !strcasecompare(imap->custom, "NOOP"))) return FALSE; break; @@ -324,7 +329,7 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, /* Do we have a continuation response? This should be a + symbol followed by a space and optionally some text as per RFC-3501 for the AUTHENTICATE and APPEND commands and as outlined in Section 4. Examples of RFC-4959 but - some e-mail servers ignore this and only send a single + instead. */ + some email servers ignore this and only send a single + instead. */ if(imap && !imap->custom && ((len == 3 && line[0] == '+') || (len >= 2 && !memcmp("+ ", line, 2)))) { switch(imapc->state) { @@ -352,34 +357,32 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, * * Gets the authentication message from the response buffer. */ -static void imap_get_message(char *buffer, char **outptr) +static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) { - size_t len = strlen(buffer); - char *message = NULL; + char *message = data->state.buffer; + size_t len = strlen(message); if(len > 2) { /* Find the start of the message */ len -= 2; - for(message = buffer + 2; *message == ' ' || *message == '\t'; - message++, len--) + for(message += 2; *message == ' ' || *message == '\t'; message++, len--) ; /* Find the end of the message */ - for(; len--;) + while(len--) if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && message[len] != '\t') break; /* Terminate the message */ - if(++len) { - message[len] = '\0'; - } + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); } else /* junk input => zero length output */ - message = &buffer[len]; + Curl_bufref_set(out, "", 0, NULL); - *outptr = message; + return CURLE_OK; } /*********************************************************************** @@ -437,7 +440,7 @@ static CURLcode imap_perform_capability(struct Curl_easy *data, imapc->tls_supported = FALSE; /* Clear the TLS capability */ /* Send the CAPABILITY command */ - result = imap_sendf(data, conn, "CAPABILITY"); + result = imap_sendf(data, "CAPABILITY"); if(!result) state(data, IMAP_CAPABILITY); @@ -451,11 +454,10 @@ static CURLcode imap_perform_capability(struct Curl_easy *data, * * Sends the STARTTLS command to start the upgrade to TLS. */ -static CURLcode imap_perform_starttls(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode imap_perform_starttls(struct Curl_easy *data) { /* Send the STARTTLS command */ - CURLcode result = imap_sendf(data, conn, "STARTTLS"); + CURLcode result = imap_sendf(data, "STARTTLS"); if(!result) state(data, IMAP_STARTTLS); @@ -516,7 +518,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, passwd = imap_atom(conn->passwd, false); /* Send the LOGIN command */ - result = imap_sendf(data, conn, "LOGIN %s %s", user ? user : "", + result = imap_sendf(data, "LOGIN %s %s", user ? user : "", passwd ? passwd : ""); free(user); @@ -536,20 +538,19 @@ static CURLcode imap_perform_login(struct Curl_easy *data, * SASL authentication mechanism. */ static CURLcode imap_perform_authenticate(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp) + const struct bufref *initresp) { CURLcode result = CURLE_OK; - (void)data; + const char *ir = (const char *) Curl_bufref_ptr(initresp); - if(initresp) { + if(ir) { /* Send the AUTHENTICATE command with the initial response */ - result = imap_sendf(data, conn, "AUTHENTICATE %s %s", mech, initresp); + result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir); } else { /* Send the AUTHENTICATE command */ - result = imap_sendf(data, conn, "AUTHENTICATE %s", mech); + result = imap_sendf(data, "AUTHENTICATE %s", mech); } return result; @@ -559,15 +560,34 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data, * * imap_continue_authenticate() * - * Sends SASL continuation data or cancellation. + * Sends SASL continuation data. */ static CURLcode imap_continue_authenticate(struct Curl_easy *data, - struct connectdata *conn, - const char *resp) + const char *mech, + const struct bufref *resp) { - struct imap_conn *imapc = &conn->proto.imapc; + struct imap_conn *imapc = &data->conn->proto.imapc; + + (void)mech; - return Curl_pp_sendf(data, &imapc->pp, "%s", resp); + return Curl_pp_sendf(data, &imapc->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * imap_cancel_authenticate() + * + * Sends SASL cancellation. + */ +static CURLcode imap_cancel_authenticate(struct Curl_easy *data, + const char *mech) +{ + struct imap_conn *imapc = &data->conn->proto.imapc; + + (void)mech; + + return Curl_pp_sendf(data, &imapc->pp, "*"); } /*********************************************************************** @@ -594,8 +614,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, } /* Calculate the SASL login details */ - result = Curl_sasl_start(&imapc->sasl, data, conn, - imapc->ir_supported, &progress); + result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress); if(!result) { if(progress == SASL_INPROGRESS) @@ -622,12 +641,11 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, static CURLcode imap_perform_list(struct Curl_easy *data) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; struct IMAP *imap = data->req.p.imap; if(imap->custom) /* Send the custom request */ - result = imap_sendf(data, conn, "%s%s", imap->custom, + result = imap_sendf(data, "%s%s", imap->custom, imap->custom_params ? imap->custom_params : ""); else { /* Make sure the mailbox is in the correct atom format if necessary */ @@ -637,7 +655,7 @@ static CURLcode imap_perform_list(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; /* Send the LIST command */ - result = imap_sendf(data, conn, "LIST \"%s\" *", mailbox); + result = imap_sendf(data, "LIST \"%s\" *", mailbox); free(mailbox); } @@ -678,7 +696,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; /* Send the SELECT command */ - result = imap_sendf(data, conn, "SELECT %s", mailbox); + result = imap_sendf(data, "SELECT %s", mailbox); free(mailbox); @@ -694,8 +712,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data) * * Sends a FETCH command to initiate the download of a message. */ -static CURLcode imap_perform_fetch(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode imap_perform_fetch(struct Curl_easy *data) { CURLcode result = CURLE_OK; struct IMAP *imap = data->req.p.imap; @@ -704,21 +721,21 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data, /* Send the FETCH command */ if(imap->partial) - result = imap_sendf(data, conn, "UID FETCH %s BODY[%s]<%s>", + result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>", imap->uid, imap->section ? imap->section : "", imap->partial); else - result = imap_sendf(data, conn, "UID FETCH %s BODY[%s]", + result = imap_sendf(data, "UID FETCH %s BODY[%s]", imap->uid, imap->section ? imap->section : ""); } else if(imap->mindex) { /* Send the FETCH command */ if(imap->partial) - result = imap_sendf(data, conn, "FETCH %s BODY[%s]<%s>", + result = imap_sendf(data, "FETCH %s BODY[%s]<%s>", imap->mindex, imap->section ? imap->section : "", imap->partial); else - result = imap_sendf(data, conn, "FETCH %s BODY[%s]", + result = imap_sendf(data, "FETCH %s BODY[%s]", imap->mindex, imap->section ? imap->section : ""); } else { @@ -740,7 +757,6 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data, static CURLcode imap_perform_append(struct Curl_easy *data) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; struct IMAP *imap = data->req.p.imap; char *mailbox; @@ -791,7 +807,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; /* Send the APPEND command */ - result = imap_sendf(data, conn, + result = imap_sendf(data, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", mailbox, data->state.infilesize); @@ -809,8 +825,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) * * Sends a SEARCH command. */ -static CURLcode imap_perform_search(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode imap_perform_search(struct Curl_easy *data) { CURLcode result = CURLE_OK; struct IMAP *imap = data->req.p.imap; @@ -822,7 +837,7 @@ static CURLcode imap_perform_search(struct Curl_easy *data, } /* Send the SEARCH command */ - result = imap_sendf(data, conn, "SEARCH %s", imap->query); + result = imap_sendf(data, "SEARCH %s", imap->query); if(!result) state(data, IMAP_SEARCH); @@ -836,11 +851,10 @@ static CURLcode imap_perform_search(struct Curl_easy *data, * * Performs the logout action prior to sclose() being called. */ -static CURLcode imap_perform_logout(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode imap_perform_logout(struct Curl_easy *data) { /* Send the LOGOUT command */ - CURLcode result = imap_sendf(data, conn, "LOGOUT"); + CURLcode result = imap_sendf(data, "LOGOUT"); if(!result) state(data, IMAP_LOGOUT); @@ -938,7 +952,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ - result = imap_perform_starttls(data, conn); + result = imap_perform_starttls(data); } else if(data->set.use_ssl <= CURLUSESSL_TRY) result = imap_perform_authentication(data, conn); @@ -993,7 +1007,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ - result = Curl_sasl_continue(&imapc->sasl, data, conn, imapcode, &progress); + result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress); if(!result) switch(progress) { case SASL_DONE: @@ -1094,9 +1108,9 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, if(imap->custom) result = imap_perform_list(data); else if(imap->query) - result = imap_perform_search(data, conn); + result = imap_perform_search(data); else - result = imap_perform_fetch(data, conn); + result = imap_perform_fetch(data); } } else { @@ -1441,7 +1455,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done) /* Set the default preferred authentication type and mechanism */ imapc->preftype = IMAP_TYPE_ANY; - Curl_sasl_init(&imapc->sasl, &saslimap); + Curl_sasl_init(&imapc->sasl, data, &saslimap); Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD); /* Initialise the pingpong layer */ @@ -1568,10 +1582,10 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, result = imap_perform_list(data); else if(!imap->custom && selected && (imap->uid || imap->mindex)) /* FETCH from the same mailbox */ - result = imap_perform_fetch(data, conn); + result = imap_perform_fetch(data); else if(!imap->custom && selected && imap->query) /* SEARCH the current mailbox */ - result = imap_perform_search(data, conn); + result = imap_perform_search(data); else if(imap->mailbox && !selected && (imap->custom || imap->uid || imap->mindex || imap->query)) /* SELECT the mailbox */ @@ -1643,7 +1657,7 @@ static CURLcode imap_disconnect(struct Curl_easy *data, /* The IMAP session may or may not have been allocated/setup at this point! */ if(!dead_connection && conn->bits.protoconnstart) { - if(!imap_perform_logout(data, conn)) + if(!imap_perform_logout(data)) (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */ } @@ -1747,17 +1761,16 @@ static CURLcode imap_setup_connection(struct Curl_easy *data, * * Designed to never block. */ -static CURLcode imap_sendf(struct Curl_easy *data, - struct connectdata *conn, const char *fmt, ...) +static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) { CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; + struct imap_conn *imapc = &data->conn->proto.imapc; DEBUGASSERT(fmt); /* Calculate the tag based on the connection ID and command ID */ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi(conn->connection_id % 26), + 'A' + curlx_sltosi(data->conn->connection_id % 26), (++imapc->cmdid)%1000); /* start with a blank buffer */ @@ -1911,8 +1924,6 @@ static CURLcode imap_parse_url_options(struct connectdata *conn) struct imap_conn *imapc = &conn->proto.imapc; const char *ptr = conn->options; - imapc->sasl.resetprefs = TRUE; - while(!result && ptr && *ptr) { const char *key = ptr; const char *value; diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index 4923cae..ada57af 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c @@ -1,6 +1,6 @@ /* This is from the BIND 4.9.4 release, modified to compile by itself */ -/* Copyright (c) 1996 - 2020 by Internet Software Consortium. +/* Copyright (c) 1996 - 2021 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -174,7 +174,7 @@ inet_pton6(const char *src, unsigned char *dst) pch = strchr((xdigits = xdigits_l), ch); if(!pch) pch = strchr((xdigits = xdigits_u), ch); - if(pch != NULL) { + if(pch) { val <<= 4; val |= (pch - xdigits); if(++saw_xdigit > 4) @@ -211,7 +211,7 @@ inet_pton6(const char *src, unsigned char *dst) *tp++ = (unsigned char) ((val >> 8) & 0xff); *tp++ = (unsigned char) (val & 0xff); } - if(colonp != NULL) { + if(colonp) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index 659947d..54edb47 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -374,7 +374,7 @@ static void krb5_end(void *app_data) } } -static struct Curl_sec_client_mech Curl_krb5_client_mech = { +static const struct Curl_sec_client_mech Curl_krb5_client_mech = { "GSSAPI", sizeof(gss_ctx_id_t), krb5_init, @@ -684,7 +684,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, (void) data; if(!conn->mech) - /* not inititalized, return error */ + /* not initialized, return error */ return -1; DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); @@ -768,7 +768,7 @@ static int sec_set_protection_level(struct Curl_easy *data) } } - /* Now try to negiociate the protection level. */ + /* Now try to negotiate the protection level. */ code = ftp_send_command(data, "PROT %c", level_to_char(level)); if(code < 0) @@ -880,7 +880,7 @@ Curl_sec_login(struct Curl_easy *data, struct connectdata *conn) void Curl_sec_end(struct connectdata *conn) { - if(conn->mech != NULL && conn->mech->end) + if(conn->mech && conn->mech->end) conn->mech->end(conn->app_data); free(conn->app_data); conn->app_data = NULL; diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 1d9e44c..3154db5 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -464,6 +464,11 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #endif #endif /* CURL_LDAP_USE_SSL */ } + else if(data->set.use_ssl > CURLUSESSL_TRY) { + failf(data, "LDAP local: explicit TLS not supported"); + result = CURLE_NOT_BUILT_IN; + goto quit; + } else { server = ldap_init(host, (int)conn->port); if(!server) { @@ -590,7 +595,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) attr_len = strlen(attr); vals = ldap_get_values_len(server, entryIterator, attribute); - if(vals != NULL) { + if(vals) { for(i = 0; (vals[i] != NULL); i++) { result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); if(result) { diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc index 3f7ae16..fde6c8c 100644 --- a/Utilities/cmcurl/lib/libcurl.rc +++ b/Utilities/cmcurl/lib/libcurl.rc @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -51,7 +51,7 @@ BEGIN VALUE "OriginalFilename", "libcurl.dll\0" VALUE "ProductName", "The curl library\0" VALUE "ProductVersion", LIBCURL_VERSION "\0" - VALUE "LegalCopyright", "\xa9 " LIBCURL_COPYRIGHT "\0" /* a9: Copyright symbol */ + VALUE "LegalCopyright", "Copyright (C) " LIBCURL_COPYRIGHT "\0" VALUE "License", "https://curl.se/docs/copyright.html\0" END END diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index e0ec739..e78da7d 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c @@ -106,9 +106,7 @@ Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e, e->next->prev = NULL; } else { - if(!e->prev) - list->head = e->next; - else + if(e->prev) e->prev->next = e->next; if(!e->next) diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index e7a428f..117cce4 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -27,6 +27,7 @@ #include "curl_md4.h" #include "warnless.h" + #ifdef USE_OPENSSL #include <openssl/opensslconf.h> #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) @@ -35,6 +36,13 @@ #endif #endif /* USE_OPENSSL */ +#ifdef USE_WOLFSSL +#include <wolfssl/options.h> +#ifdef NO_MD4 +#define OPENSSL_NO_MD4 +#endif +#endif + #ifdef USE_MBEDTLS #include <mbedtls/version.h> #if MBEDTLS_VERSION_NUMBER >= 0x03000000 @@ -74,8 +82,9 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) md4_digest(ctx, MD4_DIGEST_SIZE, result); } -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) -/* When OpenSSL is available we use the MD4-functions from OpenSSL */ +#elif (defined(USE_OPENSSL) || defined(USE_WOLFSSL)) && \ + !defined(OPENSSL_NO_MD4) +/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ #include <openssl/md4.h> #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ @@ -180,7 +189,7 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) { if(!ctx->data) { ctx->data = malloc(size); - if(ctx->data != NULL) { + if(ctx->data) { memcpy(ctx->data, data, size); ctx->size = size; } @@ -189,7 +198,7 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) static void MD4_Final(unsigned char *result, MD4_CTX *ctx) { - if(ctx->data != NULL) { + if(ctx->data) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) mbedtls_md4(ctx->data, ctx->size, result); #else diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 983ed97..c6923e0 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -39,6 +39,20 @@ #endif #endif /* USE_MBEDTLS */ +#if defined(USE_OPENSSL) && !defined(USE_AMISSL) + #include <openssl/opensslconf.h> + #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) + #define USE_OPENSSL_MD5 + #endif +#endif + +#ifdef USE_WOLFSSL + #include <wolfssl/options.h> + #ifndef NO_MD5 + #define USE_WOLFSSL_MD5 + #endif +#endif + #if defined(USE_GNUTLS) #include <nettle/md5.h> @@ -48,9 +62,10 @@ typedef struct md5_ctx MD5_CTX; -static void MD5_Init(MD5_CTX *ctx) +static CURLcode MD5_Init(MD5_CTX *ctx) { md5_init(ctx); + return CURLE_OK; } static void MD5_Update(MD5_CTX *ctx, @@ -65,8 +80,9 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) md5_digest(ctx, 16, digest); } -#elif defined(USE_OPENSSL) && !defined(USE_AMISSL) -/* When OpenSSL is available we use the MD5-function from OpenSSL */ +#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5) + +/* When OpenSSL or wolfSSL is available, we use their MD5 functions. */ #include <openssl/md5.h> #include "curl_memory.h" /* The last #include file should be: */ @@ -83,13 +99,14 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) typedef mbedtls_md5_context MD5_CTX; -static void MD5_Init(MD5_CTX *ctx) +static CURLcode MD5_Init(MD5_CTX *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_md5_starts(ctx); #else (void) mbedtls_md5_starts_ret(ctx); #endif + return CURLE_OK; } static void MD5_Update(MD5_CTX *ctx, @@ -131,9 +148,10 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx) /* The last #include file should be: */ #include "memdebug.h" -static void MD5_Init(MD5_CTX *ctx) +static CURLcode MD5_Init(MD5_CTX *ctx) { CC_MD5_Init(ctx); + return CURLE_OK; } static void MD5_Update(MD5_CTX *ctx, @@ -161,12 +179,13 @@ struct md5_ctx { }; typedef struct md5_ctx MD5_CTX; -static void MD5_Init(MD5_CTX *ctx) +static CURLcode MD5_Init(MD5_CTX *ctx) { if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash); } + return CURLE_OK; } static void MD5_Update(MD5_CTX *ctx, @@ -246,7 +265,7 @@ struct md5_ctx { }; typedef struct md5_ctx MD5_CTX; -static void MD5_Init(MD5_CTX *ctx); +static CURLcode MD5_Init(MD5_CTX *ctx); static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); static void MD5_Final(unsigned char *result, MD5_CTX *ctx); @@ -407,7 +426,7 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) return ptr; } -static void MD5_Init(MD5_CTX *ctx) +static CURLcode MD5_Init(MD5_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -416,6 +435,8 @@ static void MD5_Init(MD5_CTX *ctx) ctx->lo = 0; ctx->hi = 0; + + return CURLE_OK; } static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) @@ -540,8 +561,9 @@ const struct MD5_params Curl_DIGEST_MD5[] = { /* * @unittest: 1601 + * Returns CURLE_OK on success. */ -void Curl_md5it(unsigned char *outbuffer, const unsigned char *input, +CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, const size_t len) { MD5_CTX ctx; @@ -549,6 +571,8 @@ void Curl_md5it(unsigned char *outbuffer, const unsigned char *input, MD5_Init(&ctx); MD5_Update(&ctx, input, curlx_uztoui(len)); MD5_Final(outbuffer, &ctx); + + return CURLE_OK; } struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 0bf1b46..7783b89 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -40,6 +40,7 @@ #include "rand.h" #include "slist.h" #include "strcase.h" +#include "dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -279,29 +280,52 @@ static void mimesetstate(struct mime_state *state, /* Escape header string into allocated memory. */ -static char *escape_string(const char *src) -{ - size_t bytecount = 0; - size_t i; - char *dst; +static char *escape_string(struct Curl_easy *data, + const char *src, enum mimestrategy strategy) +{ + CURLcode result; + struct dynbuf db; + const char * const *table; + const char * const *p; + /* replace first character by rest of string. */ + static const char * const mimetable[] = { + "\\\\\\", + "\"\\\"", + NULL + }; + /* WHATWG HTML living standard 4.10.21.8 2 specifies: + For field names and filenames for file fields, the result of the + encoding in the previous bullet point must be escaped by replacing + any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D` + and 0x22 (") with `%22`. + The user agent must not perform any other escapes. */ + static const char * const formtable[] = { + "\"%22", + "\r%0D", + "\n%0A", + NULL + }; - for(i = 0; src[i]; i++) - if(src[i] == '"' || src[i] == '\\') - bytecount++; + table = formtable; + /* data can be NULL when this function is called indirectly from + curl_formget(). */ + if(strategy == MIMESTRATEGY_MAIL || + (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE))) + table = mimetable; - bytecount += i; - dst = malloc(bytecount + 1); - if(!dst) - return NULL; + Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); + + for(result = Curl_dyn_add(&db, ""); !result && *src; src++) { + for(p = table; *p && **p != *src; p++) + ; - for(i = 0; *src; src++) { - if(*src == '"' || *src == '\\') - dst[i++] = '\\'; - dst[i++] = *src; + if(*p) + result = Curl_dyn_add(&db, *p + 1); + else + result = Curl_dyn_addn(&db, src, 1); } - dst[i] = '\0'; - return dst; + return Curl_dyn_ptr(&db); } /* Check if header matches. */ @@ -462,11 +486,13 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, /* Buffered data size can only be 0, 1 or 2. */ ptr[2] = ptr[3] = '='; i = 0; - switch(st->bufend - st->bufbeg) { - case 2: - i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; - /* FALLTHROUGH */ - case 1: + + /* If there is buffered data */ + if(st->bufend != st->bufbeg) { + + if(st->bufend - st->bufbeg == 2) + i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; + i |= (st->buf[st->bufbeg] & 0xFF) << 16; ptr[0] = base64[(i >> 18) & 0x3F]; ptr[1] = base64[(i >> 12) & 0x3F]; @@ -476,7 +502,6 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, } cursize += 4; st->pos += 4; - break; } } } @@ -1865,12 +1890,12 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part, char *filename = NULL; if(part->name) { - name = escape_string(part->name); + name = escape_string(part->easy, part->name, strategy); if(!name) ret = CURLE_OUT_OF_MEMORY; } if(!ret && part->filename) { - filename = escape_string(part->filename); + filename = escape_string(part->easy, part->filename, strategy); if(!filename) ret = CURLE_OUT_OF_MEMORY; } @@ -1954,7 +1979,8 @@ void Curl_mime_unpause(curl_mimepart *part) } -#else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ +#else /* !CURL_DISABLE_HTTP && !CURL_DISABLE_MIME || + !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ /* Mime not compiled in: define stubs for externally-referenced functions. */ curl_mime *curl_mime_init(CURL *easy) diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index 7a1aec5..0fd3afc 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -858,7 +858,7 @@ static int dprintf_formatf( { void *ptr; ptr = (void *) p->data.ptr; - if(ptr != NULL) { + if(ptr) { /* If the pointer is not NULL, write it as a %#x spec. */ base = 16; digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 518ceb5..f8dcc63 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -243,6 +243,26 @@ static void trhash_dtor(void *nada) (void)nada; } +/* + * The sockhash has its own separate subhash in each entry that need to be + * safely destroyed first. + */ +static void sockhash_destroy(struct Curl_hash *h) +{ + struct Curl_hash_iterator iter; + struct Curl_hash_element *he; + + DEBUGASSERT(h); + Curl_hash_start_iterate(h, &iter); + he = Curl_hash_next_element(&iter); + while(he) { + struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr; + Curl_hash_destroy(&sh->transfers); + he = Curl_hash_next_element(&iter); + } + Curl_hash_destroy(h); +} + /* make sure this socket is present in the hash for this handle */ static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh, @@ -261,11 +281,8 @@ static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh, if(!check) return NULL; /* major failure */ - if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, - trhash_compare, trhash_dtor)) { - free(check); - return NULL; - } + Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare, + trhash_dtor); /* make/add new hash entry */ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { @@ -332,10 +349,10 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num) * per call." * */ -static int sh_init(struct Curl_hash *hash, int hashsize) +static void sh_init(struct Curl_hash *hash, int hashsize) { - return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, - sh_freeentry); + Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, + sh_freeentry); } /* @@ -362,11 +379,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ multi->magic = CURL_MULTI_HANDLE; - if(Curl_mk_dnscache(&multi->hostcache)) - goto error; + Curl_init_dnscache(&multi->hostcache); - if(sh_init(&multi->sockhash, hashsize)) - goto error; + sh_init(&multi->sockhash, hashsize); if(Curl_conncache_init(&multi->conn_cache, chashsize)) goto error; @@ -405,7 +420,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ error: - Curl_hash_destroy(&multi->sockhash); + sockhash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->hostcache); Curl_conncache_destroy(&multi->conn_cache); Curl_llist_destroy(&multi->msglist, NULL); @@ -424,6 +439,7 @@ struct Curl_multi *curl_multi_init(void) CURLMcode curl_multi_add_handle(struct Curl_multi *multi, struct Curl_easy *data) { + CURLMcode rc; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -440,6 +456,15 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(multi->dead) { + /* a "dead" handle cannot get added transfers while any existing easy + handles are still alive - but if there are none alive anymore, it is + fine to start over and unmark the "deadness" of this handle */ + if(multi->num_alive) + return CURLM_ABORTED_BY_CALLBACK; + multi->dead = FALSE; + } + /* Initialize timeout list for this handle */ Curl_llist_init(&data->state.timeoutlist, NULL); @@ -452,6 +477,34 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; + /* make the Curl_easy refer back to this multi handle - before Curl_expire() + is called. */ + data->multi = multi; + + /* Set the timeout for this handle to expire really soon so that it will + be taken care of even when this handle is added in the midst of operation + when only the curl_multi_socket() API is used. During that flow, only + sockets that time-out or have actions will be dealt with. Since this + handle has no action yet, we make sure it times out to get things to + happen. */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + /* A somewhat crude work-around for a little glitch in Curl_update_timer() + that happens if the lastcall time is set to the same time when the handle + is removed as when the next handle is added, as then the check in + Curl_update_timer() that prevents calling the application multiple times + with the same timer info will not trigger and then the new handle's + timeout will not be notified to the app. + + The work-around is thus simply to clear the 'lastcall' variable to force + Curl_update_timer() to always trigger a callback to the app when a new + easy handle is added */ + memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); + + rc = Curl_update_timer(multi); + if(rc) + return rc; + /* set the easy handle */ multistate(data, MSTATE_INIT); @@ -492,35 +545,12 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, multi->easylp = multi->easyp = data; /* both first and last */ } - /* make the Curl_easy refer back to this multi handle */ - data->multi = multi; - - /* Set the timeout for this handle to expire really soon so that it will - be taken care of even when this handle is added in the midst of operation - when only the curl_multi_socket() API is used. During that flow, only - sockets that time-out or have actions will be dealt with. Since this - handle has no action yet, we make sure it times out to get things to - happen. */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - /* increase the node-counter */ multi->num_easy++; /* increase the alive-counter */ multi->num_alive++; - /* A somewhat crude work-around for a little glitch in Curl_update_timer() - that happens if the lastcall time is set to the same time when the handle - is removed as when the next handle is added, as then the check in - Curl_update_timer() that prevents calling the application multiple times - with the same timer info will not trigger and then the new handle's - timeout will not be notified to the app. - - The work-around is thus simply to clear the 'lastcall' variable to force - Curl_update_timer() to always trigger a callback to the app when a new - easy handle is added */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - CONNCACHE_LOCK(data); /* The closure handle only ever has default timeouts set. To improve the state somewhat we clone the timeouts from each added handle so that the @@ -533,14 +563,13 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->set.no_signal; CONNCACHE_UNLOCK(data); - Curl_update_timer(multi); return CURLM_OK; } #if 0 /* Debug-function, used like this: * - * Curl_hash_print(multi->sockhash, debug_print_sock_hash); + * Curl_hash_print(&multi->sockhash, debug_print_sock_hash); * * Enable the hash print function first by editing hash.c */ @@ -548,8 +577,8 @@ static void debug_print_sock_hash(void *p) { struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; - fprintf(stderr, " [easy %p/magic %x/socket %d]", - (void *)sh->data, sh->data->magic, (int)sh->socket); + fprintf(stderr, " [readers %u][writers %u]", + sh->readers, sh->writers); } #endif @@ -562,7 +591,8 @@ static CURLcode multi_done(struct Curl_easy *data, struct connectdata *conn = data->conn; unsigned int i; - DEBUGF(infof(data, "multi_done")); + DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d", + (int)status, (int)premature, data->state.done)); if(data->state.done) /* Stop if multi_done() has already been called */ @@ -719,6 +749,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, struct Curl_easy *easy = data; bool premature; struct Curl_llist_element *e; + CURLMcode rc; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -792,8 +823,11 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* change state without using multistate(), only to make singlesocket() do what we want */ data->mstate = MSTATE_COMPLETED; - singlesocket(multi, easy); /* to let the application know what sockets that - vanish with this handle */ + + /* This ignores the return code even in case of problems because there's + nothing more to do about that, here */ + (void)singlesocket(multi, easy); /* to let the application know what sockets + that vanish with this handle */ /* Remove the association between the connection and the handle */ Curl_detach_connnection(data); @@ -858,7 +892,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, process_pending_handles(multi); - Curl_update_timer(multi); + rc = Curl_update_timer(multi); + if(rc) + return rc; return CURLM_OK; } @@ -878,6 +914,7 @@ void Curl_detach_connnection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { + Curl_connect_done(data); /* if mid-CONNECT, shut it down */ Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); Curl_ssl_detach_conn(data, conn); } @@ -1742,6 +1779,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; + if(multi->dead) { + /* a multi-level callback returned error before, meaning every individual + transfer now has failed */ + result = CURLE_ABORTED_BY_CALLBACK; + Curl_posttransfer(data); + multi_done(data, result, FALSE); + multistate(data, MSTATE_COMPLETED); + } + do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -1892,7 +1938,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, down. If the name has not yet been resolved, it is likely that new sockets have been opened in an attempt to contact another resolver. */ - singlesocket(multi, data); + rc = singlesocket(multi, data); + if(rc) + return rc; if(dns) { /* Perform the next step in the connection phase, and then move on @@ -2028,6 +2076,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_DO: + if(data->set.fprereq) { + int prereq_rc; + + /* call the prerequest callback function */ + Curl_set_in_callback(data, true); + prereq_rc = data->set.fprereq(data->set.prereq_userp, + data->info.conn_primary_ip, + data->info.conn_local_ip, + data->info.conn_primary_port, + data->info.conn_local_port); + Curl_set_in_callback(data, false); + if(prereq_rc != CURL_PREREQFUNC_OK) { + failf(data, "operation aborted by pre-request callback"); + /* failure in pre-request callback - don't do any other processing */ + result = CURLE_ABORTED_BY_CALLBACK; + Curl_posttransfer(data); + multi_done(data, result, FALSE); + stream_error = TRUE; + break; + } + } + if(data->set.connect_only) { /* keep connection open for application to use the socket */ connkeep(data->conn, "CONNECT_ONLY"); @@ -2595,7 +2665,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) *running_handles = multi->num_alive; if(CURLM_OK >= returncode) - Curl_update_timer(multi); + returncode = Curl_update_timer(multi); return returncode; } @@ -2611,7 +2681,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) multi->magic = 0; /* not good anymore */ - /* Firsrt remove all remaining easy handles */ + /* First remove all remaining easy handles */ data = multi->easyp; while(data) { nextdata = data->next; @@ -2640,7 +2710,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) /* Close all the connections in the connection cache */ Curl_conncache_close_all_connections(&multi->conn_cache); - Curl_hash_destroy(&multi->sockhash); + sockhash_destroy(&multi->sockhash); Curl_conncache_destroy(&multi->conn_cache); Curl_llist_destroy(&multi->msglist, NULL); Curl_llist_destroy(&multi->pending, NULL); @@ -2715,6 +2785,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, int num; unsigned int curraction; unsigned char actions[MAX_SOCKSPEREASYHANDLE]; + int rc; for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) socks[i] = CURL_SOCKET_BAD; @@ -2786,8 +2857,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* add 'data' to the transfer hash on this socket! */ if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ - sizeof(struct Curl_easy *), data)) + sizeof(struct Curl_easy *), data)) { + Curl_hash_destroy(&entry->transfers); return CURLM_OUT_OF_MEMORY; + } } comboaction = (entry->writers? CURL_POLL_OUT : 0) | @@ -2798,9 +2871,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* same, continue */ continue; - if(multi->socket_cb) - multi->socket_cb(data, s, comboaction, multi->socket_userp, - entry->socketp); + if(multi->socket_cb) { + rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, + entry->socketp); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + } entry->action = comboaction; /* store the current action state */ } @@ -2835,10 +2913,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi, if(oldactions & CURL_POLL_IN) entry->readers--; if(!entry->users) { - if(multi->socket_cb) - multi->socket_cb(data, s, CURL_POLL_REMOVE, - multi->socket_userp, - entry->socketp); + if(multi->socket_cb) { + rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, + multi->socket_userp, entry->socketp); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + } sh_delentry(entry, &multi->sockhash, s); } else { @@ -2857,9 +2939,11 @@ static CURLMcode singlesocket(struct Curl_multi *multi, return CURLM_OK; } -void Curl_updatesocket(struct Curl_easy *data) +CURLcode Curl_updatesocket(struct Curl_easy *data) { - singlesocket(data->multi, data); + if(singlesocket(data->multi, data)) + return CURLE_ABORTED_BY_CALLBACK; + return CURLE_OK; } @@ -2884,13 +2968,18 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); if(entry) { + int rc = 0; if(multi->socket_cb) - multi->socket_cb(data, s, CURL_POLL_REMOVE, - multi->socket_userp, - entry->socketp); + rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, + multi->socket_userp, entry->socketp); /* now remove it from the socket hash */ sh_delentry(entry, &multi->sockhash, s); + if(rc == -1) + /* This just marks the multi handle as "dead" without returning an + error code primarily because this function is used from many + places where propagating an error back is tricky. */ + multi->dead = TRUE; } } } @@ -3150,7 +3239,7 @@ CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, 0, running_handles); if(CURLM_OK >= result) - Curl_update_timer(multi); + result = Curl_update_timer(multi); return result; } @@ -3162,7 +3251,7 @@ CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); if(CURLM_OK >= result) - Curl_update_timer(multi); + result = Curl_update_timer(multi); return result; } @@ -3173,14 +3262,19 @@ CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) return CURLM_RECURSIVE_API_CALL; result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); if(CURLM_OK >= result) - Curl_update_timer(multi); + result = Curl_update_timer(multi); return result; } static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms) { - static struct curltime tv_zero = {0, 0}; + static const struct curltime tv_zero = {0, 0}; + + if(multi->dead) { + *timeout_ms = 0; + return CURLM_OK; + } if(multi->timetree) { /* we have a tree of expire times */ @@ -3233,14 +3327,15 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi, * Tell the application it should update its timers, if it subscribes to the * update timer callback. */ -void Curl_update_timer(struct Curl_multi *multi) +CURLMcode Curl_update_timer(struct Curl_multi *multi) { long timeout_ms; + int rc; - if(!multi->timer_cb) - return; + if(!multi->timer_cb || multi->dead) + return CURLM_OK; if(multi_timeout(multi, &timeout_ms)) { - return; + return CURLM_OK; } if(timeout_ms < 0) { static const struct curltime none = {0, 0}; @@ -3248,10 +3343,14 @@ void Curl_update_timer(struct Curl_multi *multi) multi->timer_lastcall = none; /* there's no timeout now but there was one previously, tell the app to disable it */ - multi->timer_cb(multi, -1, multi->timer_userp); - return; + rc = multi->timer_cb(multi, -1, multi->timer_userp); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + return CURLM_OK; } - return; + return CURLM_OK; } /* When multi_timeout() is done, multi->timetree points to the node with the @@ -3259,11 +3358,16 @@ void Curl_update_timer(struct Curl_multi *multi) * if this is the same (fixed) time as we got in a previous call and then * avoid calling the callback again. */ if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) - return; + return CURLM_OK; multi->timer_lastcall = multi->timetree->key; - multi->timer_cb(multi, timeout_ms, multi->timer_userp); + rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } + return CURLM_OK; } /* diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 2e4a6ff..db7f130 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -156,6 +156,8 @@ struct Curl_multi { #ifdef USE_OPENSSL bool ssl_seeded; #endif + bool dead; /* a callback returned error, everything needs to crash and + burn */ }; #endif /* HEADER_CURL_MULTIHANDLE_H */ diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index 2fbef53..f4d0ada 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h @@ -26,11 +26,11 @@ * Prototypes for library-wide functions provided by multi.c */ -void Curl_updatesocket(struct Curl_easy *data); +CURLcode Curl_updatesocket(struct Curl_easy *data); void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); void Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); -void Curl_update_timer(struct Curl_multi *multi); +CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connnection(struct Curl_easy *data, struct connectdata *conn); void Curl_detach_connnection(struct Curl_easy *data); diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index fb5e743..0ffb6a3 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -70,6 +70,17 @@ */ /* #define CURL_OPENLDAP_DEBUG */ +/* Machine states. */ +typedef enum { + OLDAP_STOP, /* Do nothing state, stops the state machine */ + OLDAP_SSL, /* Performing SSL handshake. */ + OLDAP_STARTTLS, /* STARTTLS request sent. */ + OLDAP_TLS, /* Performing TLS handshake. */ + OLDAP_BIND, /* Simple bind reply. */ + OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ + OLDAP_LAST /* Never used */ +} ldapstate; + #ifndef _LDAP_PVT_H extern int ldap_pvt_url_scheme2proto(const char *); extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, @@ -143,29 +154,13 @@ const struct Curl_handler Curl_handler_ldaps = { }; #endif -static const char *url_errs[] = { - "success", - "out of memory", - "bad parameter", - "unrecognized scheme", - "unbalanced delimiter", - "bad URL", - "bad host or port", - "bad or missing attributes", - "bad or missing scope", - "bad or missing filter", - "bad or missing extensions" -}; - struct ldapconninfo { - LDAP *ld; - Curl_recv *recv; /* for stacking SSL handler */ + LDAP *ld; /* Openldap connection handle. */ + Curl_recv *recv; /* For stacking SSL handler */ Curl_send *send; - int proto; - int msgid; - bool ssldone; - bool sslinst; - bool didbind; + ldapstate state; /* Current machine state. */ + int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ + int msgid; /* Current message id. */ }; struct ldapreqinfo { @@ -173,194 +168,379 @@ struct ldapreqinfo { int nument; }; -static CURLcode oldap_setup_connection(struct Curl_easy *data, - struct connectdata *conn) +/* + * state() + * + * This is the ONLY way to change LDAP state! + */ +static void state(struct Curl_easy *data, ldapstate newstate) { - struct ldapconninfo *li; - LDAPURLDesc *lud; - int rc, proto; - CURLcode status; + struct ldapconninfo *ldapc = data->conn->proto.ldapc; + +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ + static const char * const names[] = { + "STOP", + "SSL", + "STARTTLS", + "TLS", + "BIND", + "BINDV2", + /* LAST */ + }; + + if(ldapc->state != newstate) + infof(data, "LDAP %p state change from %s to %s", + (void *)ldapc, names[ldapc->state], names[newstate]); +#endif + + ldapc->state = newstate; +} - rc = ldap_url_parse(data->state.url, &lud); +/* Map some particular LDAP error codes to CURLcode values. */ +static CURLcode oldap_map_error(int rc, CURLcode result) +{ + switch(rc) { + case LDAP_NO_MEMORY: + result = CURLE_OUT_OF_MEMORY; + break; + case LDAP_INVALID_CREDENTIALS: + result = CURLE_LOGIN_DENIED; + break; + case LDAP_PROTOCOL_ERROR: + result = CURLE_UNSUPPORTED_PROTOCOL; + break; + case LDAP_INSUFFICIENT_ACCESS: + result = CURLE_REMOTE_ACCESS_DENIED; + break; + } + return result; +} + +static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) +{ + CURLcode result = CURLE_OK; + int rc = LDAP_URL_ERR_BADURL; + static const char * const url_errs[] = { + "success", + "out of memory", + "bad parameter", + "unrecognized scheme", + "unbalanced delimiter", + "bad URL", + "bad host or port", + "bad or missing attributes", + "bad or missing scope", + "bad or missing filter", + "bad or missing extensions" + }; + + *ludp = NULL; + if(!data->state.up.user && !data->state.up.password && + !data->state.up.options) + rc = ldap_url_parse(data->state.url, ludp); if(rc != LDAP_URL_SUCCESS) { const char *msg = "url parsing problem"; - status = CURLE_URL_MALFORMAT; - if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) { - if(rc == LDAP_URL_ERR_MEM) - status = CURLE_OUT_OF_MEMORY; + + result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT; + rc -= LDAP_URL_SUCCESS; + if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0])) msg = url_errs[rc]; - } failf(data, "LDAP local: %s", msg); - return status; } - proto = ldap_pvt_url_scheme2proto(lud->lud_scheme); + return result; +} + +static CURLcode oldap_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result; + LDAPURLDesc *lud; + struct ldapconninfo *li; + + /* Early URL syntax check. */ + result = oldap_url_parse(data, &lud); ldap_free_urldesc(lud); - li = calloc(1, sizeof(struct ldapconninfo)); - if(!li) - return CURLE_OUT_OF_MEMORY; - li->proto = proto; - conn->proto.ldapc = li; - connkeep(conn, "OpenLDAP default"); - return CURLE_OK; + if(!result) { + li = calloc(1, sizeof(struct ldapconninfo)); + if(!li) + result = CURLE_OUT_OF_MEMORY; + else { + li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); + conn->proto.ldapc = li; + connkeep(conn, "OpenLDAP default"); + + /* Clear the TLS upgraded flag */ + conn->bits.tls_upgraded = FALSE; + } + } + + return result; +} + +/* Starts LDAP simple bind. */ +static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + char *binddn = NULL; + struct berval passwd; + int rc; + + passwd.bv_val = NULL; + passwd.bv_len = 0; + + if(conn->bits.user_passwd) { + binddn = conn->user; + passwd.bv_val = conn->passwd; + passwd.bv_len = strlen(passwd.bv_val); + } + + rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, + NULL, NULL, &li->msgid); + if(rc == LDAP_SUCCESS) + state(data, newstate); + else + result = oldap_map_error(rc, + conn->bits.user_passwd? + CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND); + return result; } #ifdef USE_SSL static Sockbuf_IO ldapsb_tls; -#endif -static CURLcode oldap_connect(struct Curl_easy *data, bool *done) +static bool ssl_installed(struct connectdata *conn) +{ + return conn->proto.ldapc->recv != NULL; +} + +static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) { + CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct ldapconninfo *li = conn->proto.ldapc; - int rc, proto = LDAP_VERSION3; - char hosturl[1024]; - char *ptr; + bool ssldone = 0; - (void)done; + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, + FIRSTSOCKET, &ssldone); + if(!result) { + state(data, newstate); - strcpy(hosturl, "ldap"); - ptr = hosturl + 4; - if(conn->handler->flags & PROTOPT_SSL) - *ptr++ = 's'; - msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d", - conn->host.name, conn->remote_port); + if(ssldone) { + Sockbuf *sb; -#ifdef CURL_OPENLDAP_DEBUG - static int do_trace = 0; - const char *env = getenv("CURL_OPENLDAP_TRACE"); - do_trace = (env && strtol(env, NULL, 10) > 0); - if(do_trace) { - ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); + /* Install the libcurl SSL handlers into the sockbuf. */ + ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); + ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + li->recv = conn->recv[FIRSTSOCKET]; + li->send = conn->send[FIRSTSOCKET]; + } } + + return result; +} + +/* Send the STARTTLS request */ +static CURLcode oldap_perform_starttls(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct ldapconninfo *li = data->conn->proto.ldapc; + int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid); + + if(rc == LDAP_SUCCESS) + state(data, OLDAP_STARTTLS); + else + result = oldap_map_error(rc, CURLE_USE_SSL_FAILED); + return result; +} #endif +static CURLcode oldap_connect(struct Curl_easy *data, bool *done) +{ + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + static const int version = LDAP_VERSION3; + int rc; + char *hosturl; +#ifdef CURL_OPENLDAP_DEBUG + static int do_trace = -1; +#endif + + (void)done; + + hosturl = aprintf("ldap%s://%s:%d", + conn->handler->flags & PROTOPT_SSL? "s": "", + conn->host.name, conn->remote_port); + if(!hosturl) + return CURLE_OUT_OF_MEMORY; + rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); if(rc) { failf(data, "LDAP local: Cannot connect to %s, %s", hosturl, ldap_err2string(rc)); + free(hosturl); return CURLE_COULDNT_CONNECT; } - ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); + free(hosturl); + +#ifdef CURL_OPENLDAP_DEBUG + if(do_trace < 0) { + const char *env = getenv("CURL_OPENLDAP_TRACE"); + do_trace = (env && strtol(env, NULL, 10) > 0); + } + if(do_trace) + ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); +#endif + + /* Try version 3 first. */ + ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + + /* Do not chase referrals. */ + ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); #ifdef USE_SSL - if(conn->handler->flags & PROTOPT_SSL) { - CURLcode result; - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &li->ssldone); - if(result) + if(conn->handler->flags & PROTOPT_SSL) + return oldap_ssl_connect(data, OLDAP_SSL); + + if(data->set.use_ssl) { + CURLcode result = oldap_perform_starttls(data); + + if(!result || data->set.use_ssl != CURLUSESSL_TRY) return result; } #endif - return CURLE_OK; + /* Force bind even if anonymous bind is not needed in protocol version 3 + to detect missing version 3 support. */ + return oldap_perform_bind(data, OLDAP_BIND); } -static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) +/* Handle a simple bind response. */ +static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, + int code) { struct connectdata *conn = data->conn; struct ldapconninfo *li = conn->proto.ldapc; - LDAPMessage *msg = NULL; - struct timeval tv = {0, 1}, *tvp; - int rc, err; - char *info = NULL; + CURLcode result = CURLE_OK; + struct berval *bv = NULL; + int rc; -#ifdef USE_SSL - if(conn->handler->flags & PROTOPT_SSL) { - /* Is the SSL handshake complete yet? */ - if(!li->ssldone) { - CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, - &li->ssldone); - if(result || !li->ssldone) - return result; - } + if(code != LDAP_SUCCESS) + return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND); - /* Have we installed the libcurl SSL handlers into the sockbuf yet? */ - if(!li->sslinst) { - Sockbuf *sb; - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); - li->sslinst = TRUE; - li->recv = conn->recv[FIRSTSOCKET]; - li->send = conn->send[FIRSTSOCKET]; - } + rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s", + ldap_err2string(rc)); + result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); } -#endif + else + state(data, OLDAP_STOP); - tvp = &tv; - - retry: - if(!li->didbind) { - char *binddn; - struct berval passwd; + if(bv) + ber_bvfree(bv); + return result; +} - if(conn->bits.user_passwd) { - binddn = conn->user; - passwd.bv_val = conn->passwd; - passwd.bv_len = strlen(passwd.bv_val); +static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct ldapconninfo *li = conn->proto.ldapc; + LDAPMessage *msg = NULL; + struct timeval tv = {0, 0}; + int code = LDAP_SUCCESS; + int rc; + + if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) { + /* Get response to last command. */ + rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg); + if(!rc) + return CURLE_OK; /* Timed out. */ + if(rc < 0) { + failf(data, "LDAP local: connecting ldap_result %s", + ldap_err2string(rc)); + return oldap_map_error(rc, CURLE_COULDNT_CONNECT); } + + /* Get error code from message. */ + rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0); + if(rc) + code = rc; else { - binddn = NULL; - passwd.bv_val = NULL; - passwd.bv_len = 0; + /* store the latest code for later retrieval */ + data->info.httpcode = code; } - rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, - NULL, NULL, &li->msgid); - if(rc) - return CURLE_LDAP_CANNOT_BIND; - li->didbind = TRUE; - if(tvp) - return CURLE_OK; - } - rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg); - if(rc < 0) { - failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc)); - return CURLE_LDAP_CANNOT_BIND; - } - if(rc == 0) { - /* timed out */ - return CURLE_OK; - } + /* If protocol version 3 is not supported, fallback to version 2. */ + if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 +#ifdef USE_SSL + && (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) +#endif + ) { + static const int version = LDAP_VERSION2; - rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1); - if(rc) { - failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc)); - return CURLE_LDAP_CANNOT_BIND; + ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); + ldap_msgfree(msg); + return oldap_perform_bind(data, OLDAP_BINDV2); + } } - /* Try to fallback to LDAPv2? */ - if(err == LDAP_PROTOCOL_ERROR) { - int proto; - ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); - if(proto == LDAP_VERSION3) { - if(info) { - ldap_memfree(info); - info = NULL; + /* Handle response message according to current state. */ + switch(li->state) { + +#ifdef USE_SSL + case OLDAP_SSL: + result = oldap_ssl_connect(data, OLDAP_SSL); + if(!result && ssl_installed(conn)) + result = oldap_perform_bind(data, OLDAP_BIND); + break; + case OLDAP_STARTTLS: + if(code != LDAP_SUCCESS) { + if(data->set.use_ssl != CURLUSESSL_TRY) + result = oldap_map_error(code, CURLE_USE_SSL_FAILED); + else + result = oldap_perform_bind(data, OLDAP_BIND); + break; + } + /* FALLTHROUGH */ + case OLDAP_TLS: + result = oldap_ssl_connect(data, OLDAP_TLS); + if(result && data->set.use_ssl != CURLUSESSL_TRY) + result = oldap_map_error(code, CURLE_USE_SSL_FAILED); + else if(ssl_installed(conn)) { + conn->bits.tls_upgraded = TRUE; + if(conn->bits.user_passwd) + result = oldap_perform_bind(data, OLDAP_BIND); + else { + state(data, OLDAP_STOP); /* Version 3 supported: no bind required */ + result = CURLE_OK; } - proto = LDAP_VERSION2; - ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); - li->didbind = FALSE; - goto retry; } - } + break; +#endif - if(err) { - failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc), - info ? info : ""); - if(info) - ldap_memfree(info); - return CURLE_LOGIN_DENIED; + case OLDAP_BIND: + case OLDAP_BINDV2: + result = oldap_state_bind_resp(data, msg, code); + break; + default: + /* internal error */ + result = CURLE_COULDNT_CONNECT; + break; } - if(info) - ldap_memfree(info); - conn->recv[FIRSTSOCKET] = oldap_recv; - *done = TRUE; + ldap_msgfree(msg); - return CURLE_OK; + *done = li->state == OLDAP_STOP; + if(*done) + conn->recv[FIRSTSOCKET] = oldap_recv; + + return result; } static CURLcode oldap_disconnect(struct Curl_easy *data, @@ -373,7 +553,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, if(li) { if(li->ld) { #ifdef USE_SSL - if(conn->ssl[FIRSTSOCKET].use) { + if(ssl_installed(conn)) { Sockbuf *sb; ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); @@ -393,44 +573,40 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct ldapconninfo *li = conn->proto.ldapc; struct ldapreqinfo *lr; - CURLcode status = CURLE_OK; - int rc = 0; - LDAPURLDesc *ludp = NULL; + CURLcode result; + int rc; + LDAPURLDesc *lud; int msgid; connkeep(conn, "OpenLDAP do"); infof(data, "LDAP local: %s", data->state.url); - rc = ldap_url_parse(data->state.url, &ludp); - if(rc != LDAP_URL_SUCCESS) { - const char *msg = "url parsing problem"; - status = CURLE_URL_MALFORMAT; - if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) { - if(rc == LDAP_URL_ERR_MEM) - status = CURLE_OUT_OF_MEMORY; - msg = url_errs[rc]; + result = oldap_url_parse(data, &lud); + if(!result) { + rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, + lud->lud_filter, lud->lud_attrs, 0, + NULL, NULL, NULL, 0, &msgid); + ldap_free_urldesc(lud); + if(rc != LDAP_SUCCESS) { + failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); + result = CURLE_LDAP_SEARCH_FAILED; + } + else { + lr = calloc(1, sizeof(struct ldapreqinfo)); + if(!lr) { + ldap_abandon_ext(li->ld, msgid, NULL, NULL); + result = CURLE_OUT_OF_MEMORY; + } + else { + lr->msgid = msgid; + data->req.p.ldap = lr; + Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + *done = TRUE; + } } - failf(data, "LDAP local: %s", msg); - return status; - } - - rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, 0, - NULL, NULL, NULL, 0, &msgid); - ldap_free_urldesc(ludp); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); - return CURLE_LDAP_SEARCH_FAILED; } - lr = calloc(1, sizeof(struct ldapreqinfo)); - if(!lr) - return CURLE_OUT_OF_MEMORY; - lr->msgid = msgid; - data->req.p.ldap = lr; - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - *done = TRUE; - return CURLE_OK; + return result; } static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, @@ -456,163 +632,146 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, return CURLE_OK; } +static CURLcode client_write(struct Curl_easy *data, const char *prefix, + const char *value, size_t len, const char *suffix) +{ + CURLcode result = CURLE_OK; + size_t l; + + if(prefix) { + l = strlen(prefix); + /* If we have a zero-length value and the prefix ends with a space + separator, drop the latter. */ + if(!len && l && prefix[l - 1] == ' ') + l--; + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, l); + if(!result) + data->req.bytecount += l; + } + if(!result && value) { + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); + if(!result) + data->req.bytecount += len; + } + if(!result && suffix) { + l = strlen(suffix); + result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, l); + if(!result) + data->req.bytecount += l; + } + return result; +} + static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, CURLcode *err) { struct connectdata *conn = data->conn; struct ldapconninfo *li = conn->proto.ldapc; struct ldapreqinfo *lr = data->req.p.ldap; - int rc, ret; + int rc; LDAPMessage *msg = NULL; - LDAPMessage *ent; BerElement *ber = NULL; - struct timeval tv = {0, 1}; + struct timeval tv = {0, 0}; + struct berval bv, *bvals; + int binary = 0; + CURLcode result = CURLE_AGAIN; + int code; + char *info = NULL; (void)len; (void)buf; (void)sockindex; - rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg); + rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg); if(rc < 0) { failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); - *err = CURLE_RECV_ERROR; - return -1; + result = CURLE_RECV_ERROR; } - *err = CURLE_AGAIN; - ret = -1; + *err = result; - /* timed out */ + /* error or timed out */ if(!msg) - return ret; - - for(ent = ldap_first_message(li->ld, msg); ent; - ent = ldap_next_message(li->ld, ent)) { - struct berval bv, *bvals; - int binary = 0, msgtype; - CURLcode writeerr; - - msgtype = ldap_msgtype(ent); - if(msgtype == LDAP_RES_SEARCH_RESULT) { - int code; - char *info = NULL; - rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0); - if(rc) { - failf(data, "LDAP local: search ldap_parse_result %s", - ldap_err2string(rc)); - *err = CURLE_LDAP_SEARCH_FAILED; - } - else if(code && code != LDAP_SIZELIMIT_EXCEEDED) { - failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc), - info ? info : ""); - *err = CURLE_LDAP_SEARCH_FAILED; - } - else { - /* successful */ - if(code == LDAP_SIZELIMIT_EXCEEDED) - infof(data, "There are more than %d entries", lr->nument); - data->req.size = data->req.bytecount; - *err = CURLE_OK; - ret = 0; - } - lr->msgid = 0; - ldap_memfree(info); + return -1; + + result = CURLE_OK; + + switch(ldap_msgtype(msg)) { + case LDAP_RES_SEARCH_RESULT: + lr->msgid = 0; + rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0); + if(rc) { + failf(data, "LDAP local: search ldap_parse_result %s", + ldap_err2string(rc)); + result = CURLE_LDAP_SEARCH_FAILED; break; } - else if(msgtype != LDAP_RES_SEARCH_ENTRY) - continue; + /* store the latest code for later retrieval */ + data->info.httpcode = code; + + switch(code) { + case LDAP_SIZELIMIT_EXCEEDED: + infof(data, "There are more than %d entries", lr->nument); + /* FALLTHROUGH */ + case LDAP_SUCCESS: + data->req.size = data->req.bytecount; + break; + default: + failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code), + info ? info : ""); + result = CURLE_LDAP_SEARCH_FAILED; + break; + } + if(info) + ldap_memfree(info); + break; + case LDAP_RES_SEARCH_ENTRY: lr->nument++; - rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv); + rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); if(rc < 0) { - *err = CURLE_RECV_ERROR; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4); - if(writeerr) { - *err = writeerr; - return -1; - } - - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val, - bv.bv_len); - if(writeerr) { - *err = writeerr; - return -1; + result = CURLE_RECV_ERROR; + break; } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - data->req.bytecount += bv.bv_len + 5; + result = client_write(data, "DN: ", bv.bv_val, bv.bv_len, "\n"); + if(result) + break; - for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals); + for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); rc == LDAP_SUCCESS; - rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals)) { + rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { int i; if(!bv.bv_val) break; - if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7)) - binary = 1; - else - binary = 0; - if(!bvals) { - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val, - bv.bv_len); - if(writeerr) { - *err = writeerr; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)":\n", 2); - if(writeerr) { - *err = writeerr; - return -1; - } - data->req.bytecount += bv.bv_len + 3; + result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":\n"); + if(result) + break; continue; } + binary = bv.bv_len > 7 && + !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7); + for(i = 0; bvals[i].bv_val != NULL; i++) { int binval = 0; - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val, - bv.bv_len); - if(writeerr) { - *err = writeerr; - return -1; - } - - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)":", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - data->req.bytecount += bv.bv_len + 2; + result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":"); + if(result) + break; if(!binary) { /* check for leading or trailing whitespace */ if(ISSPACE(bvals[i].bv_val[0]) || - ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1])) + ISSPACE(bvals[i].bv_val[bvals[i].bv_len - 1])) binval = 1; else { /* check for unprintable characters */ unsigned int j; - for(j = 0; j<bvals[i].bv_len; j++) + for(j = 0; j < bvals[i].bv_len; j++) if(!ISPRINT(bvals[i].bv_val[j])) { binval = 1; break; @@ -622,80 +781,42 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, if(binary || binval) { char *val_b64 = NULL; size_t val_b64_sz = 0; - /* Binary value, encode to base64. */ - CURLcode error = Curl_base64_encode(data, - bvals[i].bv_val, - bvals[i].bv_len, - &val_b64, - &val_b64_sz); - if(error) { - ber_memfree(bvals); - ber_free(ber, 0); - ldap_msgfree(msg); - *err = error; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)": ", 2); - if(writeerr) { - *err = writeerr; - return -1; - } - - data->req.bytecount += 2; - if(val_b64_sz > 0) { - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, - val_b64_sz); - if(writeerr) { - *err = writeerr; - return -1; - } - free(val_b64); - data->req.bytecount += val_b64_sz; - } - } - else { - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)" ", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, bvals[i].bv_val, - bvals[i].bv_len); - if(writeerr) { - *err = writeerr; - return -1; - } - - data->req.bytecount += bvals[i].bv_len + 1; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(writeerr) { - *err = writeerr; - return -1; + /* Binary value, encode to base64. */ + if(bvals[i].bv_len) + result = Curl_base64_encode(data, bvals[i].bv_val, bvals[i].bv_len, + &val_b64, &val_b64_sz); + if(!result) + result = client_write(data, ": ", val_b64, val_b64_sz, "\n"); + free(val_b64); } - - data->req.bytecount++; + else + result = client_write(data, " ", + bvals[i].bv_val, bvals[i].bv_len, "\n"); + if(result) + break; } + ber_memfree(bvals); - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(writeerr) { - *err = writeerr; - return -1; - } - data->req.bytecount++; - } - writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(writeerr) { - *err = writeerr; - return -1; + bvals = NULL; + if(!result) + result = client_write(data, "\n", NULL, 0, NULL); + if(result) + break; } - data->req.bytecount++; + ber_free(ber, 0); + + if(!result) + result = client_write(data, "\n", NULL, 0, NULL); + if(!result) + result = CURLE_AGAIN; + break; } + ldap_msgfree(msg); - return ret; + *err = result; + return result? -1: 0; } #ifdef USE_SSL diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index d3f3de6..d4ca678 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -78,6 +78,7 @@ #include "select.h" #include "multiif.h" #include "url.h" +#include "bufref.h" #include "curl_sasl.h" #include "curl_md5.h" #include "warnless.h" @@ -103,12 +104,12 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data, static CURLcode pop3_parse_url_options(struct connectdata *conn); static CURLcode pop3_parse_url_path(struct Curl_easy *data); static CURLcode pop3_parse_custom_request(struct Curl_easy *data); -static CURLcode pop3_perform_auth(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp); -static CURLcode pop3_continue_auth(struct Curl_easy *data, - struct connectdata *conn, const char *resp); -static void pop3_get_message(char *buffer, char **outptr); +static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); /* * POP3 protocol handler. @@ -170,13 +171,16 @@ const struct Curl_handler Curl_handler_pop3s = { /* SASL parameters for the pop3 protocol */ static const struct SASLproto saslpop3 = { - "pop", /* The service name */ - '*', /* Code received when continuation is expected */ - '+', /* Code to receive upon authentication success */ - 255 - 8, /* Maximum initial response length (no max) */ - pop3_perform_auth, /* Send authentication command */ - pop3_continue_auth, /* Send authentication continuation */ - pop3_get_message /* Get SASL response message */ + "pop", /* The service name */ + pop3_perform_auth, /* Send authentication command */ + pop3_continue_auth, /* Send authentication continuation */ + pop3_cancel_auth, /* Send authentication cancellation */ + pop3_get_message, /* Get SASL response message */ + 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + '*', /* Code received when continuation is expected */ + '+', /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ }; #ifdef USE_SSL @@ -250,34 +254,32 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn, * * Gets the authentication message from the response buffer. */ -static void pop3_get_message(char *buffer, char **outptr) +static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) { - size_t len = strlen(buffer); - char *message = NULL; + char *message = data->state.buffer; + size_t len = strlen(message); if(len > 2) { /* Find the start of the message */ len -= 2; - for(message = buffer + 2; *message == ' ' || *message == '\t'; - message++, len--) + for(message += 2; *message == ' ' || *message == '\t'; message++, len--) ; /* Find the end of the message */ - for(; len--;) + while(len--) if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && message[len] != '\t') break; /* Terminate the message */ - if(++len) { - message[len] = '\0'; - } + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); } else /* junk input => zero length output */ - message = &buffer[len]; + Curl_bufref_set(out, "", 0, NULL); - *outptr = message; + return CURLE_OK; } /*********************************************************************** @@ -474,16 +476,16 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, * authentication mechanism. */ static CURLcode pop3_perform_auth(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp) + const struct bufref *initresp) { CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + const char *ir = (const char *) Curl_bufref_ptr(initresp); - if(initresp) { /* AUTH <mech> ...<crlf> */ + if(ir) { /* AUTH <mech> ...<crlf> */ /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, initresp); + result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir); } else { /* Send the AUTH command */ @@ -497,15 +499,33 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data, * * pop3_continue_auth() * - * Sends SASL continuation data or cancellation. + * Sends SASL continuation data. */ static CURLcode pop3_continue_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *resp) + const char *mech, + const struct bufref *resp) { - struct pop3_conn *pop3c = &conn->proto.pop3c; + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + + (void)mech; - return Curl_pp_sendf(data, &pop3c->pp, "%s", resp); + return Curl_pp_sendf(data, &pop3c->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * pop3_cancel_auth() + * + * Sends SASL cancellation. + */ +static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct pop3_conn *pop3c = &data->conn->proto.pop3c; + + (void)mech; + + return Curl_pp_sendf(data, &pop3c->pp, "*"); } /*********************************************************************** @@ -532,7 +552,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) { /* Calculate the SASL login details */ - result = Curl_sasl_start(&pop3c->sasl, data, conn, FALSE, &progress); + result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress); if(!result) if(progress == SASL_INPROGRESS) @@ -801,7 +821,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ - result = Curl_sasl_continue(&pop3c->sasl, data, conn, pop3code, &progress); + result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress); if(!result) switch(progress) { case SASL_DONE: @@ -1011,7 +1031,9 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, break; case POP3_QUIT: - /* fallthrough, just stop! */ + state(data, POP3_STOP); + break; + default: /* internal error */ state(data, POP3_STOP); @@ -1102,7 +1124,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) /* Set the default preferred authentication type and mechanism */ pop3c->preftype = POP3_TYPE_ANY; - Curl_sasl_init(&pop3c->sasl, &saslpop3); + Curl_sasl_init(&pop3c->sasl, data, &saslpop3); /* Initialise the pingpong layer */ Curl_pp_setup(pp); @@ -1343,8 +1365,6 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) struct pop3_conn *pop3c = &conn->proto.pop3c; const char *ptr = conn->options; - pop3c->sasl.resetprefs = TRUE; - while(!result && ptr && *ptr) { const char *key = ptr; const char *value; diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index 84d0b8f..5f31870 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -64,7 +64,7 @@ * Waiting indefinitely with this function is not allowed, a * zero or negative timeout value will return immediately. * Timeout resolution, accuracy, as well as maximum supported - * value is system dependent, neither factor is a citical issue + * value is system dependent, neither factor is a critical issue * for the intended use of this function in the library. * * Return values: diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 14ca84b..bcfa27a 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -608,7 +608,7 @@ static CURLcode chop_write(struct Curl_easy *data, /* Curl_client_write() sends data to the write callback(s) The bit pattern defines to what "streams" to write to. Body and/or header. - The defines are in sendf.h of course. "len" is not allowed to be 0. + The defines are in sendf.h of course. If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the local character encoding. This is a problem and should be changed in @@ -621,8 +621,10 @@ CURLcode Curl_client_write(struct Curl_easy *data, { struct connectdata *conn = data->conn; - DEBUGASSERT(len); - DEBUGASSERT(type <= 3); + DEBUGASSERT(!(type & ~CLIENTWRITE_BOTH)); + + if(!len) + return CURLE_OK; /* FTP data may need conversion. */ if((type & CLIENTWRITE_BODY) && diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 08827d1..599ed5d 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -1870,6 +1870,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssl.primary.verifypeer; } break; +#ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYPEER: /* * Enable peer SSL verifying for DoH. @@ -1877,6 +1878,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.doh_verifypeer = (0 != va_arg(param, long)) ? TRUE : FALSE; break; +#endif #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_VERIFYPEER: /* @@ -1909,6 +1911,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssl.primary.verifyhost; } break; +#ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYHOST: /* * Enable verification of the host name in the peer certificate for DoH @@ -1918,6 +1921,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Treat both 1 and 2 as TRUE */ data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE); break; +#endif #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_VERIFYHOST: /* @@ -1953,6 +1957,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssl.primary.verifystatus; } break; +#ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYSTATUS: /* * Enable certificate status verifying for DoH. @@ -1965,6 +1970,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.doh_verifystatus = (0 != va_arg(param, long)) ? TRUE : FALSE; break; +#endif case CURLOPT_SSL_CTX_FUNCTION: /* * Set a SSL_CTX callback @@ -2477,6 +2483,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: + /* + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], + va_arg(param, char *)); + break; + case CURLOPT_SSH_KNOWNHOSTS: /* * Store the file name to read known hosts from. @@ -2507,8 +2522,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * disable libcurl transfer encoding is used */ +#ifndef USE_HYPER data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; break; +#else + return CURLE_NOT_BUILT_IN; /* hyper doesn't support */ +#endif case CURLOPT_HTTP_CONTENT_DECODING: /* @@ -2596,6 +2615,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; #endif +#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \ + !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP) + case CURLOPT_MIME_OPTIONS: + data->set.mime_options = va_arg(param, long); + break; +#endif + case CURLOPT_SASL_AUTHZID: /* Authorisation identity (identity to act as) */ result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], @@ -2929,6 +2955,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.maxage_conn = arg; break; + case CURLOPT_MAXLIFETIME_CONN: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxlifetime_conn = arg; + break; case CURLOPT_TRAILERFUNCTION: #ifndef CURL_DISABLE_HTTP data->set.trailer_callback = va_arg(param, curl_trailer_callback); @@ -3004,6 +3036,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return result; break; #endif + case CURLOPT_PREREQFUNCTION: + data->set.fprereq = va_arg(param, curl_prereq_callback); + break; + case CURLOPT_PREREQDATA: + data->set.prereq_userp = va_arg(param, void *); + break; default: /* unknown tag and its companion, just ignore: */ result = CURLE_UNKNOWN_OPTION; diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index c35dec8..fa8742f 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -25,11 +25,11 @@ /* * Include header files for windows builds before redefining anything. * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs + * winsock2.h or ws2tcpip.h. Any other windows thing belongs * to any other further and independent block. Under Cygwin things work * just as under linux (e.g. <sys/socket.h>) and the winsock headers should * never be included when __CYGWIN__ is defined. configure script takes - * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H, + * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H, * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined. */ @@ -47,10 +47,6 @@ # ifdef HAVE_WS2TCPIP_H # include <ws2tcpip.h> # endif -# else -# ifdef HAVE_WINSOCK_H -# include <winsock.h> -# endif # endif # include <tchar.h> # ifdef UNICODE @@ -67,10 +63,6 @@ #ifdef HAVE_WINSOCK2_H # define USE_WINSOCK 2 -#else -# ifdef HAVE_WINSOCK_H -# error "WinSock version 1 is no longer supported, version 2 is required!" -# endif #endif /* diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index a2e7e41..cf7ea4f 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c @@ -29,11 +29,18 @@ #include "curl_sha256.h" #include "curl_hmac.h" +#ifdef USE_WOLFSSL +#include <wolfssl/options.h> +#ifndef NO_SHA256 +#define USE_OPENSSL_SHA256 +#endif +#endif + #if defined(USE_OPENSSL) #include <openssl/opensslv.h> -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) +#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) #define USE_OPENSSL_SHA256 #endif @@ -63,7 +70,40 @@ #if defined(USE_OPENSSL_SHA256) /* When OpenSSL is available we use the SHA256-function from OpenSSL */ -#include <openssl/sha.h> +#include <openssl/evp.h> + +#include "curl_memory.h" + +/* The last #include file should be: */ +#include "memdebug.h" + +struct sha256_ctx { + EVP_MD_CTX *openssl_ctx; +}; +typedef struct sha256_ctx my_sha256_ctx; + +static CURLcode my_sha256_init(my_sha256_ctx *ctx) +{ + ctx->openssl_ctx = EVP_MD_CTX_create(); + if(!ctx->openssl_ctx) + return CURLE_OUT_OF_MEMORY; + + EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL); + return CURLE_OK; +} + +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) +{ + EVP_DigestUpdate(ctx->openssl_ctx, data, length); +} + +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +{ + EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL); + EVP_MD_CTX_destroy(ctx->openssl_ctx); +} #elif defined(USE_GNUTLS) @@ -74,21 +114,22 @@ /* The last #include file should be: */ #include "memdebug.h" -typedef struct sha256_ctx SHA256_CTX; +typedef struct sha256_ctx my_sha256_ctx; -static void SHA256_Init(SHA256_CTX *ctx) +static CURLcode my_sha256_init(my_sha256_ctx *ctx) { sha256_init(ctx); + return CURLE_OK; } -static void SHA256_Update(SHA256_CTX *ctx, - const unsigned char *data, - unsigned int length) +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) { sha256_update(ctx, length, data); } -static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) { sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); } @@ -102,20 +143,21 @@ static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) /* The last #include file should be: */ #include "memdebug.h" -typedef mbedtls_sha256_context SHA256_CTX; +typedef mbedtls_sha256_context my_sha256_ctx; -static void SHA256_Init(SHA256_CTX *ctx) +static CURLcode my_sha256_init(my_sha256_ctx *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_sha256_starts(ctx, 0); #else (void) mbedtls_sha256_starts_ret(ctx, 0); #endif + return CURLE_OK; } -static void SHA256_Update(SHA256_CTX *ctx, - const unsigned char *data, - unsigned int length) +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_sha256_update(ctx, data, length); @@ -124,7 +166,7 @@ static void SHA256_Update(SHA256_CTX *ctx, #endif } -static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_sha256_finish(ctx, digest); @@ -145,21 +187,22 @@ static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) /* The last #include file should be: */ #include "memdebug.h" -typedef CC_SHA256_CTX SHA256_CTX; +typedef CC_SHA256_CTX my_sha256_ctx; -static void SHA256_Init(SHA256_CTX *ctx) +static CURLcode my_sha256_init(my_sha256_ctx *ctx) { (void) CC_SHA256_Init(ctx); + return CURLE_OK; } -static void SHA256_Update(SHA256_CTX *ctx, - const unsigned char *data, - unsigned int length) +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) { (void) CC_SHA256_Update(ctx, data, length); } -static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) { (void) CC_SHA256_Final(digest, ctx); } @@ -172,28 +215,30 @@ struct sha256_ctx { HCRYPTPROV hCryptProv; HCRYPTHASH hHash; }; -typedef struct sha256_ctx SHA256_CTX; +typedef struct sha256_ctx my_sha256_ctx; #if !defined(CALG_SHA_256) #define CALG_SHA_256 0x0000800c #endif -static void SHA256_Init(SHA256_CTX *ctx) +static CURLcode my_sha256_init(my_sha256_ctx *ctx) { if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); } + + return CURLE_OK; } -static void SHA256_Update(SHA256_CTX *ctx, - const unsigned char *data, - unsigned int length) +static void my_sha256_update(my_sha256_ctx *ctx, + const unsigned char *data, + unsigned int length) { CryptHashData(ctx->hHash, (unsigned char *) data, length, 0); } -static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx) +static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) { unsigned long length = 0; @@ -262,7 +307,7 @@ struct sha256_state { unsigned long state[8], curlen; unsigned char buf[64]; }; -typedef struct sha256_state SHA256_CTX; +typedef struct sha256_state my_sha256_ctx; /* The K array */ static const unsigned long K[64] = { @@ -339,7 +384,7 @@ static int sha256_compress(struct sha256_state *md, } /* Initialize the hash state */ -static void SHA256_Init(struct sha256_state *md) +static CURLcode my_sha256_init(struct sha256_state *md) { md->curlen = 0; md->length = 0; @@ -351,6 +396,8 @@ static void SHA256_Init(struct sha256_state *md) md->state[5] = 0x9B05688CUL; md->state[6] = 0x1F83D9ABUL; md->state[7] = 0x5BE0CD19UL; + + return CURLE_OK; } /* @@ -358,11 +405,11 @@ static void SHA256_Init(struct sha256_state *md) @param md The hash state @param in The data to hash @param inlen The length of the data (octets) - @return CRYPT_OK if successful + @return 0 if successful */ -static int SHA256_Update(struct sha256_state *md, - const unsigned char *in, - unsigned long inlen) +static int my_sha256_update(struct sha256_state *md, + const unsigned char *in, + unsigned long inlen) { unsigned long n; @@ -399,10 +446,10 @@ static int SHA256_Update(struct sha256_state *md, Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (32 bytes) - @return CRYPT_OK if successful + @return 0 if successful */ -static int SHA256_Final(unsigned char *out, - struct sha256_state *md) +static int my_sha256_final(unsigned char *out, + struct sha256_state *md) { int i; @@ -455,28 +502,34 @@ static int SHA256_Final(unsigned char *out, * output [in/out] - The output buffer. * input [in] - The input data. * length [in] - The input length. + * + * Returns CURLE_OK on success. */ -void Curl_sha256it(unsigned char *output, const unsigned char *input, +CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, const size_t length) { - SHA256_CTX ctx; + CURLcode result; + my_sha256_ctx ctx; - SHA256_Init(&ctx); - SHA256_Update(&ctx, input, curlx_uztoui(length)); - SHA256_Final(output, &ctx); + result = my_sha256_init(&ctx); + if(!result) { + my_sha256_update(&ctx, input, curlx_uztoui(length)); + my_sha256_final(output, &ctx); + } + return result; } const struct HMAC_params Curl_HMAC_SHA256[] = { { /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, SHA256_Init), + CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init), /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, SHA256_Update), + CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update), /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, SHA256_Final), + CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final), /* Size of hash context structure. */ - sizeof(SHA256_CTX), + sizeof(my_sha256_ctx), /* Maximum key length. */ 64, /* Result size. */ diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c index 9c43c8f..403563f 100644 --- a/Utilities/cmcurl/lib/share.c +++ b/Utilities/cmcurl/lib/share.c @@ -39,11 +39,7 @@ curl_share_init(void) if(share) { share->magic = CURL_GOOD_SHARE; share->specifier |= (1<<CURL_LOCK_DATA_SHARE); - - if(Curl_mk_dnscache(&share->hostcache)) { - free(share); - return NULL; - } + Curl_init_dnscache(&share->hostcache); } return share; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 02ddaca..6c08293 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -82,6 +82,7 @@ #include "multiif.h" #include "url.h" #include "curl_gethostname.h" +#include "bufref.h" #include "curl_sasl.h" #include "warnless.h" /* The last 3 #include files should be in this order */ @@ -108,12 +109,12 @@ static CURLcode smtp_parse_url_path(struct Curl_easy *data); static CURLcode smtp_parse_custom_request(struct Curl_easy *data); static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, char **address, struct hostname *host); -static CURLcode smtp_perform_auth(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp); -static CURLcode smtp_continue_auth(struct Curl_easy *data, - struct connectdata *conn, const char *resp); -static void smtp_get_message(char *buffer, char **outptr); +static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, + const struct bufref *initresp); +static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, + const struct bufref *resp); +static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); +static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); /* * SMTP protocol handler. @@ -175,13 +176,16 @@ const struct Curl_handler Curl_handler_smtps = { /* SASL parameters for the smtp protocol */ static const struct SASLproto saslsmtp = { - "smtp", /* The service name */ - 334, /* Code received when continuation is expected */ - 235, /* Code to receive upon authentication success */ - 512 - 8, /* Maximum initial response length (no max) */ - smtp_perform_auth, /* Send authentication command */ - smtp_continue_auth, /* Send authentication continuation */ - smtp_get_message /* Get SASL response message */ + "smtp", /* The service name */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_cancel_auth, /* Cancel authentication */ + smtp_get_message, /* Get SASL response message */ + 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + SASL_AUTH_DEFAULT, /* Default mechanisms */ + SASL_FLAG_BASE64 /* Configuration flags */ }; #ifdef USE_SSL @@ -218,7 +222,7 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, /* Do we have a command response? This should be the response code followed by a space and optionally some text as per RFC-5321 and as outlined in - Section 4. Examples of RFC-4954 but some e-mail servers ignore this and + Section 4. Examples of RFC-4954 but some email servers ignore this and only send the response code instead as per Section 4.2. */ if(line[3] == ' ' || len == 5) { char tmpline[6]; @@ -248,34 +252,32 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, * * Gets the authentication message from the response buffer. */ -static void smtp_get_message(char *buffer, char **outptr) +static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) { - size_t len = strlen(buffer); - char *message = NULL; + char *message = data->state.buffer; + size_t len = strlen(message); if(len > 4) { /* Find the start of the message */ len -= 4; - for(message = buffer + 4; *message == ' ' || *message == '\t'; - message++, len--) + for(message += 4; *message == ' ' || *message == '\t'; message++, len--) ; /* Find the end of the message */ - for(; len--;) + while(len--) if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && message[len] != '\t') break; /* Terminate the message */ - if(++len) { - message[len] = '\0'; - } + message[++len] = '\0'; + Curl_bufref_set(out, message, len, NULL); } else /* junk input => zero length output */ - message = &buffer[len]; + Curl_bufref_set(out, "", 0, NULL); - *outptr = message; + return CURLE_OK; } /*********************************************************************** @@ -421,16 +423,16 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) * authentication mechanism. */ static CURLcode smtp_perform_auth(struct Curl_easy *data, - struct connectdata *conn, const char *mech, - const char *initresp) + const struct bufref *initresp) { CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; + struct smtp_conn *smtpc = &data->conn->proto.smtpc; + const char *ir = (const char *) Curl_bufref_ptr(initresp); - if(initresp) { /* AUTH <mech> ...<crlf> */ + if(ir) { /* AUTH <mech> ...<crlf> */ /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, initresp); + result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir); } else { /* Send the AUTH command */ @@ -444,14 +446,33 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data, * * smtp_continue_auth() * - * Sends SASL continuation data or cancellation. + * Sends SASL continuation data. */ static CURLcode smtp_continue_auth(struct Curl_easy *data, - struct connectdata *conn, const char *resp) + const char *mech, + const struct bufref *resp) { - struct smtp_conn *smtpc = &conn->proto.smtpc; + struct smtp_conn *smtpc = &data->conn->proto.smtpc; + + (void)mech; + + return Curl_pp_sendf(data, &smtpc->pp, + "%s", (const char *) Curl_bufref_ptr(resp)); +} + +/*********************************************************************** + * + * smtp_cancel_auth() + * + * Sends SASL cancellation. + */ +static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech) +{ + struct smtp_conn *smtpc = &data->conn->proto.smtpc; - return Curl_pp_sendf(data, &smtpc->pp, "%s", resp); + (void)mech; + + return Curl_pp_sendf(data, &smtpc->pp, "*"); } /*********************************************************************** @@ -469,7 +490,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) saslprogress progress; /* Check we have enough data to authenticate with, and the - server supports authentiation, and end the connect phase if not */ + server supports authentication, and end the connect phase if not */ if(!smtpc->auth_supported || !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { state(data, SMTP_STOP); @@ -477,7 +498,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data) } /* Calculate the SASL login details */ - result = Curl_sasl_start(&smtpc->sasl, data, conn, FALSE, &progress); + result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress); if(!result) { if(progress == SASL_INPROGRESS) @@ -506,7 +527,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) if(smtp->rcpt) { /* We notify the server we are sending UTF-8 data if a) it supports the - SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in + SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in either the local address or host name parts. This is regardless of whether the host name is encoded using IDN ACE */ bool utf8 = FALSE; @@ -579,7 +600,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) struct connectdata *conn = data->conn; /* We notify the server we are sending UTF-8 data if a) it supports the - SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in + SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in either the local address or host name parts. This is regardless of whether the host name is encoded using IDN ACE */ bool utf8 = FALSE; @@ -985,7 +1006,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ - result = Curl_sasl_continue(&smtpc->sasl, data, conn, smtpcode, &progress); + result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress); if(!result) switch(progress) { case SASL_DONE: @@ -1333,7 +1354,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp); /* Initialize the SASL storage */ - Curl_sasl_init(&smtpc->sasl, &saslsmtp); + Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); /* Initialise the pingpong layer */ Curl_pp_setup(pp); @@ -1655,8 +1676,6 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn) struct smtp_conn *smtpc = &conn->proto.smtpc; const char *ptr = conn->options; - smtpc->sasl.resetprefs = TRUE; - while(!result && ptr && *ptr) { const char *key = ptr; const char *value; diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index db4c808..a014aa6 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -38,6 +38,7 @@ #include "timeval.h" #include "socks.h" #include "multiif.h" /* for getsock macros */ +#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -856,10 +857,32 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user, socksreq[len++] = 0; /* must be zero */ if(!socks5_resolve_local) { - socksreq[len++] = 3; /* ATYP: domain name = 3 */ - socksreq[len++] = (char) hostname_len; /* one byte address length */ - memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ - len += hostname_len; + /* ATYP: domain name = 3, + IPv6 == 4, + IPv4 == 1 */ + unsigned char ip4[4]; +#ifdef ENABLE_IPV6 + if(conn->bits.ipv6_ip) { + char ip6[16]; + if(1 != Curl_inet_pton(AF_INET6, hostname, ip6)) + return CURLPX_BAD_ADDRESS_TYPE; + socksreq[len++] = 4; + memcpy(&socksreq[len], ip6, sizeof(ip6)); + len += sizeof(ip6); + } + else +#endif + if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) { + socksreq[len++] = 1; + memcpy(&socksreq[len], ip4, sizeof(ip4)); + len += sizeof(ip4); + } + else { + socksreq[len++] = 3; + socksreq[len++] = (char) hostname_len; /* one byte address length */ + memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ + len += hostname_len; + } infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", hostname, remote_port); } diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index 34bfa37..8ef2f8f 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c @@ -257,7 +257,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 1) { /* status / messgae type */ + if(socksreq[1] != 1) { /* status / message type */ failf(data, "Invalid GSS-API authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); @@ -452,7 +452,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 2) { /* status / messgae type */ + if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid GSS-API encryption response type (%d %d).", socksreq[0], socksreq[1]); gss_delete_sec_context(&gss_status, &gss_context, NULL); diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index cb225b9..ffc8703 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c @@ -277,7 +277,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 1) { /* status / messgae type */ + if(socksreq[1] != 1) { /* status / message type */ failf(data, "Invalid SSPI authentication response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); free(service_name); diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c index a94e2c8..bcc0795 100644 --- a/Utilities/cmcurl/lib/splay.c +++ b/Utilities/cmcurl/lib/splay.c @@ -107,7 +107,7 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, if(!node) return t; - if(t != NULL) { + if(t) { t = Curl_splay(i, t); if(compare(i, t->key) == 0) { /* There already exists a node in the tree with the very same key. Build @@ -154,7 +154,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, struct Curl_tree *t, struct Curl_tree **removed) { - static struct curltime tv_zero = {0, 0}; + static const struct curltime tv_zero = {0, 0}; struct Curl_tree *x; if(!t) { diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index 8a27197..07d73a7 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -404,6 +404,9 @@ curl_multi_strerror(CURLMcode error) case CURLM_BAD_FUNCTION_ARGUMENT: return "A libcurl function was given a bad argument"; + case CURLM_ABORTED_BY_CALLBACK: + return "Operation was aborted by an application callback"; + case CURLM_LAST: break; } @@ -453,6 +456,114 @@ curl_share_strerror(CURLSHcode error) #endif } +const char * +curl_url_strerror(CURLUcode error) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + switch(error) { + case CURLUE_OK: + return "No error"; + + case CURLUE_BAD_HANDLE: + return "An invalid CURLU pointer was passed as argument"; + + case CURLUE_BAD_PARTPOINTER: + return "An invalid 'part' argument was passed as argument"; + + case CURLUE_MALFORMED_INPUT: + return "Malformed input to a URL function"; + + case CURLUE_BAD_PORT_NUMBER: + return "Port number was not a decimal number between 0 and 65535"; + + case CURLUE_UNSUPPORTED_SCHEME: + return "This libcurl build doesn't support the given URL scheme"; + + case CURLUE_URLDECODE: + return "URL decode error, most likely because of rubbish in the input"; + + case CURLUE_OUT_OF_MEMORY: + return "A memory function failed"; + + case CURLUE_USER_NOT_ALLOWED: + return "Credentials was passed in the URL when prohibited"; + + case CURLUE_UNKNOWN_PART: + return "An unknown part ID was passed to a URL API function"; + + case CURLUE_NO_SCHEME: + return "No scheme part in the URL"; + + case CURLUE_NO_USER: + return "No user part in the URL"; + + case CURLUE_NO_PASSWORD: + return "No password part in the URL"; + + case CURLUE_NO_OPTIONS: + return "No options part in the URL"; + + case CURLUE_NO_HOST: + return "No host part in the URL"; + + case CURLUE_NO_PORT: + return "No port part in the URL"; + + case CURLUE_NO_QUERY: + return "No query part in the URL"; + + case CURLUE_NO_FRAGMENT: + return "No fragment part in the URL"; + + case CURLUE_NO_ZONEID: + return "No zoneid part in the URL"; + + case CURLUE_BAD_LOGIN: + return "Bad login part"; + + case CURLUE_BAD_IPV6: + return "Bad IPv6 address"; + + case CURLUE_BAD_HOSTNAME: + return "Bad hostname"; + + case CURLUE_BAD_FILE_URL: + return "Bad file:// URL"; + + case CURLUE_BAD_SLASHES: + return "Unsupported number of slashes"; + + case CURLUE_BAD_SCHEME: + return "Bad scheme"; + + case CURLUE_BAD_PATH: + return "Bad path"; + + case CURLUE_BAD_FRAGMENT: + return "Bad fragment"; + + case CURLUE_BAD_QUERY: + return "Bad query"; + + case CURLUE_BAD_PASSWORD: + return "Bad password"; + + case CURLUE_BAD_USER: + return "Bad user"; + + case CURLUE_LAST: + break; + } + + return "CURLUcode unknown"; +#else + if(error == CURLUE_OK) + return "No error"; + else + return "Error"; +#endif +} + #ifdef USE_WINSOCK /* This is a helper function for Curl_strerror that converts Winsock error * codes (WSAGetLastError) to error messages. diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index 2939fd0..9a6dd9c 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -102,7 +102,9 @@ CURLcode Curl_win32_init(long flags) Curl_if_nametoindex = pIfNameToIndex; } - if(curlx_verify_windows_version(6, 0, PLATFORM_WINNT, + /* curlx_verify_windows_version must be called during init at least once + because it has its own initialization routine. */ + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { Curl_isVistaOrGreater = TRUE; } diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index aae997d..f8c6844 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -186,7 +186,7 @@ const struct Curl_handler Curl_handler_tftp = { PORT_TFTP, /* defport */ CURLPROTO_TFTP, /* protocol */ CURLPROTO_TFTP, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ + PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ }; /********************************************************** @@ -1304,9 +1304,9 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done) /********************************************************** * - * tftp_peform + * tftp_perform * - * Entry point for transfer from tftp_do, sarts state mach + * Entry point for transfer from tftp_do, starts state mach * **********************************************************/ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done) diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 05fec79..22704fa 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -1631,7 +1631,7 @@ CURLcode Curl_follow(struct Curl_easy *data, if((type != FOLLOW_RETRY) && (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN)) + Curl_is_absolute_url(newurl, NULL, 0)) /* If this is not redirect due to a 401 or 407 response and an absolute URL: don't allow a custom port number */ disallowport = TRUE; diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 37b6c0e..9f10135 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -137,6 +137,15 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "curl_memory.h" #include "memdebug.h" +/* Count of the backend ssl objects to allocate */ +#ifdef USE_SSL +# ifndef CURL_DISABLE_PROXY +# define SSL_BACKEND_CNT 4 +# else +# define SSL_BACKEND_CNT 2 +# endif +#endif + static void conn_free(struct connectdata *conn); /* Some parts of the code (e.g. chunked encoding) assume this buffer has at @@ -354,9 +363,7 @@ static void up_free(struct Curl_easy *data) * This is the internal function curl_easy_cleanup() calls. This should * cleanup and free all resources associated with this sessionhandle. * - * NOTE: if we ever add something that attempts to write to a socket or - * similar here, we must ignore SIGPIPE first. It is currently only done - * when curl_easy_perform() is invoked. + * We ignore SIGPIPE when this is called from curl_easy_cleanup. */ CURLcode Curl_close(struct Curl_easy **datap) @@ -542,8 +549,10 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) * libcurl 7.10 introduced SSL verification *by default*! This needs to be * switched off unless wanted. */ +#ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; +#endif set->ssl.primary.verifypeer = TRUE; set->ssl.primary.verifyhost = TRUE; #ifdef USE_TLS_SRP @@ -622,6 +631,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ set->maxage_conn = 118; + set->maxlifetime_conn = 0; set->http09_allowed = FALSE; set->httpwant = #ifdef USE_NGHTTP2 @@ -844,7 +854,7 @@ CURLcode Curl_disconnect(struct Curl_easy *data, return CURLE_OK; } - if(conn->dns_entry != NULL) { + if(conn->dns_entry) { Curl_resolv_unlock(data, conn->dns_entry); conn->dns_entry = NULL; } @@ -962,21 +972,36 @@ socks_proxy_info_matches(const struct proxy_info *data, #define socks_proxy_info_matches(x,y) FALSE #endif -/* A connection has to have been idle for a shorter time than 'maxage_conn' to - be subject for reuse. The success rate is just too low after this. */ +/* A connection has to have been idle for a shorter time than 'maxage_conn' + (the success rate is just too low after this), or created less than + 'maxlifetime_conn' ago, to be subject for reuse. */ static bool conn_maxage(struct Curl_easy *data, struct connectdata *conn, struct curltime now) { - timediff_t idletime = Curl_timediff(now, conn->lastused); + timediff_t idletime, lifetime; + + idletime = Curl_timediff(now, conn->lastused); idletime /= 1000; /* integer seconds is fine */ if(idletime > data->set.maxage_conn) { - infof(data, "Too old connection (%ld seconds), disconnect it", + infof(data, "Too old connection (%ld seconds idle), disconnect it", idletime); return TRUE; } + + lifetime = Curl_timediff(now, conn->created); + lifetime /= 1000; /* integer seconds is fine */ + + if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) { + infof(data, + "Too old connection (%ld seconds since creation), disconnect it", + lifetime); + return TRUE; + } + + return FALSE; } @@ -1284,13 +1309,12 @@ ConnectionExists(struct Curl_easy *data, if(check->proxy_ssl[FIRSTSOCKET].state != ssl_connection_complete) continue; } - else { - if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) - continue; - if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) - continue; - } + + if(!Curl_ssl_config_matches(&needle->ssl_config, + &check->ssl_config)) + continue; + if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) + continue; } } #endif @@ -1667,7 +1691,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) data becomes proxy backend data). */ { size_t sslsize = Curl_ssl->sizeof_ssl_backend_data; - char *ssl = calloc(4, sslsize); + char *ssl = calloc(SSL_BACKEND_CNT, sslsize); if(!ssl) { free(conn); return NULL; @@ -1934,7 +1958,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; if(data->set.str[STRING_DEFAULT_PROTOCOL] && - !Curl_is_absolute_url(data->state.url, NULL, MAX_SCHEME_LEN)) { + !Curl_is_absolute_url(data->state.url, NULL, 0)) { char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], data->state.url); if(!url) @@ -1954,7 +1978,8 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, CURLU_DISALLOW_USER : 0) | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); if(uc) { - DEBUGF(infof(data, "curl_url_set rejected %s", data->state.url)); + DEBUGF(infof(data, "curl_url_set rejected %s: %s", data->state.url, + curl_url_strerror(uc))); return Curl_uc_to_curlcode(uc); } @@ -2380,6 +2405,11 @@ static CURLcode parse_proxy(struct Curl_easy *data, CURLcode result = CURLE_OK; char *scheme = NULL; + if(!uhp) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* When parsing the proxy, allowing non-supported schemes since we have these made up ones for proxies. Guess scheme for URLs without it. */ uc = curl_url_set(uhp, CURLUPART_URL, proxy, @@ -2571,7 +2601,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, if(data->set.str[STRING_PROXY]) { proxy = strdup(data->set.str[STRING_PROXY]); /* if global proxy is set, this is it */ - if(NULL == proxy) { + if(!proxy) { failf(data, "memory shortage"); result = CURLE_OUT_OF_MEMORY; goto out; @@ -2581,7 +2611,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, if(data->set.str[STRING_PRE_PROXY]) { socksproxy = strdup(data->set.str[STRING_PRE_PROXY]); /* if global socks proxy is set, this is it */ - if(NULL == socksproxy) { + if(!socksproxy) { failf(data, "memory shortage"); result = CURLE_OUT_OF_MEMORY; goto out; @@ -2763,7 +2793,7 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, size_t plen; size_t olen; - /* the input length check is because this is called directcly from setopt + /* the input length check is because this is called directly from setopt and isn't going through the regular string length check */ size_t llen = strlen(login); if(llen > CURL_MAX_INPUT_LENGTH) @@ -4093,7 +4123,7 @@ CURLcode Curl_connect(struct Curl_easy *data, /* init the single-transfer specific data */ Curl_free_request_state(data); memset(&data->req, 0, sizeof(struct SingleRequest)); - data->req.maxdownload = -1; + data->req.size = data->req.maxdownload = -1; /* call the stuff that needs to be called */ result = create_conn(data, &conn, asyncp); diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index 4257233..bd6b601 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,8 +22,6 @@ * ***************************************************************************/ #include "curl_setup.h" -/* scheme is not URL encoded, the longest libcurl supported ones are... */ -#define MAX_SCHEME_LEN 40 bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen); diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index 7f03862..d29aeb2 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,6 +30,7 @@ #include "escape.h" #include "curl_ctype.h" #include "inet_pton.h" +#include "inet_ntop.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -50,6 +51,9 @@ ((str)[1] == ':' || (str)[1] == '|') && \ ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) +/* scheme is not URL encoded, the longest libcurl supported ones are... */ +#define MAX_SCHEME_LEN 40 + /* Internal representation of CURLU. Point to URL-encoded strings. */ struct Curl_URL { char *scheme; @@ -157,23 +161,23 @@ static size_t strlen_url(const char *url, bool relative) continue; } - switch(*ptr) { - case '?': - left = FALSE; - /* FALLTHROUGH */ - default: - if(urlchar_needs_escaping(*ptr)) - newlen += 2; - newlen++; - break; - case ' ': + if(*ptr == ' ') { if(left) newlen += 3; else newlen++; - break; + continue; } + + if (*ptr == '?') + left = FALSE; + + if(urlchar_needs_escaping(*ptr)) + newlen += 2; + + newlen++; } + return newlen; } @@ -202,19 +206,7 @@ static void strcpy_url(char *output, const char *url, bool relative) continue; } - switch(*iptr) { - case '?': - left = FALSE; - /* FALLTHROUGH */ - default: - if(urlchar_needs_escaping(*iptr)) { - msnprintf(optr, 4, "%%%02x", *iptr); - optr += 3; - } - else - *optr++=*iptr; - break; - case ' ': + if(*iptr == ' ') { if(left) { *optr++='%'; /* add a '%' */ *optr++='2'; /* add a '2' */ @@ -222,41 +214,58 @@ static void strcpy_url(char *output, const char *url, bool relative) } else *optr++='+'; /* add a '+' here */ - break; + continue; } + + if(*iptr == '?') + left = FALSE; + + if(urlchar_needs_escaping(*iptr)) { + msnprintf(optr, 4, "%%%02x", *iptr); + optr += 3; + } + else + *optr++ = *iptr; } *optr = 0; /* null-terminate output buffer */ } /* - * Returns true if the given URL is absolute (as opposed to relative) within - * the buffer size. Returns the scheme in the buffer if TRUE and 'buf' is - * non-NULL. + * Returns true if the given URL is absolute (as opposed to relative). Returns + * the scheme in the buffer if TRUE and 'buf' is non-NULL. The buflen must + * be larger than MAX_SCHEME_LEN if buf is set. */ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen) { size_t i; + DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); + (void)buflen; /* only used in debug-builds */ + if(buf) + buf[0] = 0; /* always leave a defined value in buf */ #ifdef WIN32 if(STARTS_WITH_DRIVE_PREFIX(url)) return FALSE; #endif - for(i = 0; i < buflen && url[i]; ++i) { + for(i = 0; i < MAX_SCHEME_LEN; ++i) { char s = url[i]; - if((s == ':') && (url[i + 1] == '/')) { - if(buf) - buf[i] = 0; - return TRUE; - } - /* RFC 3986 3.1 explains: - scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ - else if(ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') ) { - if(buf) - buf[i] = (char)TOLOWER(s); + if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { + /* RFC 3986 3.1 explains: + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ } - else + else { break; + } + } + if(i && (url[i] == ':') && (url[i + 1] == '/')) { + if(buf) { + buf[i] = 0; + while(i--) { + buf[i] = (char)TOLOWER(url[i]); + } + } + return TRUE; } return FALSE; } @@ -420,6 +429,29 @@ static char *concat_url(const char *base, const char *relurl) return newest; } +/* scan for byte values < 31 or 127 */ +static bool junkscan(const char *part, unsigned int flags) +{ + if(part) { + static const char badbytes[]={ + /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x7f, 0x00 /* null-terminate */ + }; + size_t n = strlen(part); + size_t nfine = strcspn(part, badbytes); + if(nfine != n) + /* since we don't know which part is scanned, return a generic error + code */ + return TRUE; + if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' ')) + return TRUE; + } + return FALSE; +} + /* * parse_hostname_login() * @@ -467,7 +499,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, (h && (h->flags & PROTOPT_URLOPTIONS)) ? &optionsp:NULL); if(ccode) { - result = CURLUE_MALFORMED_INPUT; + result = CURLUE_BAD_LOGIN; goto out; } @@ -477,15 +509,28 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, result = CURLUE_USER_NOT_ALLOWED; goto out; } - + if(junkscan(userp, flags)) { + result = CURLUE_BAD_USER; + goto out; + } u->user = userp; } - if(passwdp) + if(passwdp) { + if(junkscan(passwdp, flags)) { + result = CURLUE_BAD_PASSWORD; + goto out; + } u->password = passwdp; + } - if(optionsp) + if(optionsp) { + if(junkscan(optionsp, flags)) { + result = CURLUE_BAD_LOGIN; + goto out; + } u->options = optionsp; + } return CURLUE_OK; out: @@ -493,6 +538,9 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, free(userp); free(passwdp); free(optionsp); + u->user = NULL; + u->password = NULL; + u->options = NULL; return result; } @@ -516,19 +564,19 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname, int zonelen = len; if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) { if(']' != endbracket) - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; portptr = &hostname[--zonelen + len + 1]; } else - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; } else - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; /* this is a RFC2732-style specified IP-address */ if(portptr && *portptr) { if(*portptr != ':') - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; } else portptr = NULL; @@ -558,9 +606,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname, port = strtol(portptr + 1, &rest, 10); /* Port number must be decimal */ - if((port <= 0) || (port > 0xffff)) - /* Single unix standard says port numbers are 16 bits long, but we don't - treat port zero as OK. */ + if(port > 0xffff) return CURLUE_BAD_PORT_NUMBER; if(rest[0]) @@ -579,46 +625,20 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname, return CURLUE_OK; } -/* scan for byte values < 31 or 127 */ -static bool junkscan(const char *part, unsigned int flags) -{ - if(part) { - static const char badbytes[]={ - /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x7f, 0x00 /* null-terminate */ - }; - size_t n = strlen(part); - size_t nfine = strcspn(part, badbytes); - if(nfine != n) - /* since we don't know which part is scanned, return a generic error - code */ - return TRUE; - if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' ')) - return TRUE; - } - return FALSE; -} - static CURLUcode hostname_check(struct Curl_URL *u, char *hostname) { size_t len; size_t hlen = strlen(hostname); if(hostname[0] == '[') { -#ifdef ENABLE_IPV6 - char dest[16]; /* fits a binary IPv6 address */ -#endif const char *l = "0123456789abcdefABCDEF:."; if(hlen < 4) /* '[::]' is the shortest possible valid string */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; hostname++; hlen -= 2; if(hostname[hlen] != ']') - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; /* only valid letters are ok */ len = strspn(hostname, l); @@ -635,6 +655,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname) while(*h && (*h != ']') && (i < 15)) zoneid[i++] = *h++; if(!i || (']' != *h)) + /* impossible to reach? */ return CURLUE_MALFORMED_INPUT; zoneid[i] = 0; u->zoneid = strdup(zoneid); @@ -644,22 +665,34 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname) hostname[len + 1] = 0; /* terminate the hostname */ } else - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; /* hostname is fine */ } #ifdef ENABLE_IPV6 - hostname[hlen] = 0; /* end the address there */ - if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) - return CURLUE_MALFORMED_INPUT; - hostname[hlen] = ']'; /* restore ending bracket */ + { + char dest[16]; /* fits a binary IPv6 address */ + char norm[MAX_IPADR_LEN]; + hostname[hlen] = 0; /* end the address there */ + if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) + return CURLUE_BAD_IPV6; + + /* check if it can be done shorter */ + if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) && + (strlen(norm) < hlen)) { + strcpy(hostname, norm); + hlen = strlen(norm); + hostname[hlen + 1] = 0; + } + hostname[hlen] = ']'; /* restore ending bracket */ + } #endif } else { /* letters from the second string is not ok */ - len = strcspn(hostname, " "); + len = strcspn(hostname, " \r\n"); if(hlen != len) /* hostname with bad content */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_HOSTNAME; } if(!hostname[0]) return CURLUE_NO_HOST; @@ -756,10 +789,35 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen) return TRUE; } +/* return strdup'ed version in 'outp', possibly percent decoded */ +static CURLUcode decode_host(char *hostname, char **outp) +{ + char *per = NULL; + if(hostname[0] != '[') + /* only decode if not an ipv6 numerical */ + per = strchr(hostname, '%'); + if(!per) { + *outp = strdup(hostname); + if(!*outp) + return CURLUE_OUT_OF_MEMORY; + } + else { + /* might be encoded */ + size_t dlen; + CURLcode result = Curl_urldecode(NULL, hostname, 0, + outp, &dlen, REJECT_CTRL); + if(result) + return CURLUE_BAD_HOSTNAME; + } + + return CURLUE_OK; +} + static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) { char *path; bool path_alloced = FALSE; + bool uncpath = FALSE; char *hostname; char *query = NULL; char *fragment = NULL; @@ -794,11 +852,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) } /* handle the file: scheme */ - if(url_has_scheme && strcasecompare(schemebuf, "file")) { + if(url_has_scheme && !strcmp(schemebuf, "file")) { + if(urllen <= 6) + /* file:/ is not enough to actually be a complete file: URL */ + return CURLUE_BAD_FILE_URL; + /* path has been allocated large enough to hold this */ strcpy(path, &url[5]); - hostname = NULL; /* no host for file: URLs */ u->scheme = strdup("file"); if(!u->scheme) return CURLUE_OUT_OF_MEMORY; @@ -820,10 +881,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) * * o the hostname matches "localhost" (case-insensitively), or * - * o the hostname is a FQDN that resolves to this machine. + * o the hostname is a FQDN that resolves to this machine, or + * + * o it is an UNC String transformed to an URI (Windows only, RFC 8089 + * Appendix E.3). * * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local. + * "127.0.0.1" hostnames as local, otherwise as an UNC String. * * Additionally, there is an exception for URLs with a Windows drive * letter in the authority (which was accidentally omitted from RFC 8089 @@ -832,25 +896,50 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { /* the URL includes a host name, it must match "localhost" or "127.0.0.1" to be valid */ - if(!checkprefix("localhost/", ptr) && - !checkprefix("127.0.0.1/", ptr)) { + if(checkprefix("localhost/", ptr) || + checkprefix("127.0.0.1/", ptr)) { + ptr += 9; /* now points to the slash after the host */ + } + else { +#if defined(WIN32) + size_t len; + + /* the host name, NetBIOS computer name, can not contain disallowed + chars, and the delimiting slash character must be appended to the + host name */ + path = strpbrk(ptr, "/\\:*?\"<>|"); + if(!path || *path != '/') + return CURLUE_BAD_FILE_URL; + + len = path - ptr; + if(len) { + memcpy(hostname, ptr, len); + hostname[len] = 0; + uncpath = TRUE; + } + + ptr -= 2; /* now points to the // before the host in UNC */ +#else /* Invalid file://hostname/, expected localhost or 127.0.0.1 or none */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_FILE_URL; +#endif } - ptr += 9; /* now points to the slash after the host */ } path = ptr; } + if(!uncpath) + hostname = NULL; /* no host for file: URLs by default */ + #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) /* Don't allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || STARTS_WITH_URL_DRIVE_PREFIX(path)) { /* File drive letters are only accepted in MSDOS/Windows */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_FILE_URL; } #else /* If the path starts with a slash and a drive letter, ditch the slash */ @@ -877,7 +966,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) } if((i < 1) || (i>3)) /* less than one or more than three slashes */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_SLASHES; schemep = schemebuf; if(!Curl_builtin_scheme(schemep) && @@ -885,13 +974,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) return CURLUE_UNSUPPORTED_SCHEME; if(junkscan(schemep, flags)) - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_SCHEME; } else { /* no scheme! */ if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_SCHEME; if(flags & CURLU_DEFAULT_SCHEME) schemep = DEFAULT_SCHEME; @@ -902,7 +991,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) } hostp = p; /* host name starts here */ - while(*p && !HOSTNAME_END(*p)) /* find end of host name */ + /* find the end of the host name + port number */ + while(*p && !HOSTNAME_END(*p)) p++; len = p - hostp; @@ -912,7 +1002,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) } else { if(!(flags & CURLU_NO_AUTHORITY)) - return CURLUE_MALFORMED_INPUT; + return CURLUE_NO_HOST; } len = strlen(p); @@ -926,9 +1016,6 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) } } - if(junkscan(path, flags)) - return CURLUE_MALFORMED_INPUT; - if((flags & CURLU_URLENCODE) && path[0]) { /* worst case output length is 3x the original! */ char *newp = malloc(strlen(path) * 3); @@ -942,6 +1029,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) fragment = strchr(path, '#'); if(fragment) { *fragment++ = 0; + if(junkscan(fragment, flags)) + return CURLUE_BAD_FRAGMENT; if(fragment[0]) { u->fragment = strdup(fragment); if(!u->fragment) @@ -952,12 +1041,17 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) query = strchr(path, '?'); if(query) { *query++ = 0; + if(junkscan(query, flags)) + return CURLUE_BAD_QUERY; /* done even if the query part is a blank string */ u->query = strdup(query); if(!u->query) return CURLUE_OUT_OF_MEMORY; } + if(junkscan(path, flags)) + return CURLUE_BAD_PATH; + if(!path[0]) /* if there's no path left set, unset */ path = NULL; @@ -987,12 +1081,10 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(hostname) { char normalized_ipv4[sizeof("255.255.255.255") + 1]; + /* * Parse the login details and strip them out of the host name. */ - if(junkscan(hostname, flags)) - return CURLUE_MALFORMED_INPUT; - result = parse_hostname_login(u, &hostname, flags); if(result) return result; @@ -1001,22 +1093,27 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags) if(result) return result; + if(junkscan(hostname, flags)) + return CURLUE_BAD_HOSTNAME; + if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) { /* Skip hostname check, it's allowed to be empty. */ + u->host = strdup(""); } else { - result = hostname_check(u, hostname); - if(result) - return result; + if(ipv4_normalize(hostname, normalized_ipv4, sizeof(normalized_ipv4))) + u->host = strdup(normalized_ipv4); + else { + result = decode_host(hostname, &u->host); + if(result) + return result; + result = hostname_check(u, u->host); + if(result) + return result; + } } - - if(ipv4_normalize(hostname, normalized_ipv4, sizeof(normalized_ipv4))) - u->host = strdup(normalized_ipv4); - else - u->host = strdup(hostname); if(!u->host) return CURLUE_OUT_OF_MEMORY; - if((flags & CURLU_GUESS_SCHEME) && !schemep) { /* legacy curl-style guess based on host name */ if(checkprefix("ftp.", hostname)) @@ -1111,6 +1208,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, CURLUcode ifmissing = CURLUE_UNKNOWN_PART; char portbuf[7]; bool urldecode = (flags & CURLU_URLDECODE)?1:0; + bool urlencode = (flags & CURLU_URLENCODE)?1:0; bool plusdecode = FALSE; (void)flags; if(!u) @@ -1143,6 +1241,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, break; case CURLUPART_ZONEID: ptr = u->zoneid; + ifmissing = CURLUE_NO_ZONEID; break; case CURLUPART_PORT: ptr = u->port; @@ -1228,16 +1327,54 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, if(h && !(h->flags & PROTOPT_URLOPTIONS)) options = NULL; - if((u->host[0] == '[') && u->zoneid) { - /* make it '[ host %25 zoneid ]' */ - size_t hostlen = strlen(u->host); - size_t alen = hostlen + 3 + strlen(u->zoneid) + 1; - allochost = malloc(alen); + if(u->host[0] == '[') { + if(u->zoneid) { + /* make it '[ host %25 zoneid ]' */ + size_t hostlen = strlen(u->host); + size_t alen = hostlen + 3 + strlen(u->zoneid) + 1; + allochost = malloc(alen); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; + memcpy(allochost, u->host, hostlen - 1); + msnprintf(&allochost[hostlen - 1], alen - hostlen + 1, + "%%25%s]", u->zoneid); + } + } + else if(urlencode) { + allochost = curl_easy_escape(NULL, u->host, 0); if(!allochost) return CURLUE_OUT_OF_MEMORY; - memcpy(allochost, u->host, hostlen - 1); - msnprintf(&allochost[hostlen - 1], alen - hostlen + 1, - "%%25%s]", u->zoneid); + } + else { + /* only encode '%' in output host name */ + char *host = u->host; + size_t pcount = 0; + /* first, count number of percents present in the name */ + while(*host) { + if(*host == '%') + pcount++; + host++; + } + /* if there were percents, encode the host name */ + if(pcount) { + size_t hostlen = strlen(u->host); + size_t alen = hostlen + 2 * pcount + 1; + char *o = allochost = malloc(alen); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; + + host = u->host; + while(*host) { + if(*host == '%') { + memcpy(o, "%25", 3); + o += 3; + host++; + continue; + } + *o++ = *host++; + } + *o = '\0'; + } } url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", @@ -1362,7 +1499,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, case CURLUPART_SCHEME: if(strlen(part) > MAX_SCHEME_LEN) /* too long */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_SCHEME; if(!(flags & CURLU_NON_SUPPORT_SCHEME) && /* verify that it is a fine scheme */ !Curl_builtin_scheme(part)) @@ -1379,10 +1516,15 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, case CURLUPART_OPTIONS: storep = &u->options; break; - case CURLUPART_HOST: + case CURLUPART_HOST: { + size_t len = strcspn(part, " \r\n"); + if(strlen(part) != len) + /* hostname with bad content */ + return CURLUE_BAD_HOSTNAME; storep = &u->host; Curl_safefree(u->zoneid); break; + } case CURLUPART_ZONEID: storep = &u->zoneid; break; @@ -1395,7 +1537,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_BAD_PORT_NUMBER; if(*endp) /* weirdly provided number, not good! */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_PORT_NUMBER; storep = &u->port; } break; @@ -1424,7 +1566,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, char *redired_url; CURLU *handle2; - if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN + 1)) { + if(Curl_is_absolute_url(part, NULL, 0)) { handle2 = curl_url(); if(!handle2) return CURLUE_OUT_OF_MEMORY; @@ -1559,7 +1701,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, else { if(hostname_check(u, (char *)newp)) { free((char *)newp); - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_HOSTNAME; } } } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 6ffd976..cc9c888 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -330,7 +330,7 @@ struct digestdata { char *opaque; char *qop; char *algorithm; - int nc; /* nounce count */ + int nc; /* nonce count */ BIT(stale); /* set true for re-negotiation */ BIT(userhash); #endif @@ -518,7 +518,9 @@ struct ConnectBits { BIT(tls_enable_npn); /* TLS NPN extension? */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ BIT(connect_only); +#ifndef CURL_DISABLE_DOH BIT(doh); +#endif #ifdef USE_UNIX_SOCKETS BIT(abstract_unix_socket); #endif @@ -835,6 +837,7 @@ struct Curl_handler { #define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ #define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in user name and password */ +#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol can't proxy over TCP */ #define CONNCHECK_NONE 0 /* No checks */ #define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ @@ -1554,6 +1557,7 @@ enum dupstring { STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ + STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */ STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ STRING_PROXY_SERVICE_NAME, /* Proxy service name */ STRING_SERVICE_NAME, /* Service name */ @@ -1651,6 +1655,8 @@ struct UserDefined { curl_closesocket_callback fclosesocket; /* function for closing the socket */ void *closesocket_client; + curl_prereq_callback fprereq; /* pre-initial request callback */ + void *prereq_userp; /* pre-initial request user data */ void *seek_client; /* pointer to pass to the seek callback */ /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */ @@ -1675,6 +1681,8 @@ struct UserDefined { long server_response_timeout; /* in milliseconds, 0 means no timeout */ long maxage_conn; /* in seconds, max idle time to allow a connection that is to be reused */ + long maxlifetime_conn; /* in seconds, max time since creation to allow a + connection that is to be reused */ long tftp_blksize; /* in bytes, 0 means use default */ curl_off_t filesize; /* size of file to upload, -1 means unknown */ long low_speed_limit; /* bytes/second */ @@ -1744,6 +1752,7 @@ struct UserDefined { unsigned int scope_id; /* Scope id for IPv6 */ long allowed_protocols; long redir_protocols; + long mime_options; /* Mime option flags. */ struct curl_slist *mail_rcpt; /* linked list of mail recipients */ /* Common RTSP header options */ Curl_RtspReq rtspreq; /* RTSP request type */ @@ -1851,11 +1860,12 @@ struct UserDefined { header */ BIT(abstract_unix_socket); BIT(disallow_username_in_url); /* disallow username in url */ +#ifndef CURL_DISABLE_DOH BIT(doh); /* DNS-over-HTTPS enabled */ - BIT(doh_get); /* use GET for DoH requests, instead of POST */ BIT(doh_verifypeer); /* DoH certificate peer verification */ BIT(doh_verifyhost); /* DoH certificate hostname verification */ BIT(doh_verifystatus); /* DoH certificate status verification */ +#endif BIT(http09_allowed); /* allow HTTP/0.9 responses */ BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some recipients */ diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index a04ffab..d8aac66 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -230,7 +230,7 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value) return CURLE_OUT_OF_MEMORY; token = strtok_r(tmp, ",", &tok_buf); - while(token != NULL) { + while(token) { if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) *value |= DIGEST_QOP_VALUE_AUTH; else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) @@ -556,7 +556,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, return CURLE_OUT_OF_MEMORY; token = strtok_r(tmp, ",", &tok_buf); - while(token != NULL) { + while(token) { if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) { foundAuth = TRUE; } @@ -666,8 +666,8 @@ static CURLcode auth_create_digest_http_message( struct digestdata *digest, char **outptr, size_t *outlen, void (*convert_to_ascii)(unsigned char *, unsigned char *), - void (*hash)(unsigned char *, const unsigned char *, - const size_t)) + CURLcode (*hash)(unsigned char *, const unsigned char *, + const size_t)) { CURLcode result; unsigned char hashbuf[32]; /* 32 bytes/256 bits */ @@ -722,8 +722,7 @@ static CURLcode auth_create_digest_http_message( unq(nonce-value) ":" unq(cnonce-value) */ - hashthis = aprintf("%s:%s:%s", digest->userhash ? userh : userp, - digest->realm, passwdp); + hashthis = aprintf("%s:%s:%s", userp, digest->realm, passwdp); if(!hashthis) return CURLE_OUT_OF_MEMORY; diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 0aa3f1c..04f6590 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -603,7 +603,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, memcpy(tmp, &ntlm->nonce[0], 8); memcpy(tmp + 8, entropy, 8); - Curl_md5it(md5sum, tmp, 16); + result = Curl_md5it(md5sum, tmp, 16); + if(result) + return result; /* We shall only use the first 8 bytes of md5sum, but the des code in Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */ diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c index b8157e9..79a2aa6 100644 --- a/Utilities/cmcurl/lib/version_win32.c +++ b/Utilities/cmcurl/lib/version_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2020, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,11 +26,28 @@ #include <curl/curl.h> #include "version_win32.h" +#include "warnless.h" /* The last #include files should be: */ #include "curl_memory.h" #include "memdebug.h" +/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) + and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ +struct OUR_OSVERSIONINFOEXW { + ULONG dwOSVersionInfoSize; + ULONG dwMajorVersion; + ULONG dwMinorVersion; + ULONG dwBuildNumber; + ULONG dwPlatformId; + WCHAR szCSDVersion[128]; + USHORT wServicePackMajor; + USHORT wServicePackMinor; + USHORT wSuiteMask; + UCHAR wProductType; + UCHAR wReserved; +}; + /* * curlx_verify_windows_version() * @@ -40,6 +57,8 @@ * * majorVersion [in] - The major version number. * minorVersion [in] - The minor version number. + * buildVersion [in] - The build version number. If 0, this parameter is + * ignored. * platform [in] - The optional platform identifier. * condition [in] - The test condition used to specifier whether we are * checking a version less then, equal to or greater than @@ -50,6 +69,7 @@ */ bool curlx_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion, + const unsigned int buildVersion, const PlatformIdentifier platform, const VersionCondition condition) { @@ -101,34 +121,52 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case VERSION_LESS_THAN: if(osver.dwMajorVersion < majorVersion || (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion < minorVersion)) + osver.dwMinorVersion < minorVersion) || + (buildVersion != 0 && + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + osver.dwBuildNumber < buildVersion))) matched = TRUE; break; case VERSION_LESS_THAN_EQUAL: if(osver.dwMajorVersion < majorVersion || (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion <= minorVersion)) + osver.dwMinorVersion < minorVersion) || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber <= buildVersion))) matched = TRUE; break; case VERSION_EQUAL: if(osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion) + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber == buildVersion)) matched = TRUE; break; case VERSION_GREATER_THAN_EQUAL: if(osver.dwMajorVersion > majorVersion || (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion >= minorVersion)) + osver.dwMinorVersion > minorVersion) || + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + (buildVersion == 0 || + osver.dwBuildNumber >= buildVersion))) matched = TRUE; break; case VERSION_GREATER_THAN: if(osver.dwMajorVersion > majorVersion || (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion > minorVersion)) + osver.dwMinorVersion > minorVersion) || + (buildVersion != 0 && + (osver.dwMajorVersion == majorVersion && + osver.dwMinorVersion == minorVersion && + osver.dwBuildNumber > buildVersion))) matched = TRUE; break; } @@ -144,6 +182,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case PLATFORM_WINNT: if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT) matched = FALSE; + break; default: /* like platform == PLATFORM_DONT_CARE */ break; @@ -152,16 +191,31 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, } #else ULONGLONG cm = 0; - OSVERSIONINFOEX osver; + struct OUR_OSVERSIONINFOEXW osver; BYTE majorCondition; BYTE minorCondition; + BYTE buildCondition; BYTE spMajorCondition; BYTE spMinorCondition; + DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION | + VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; + + typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) + (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); + static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo; + static bool onetime = true; /* safe because first call is during init */ + + if(onetime) { + pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, + (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo"))); + onetime = false; + } switch(condition) { case VERSION_LESS_THAN: majorCondition = VER_LESS; minorCondition = VER_LESS; + buildCondition = VER_LESS; spMajorCondition = VER_LESS_EQUAL; spMinorCondition = VER_LESS_EQUAL; break; @@ -169,6 +223,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case VERSION_LESS_THAN_EQUAL: majorCondition = VER_LESS_EQUAL; minorCondition = VER_LESS_EQUAL; + buildCondition = VER_LESS_EQUAL; spMajorCondition = VER_LESS_EQUAL; spMinorCondition = VER_LESS_EQUAL; break; @@ -176,6 +231,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case VERSION_EQUAL: majorCondition = VER_EQUAL; minorCondition = VER_EQUAL; + buildCondition = VER_EQUAL; spMajorCondition = VER_GREATER_EQUAL; spMinorCondition = VER_GREATER_EQUAL; break; @@ -183,6 +239,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case VERSION_GREATER_THAN_EQUAL: majorCondition = VER_GREATER_EQUAL; minorCondition = VER_GREATER_EQUAL; + buildCondition = VER_GREATER_EQUAL; spMajorCondition = VER_GREATER_EQUAL; spMinorCondition = VER_GREATER_EQUAL; break; @@ -190,6 +247,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, case VERSION_GREATER_THAN: majorCondition = VER_GREATER; minorCondition = VER_GREATER; + buildCondition = VER_GREATER; spMajorCondition = VER_GREATER_EQUAL; spMinorCondition = VER_GREATER_EQUAL; break; @@ -202,6 +260,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, osver.dwOSVersionInfoSize = sizeof(osver); osver.dwMajorVersion = majorVersion; osver.dwMinorVersion = minorVersion; + osver.dwBuildNumber = buildVersion; if(platform == PLATFORM_WINDOWS) osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS; else if(platform == PLATFORM_WINNT) @@ -211,13 +270,43 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition); cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition); cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition); - if(platform != PLATFORM_DONT_CARE) + + if(platform != PLATFORM_DONT_CARE) { cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL); + dwTypeMask |= VER_PLATFORMID; + } + + /* Later versions of Windows have version functions that may not return the + real version of Windows unless the application is so manifested. We prefer + the real version always, so we use the Rtl variant of the function when + possible. Note though the function signatures have underlying fundamental + types that are the same, the return values are different. */ + if(pRtlVerifyVersionInfo) + matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + else + matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm); + + /* Compare the build number separately. VerifyVersionInfo normally compares + major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not + do the same for build (eg 1.9 build 222 is not less than 2.0 build 111). + Build comparison is only needed when build numbers are equal (eg 1.9 is + always less than 2.0 so build comparison is not needed). */ + if(matched && buildVersion && + (condition == VERSION_EQUAL || + ((condition == VERSION_GREATER_THAN_EQUAL || + condition == VERSION_LESS_THAN_EQUAL) && + curlx_verify_windows_version(majorVersion, minorVersion, 0, + platform, VERSION_EQUAL)))) { + + cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition); + dwTypeMask = VER_BUILDNUMBER; + if(pRtlVerifyVersionInfo) + matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); + else + matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, + dwTypeMask, cm); + } - if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION | - VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR), - cm)) - matched = TRUE; #endif return matched; diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h index 9b1bd88..38af87f 100644 --- a/Utilities/cmcurl/lib/version_win32.h +++ b/Utilities/cmcurl/lib/version_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2020, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,6 +45,7 @@ typedef enum { /* This is used to verify if we are running on a specific windows version */ bool curlx_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion, + const unsigned int buildVersion, const PlatformIdentifier platform, const VersionCondition condition); diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c index 9fcfe81..1596049 100644 --- a/Utilities/cmcurl/lib/vquic/ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/ngtcp2.c @@ -29,8 +29,10 @@ #ifdef USE_OPENSSL #include <openssl/err.h> #include <ngtcp2/ngtcp2_crypto_openssl.h> +#include "vtls/openssl.h" #elif defined(USE_GNUTLS) #include <ngtcp2/ngtcp2_crypto_gnutls.h> +#include "vtls/gtls.h" #endif #include "urldata.h" #include "sendf.h" @@ -61,6 +63,7 @@ #endif #define H3_ALPN_H3_29 "\x5h3-29" +#define H3_ALPN_H3 "\x2h3" /* * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. @@ -286,6 +289,27 @@ static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); } + { + struct connectdata *conn = data->conn; + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + + if(conn->ssl_config.verifypeer) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return NULL; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } + } return ssl_ctx; } @@ -303,9 +327,10 @@ static int quic_init_ssl(struct quicsocket *qs) SSL_set_app_data(qs->ssl, qs); SSL_set_connect_state(qs->ssl); + SSL_set_quic_use_legacy_codepoint(qs->ssl, 0); - alpn = (const uint8_t *)H3_ALPN_H3_29; - alpnlen = sizeof(H3_ALPN_H3_29) - 1; + alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; + alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; if(alpn) SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); @@ -417,7 +442,7 @@ static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata) static int quic_init_ssl(struct quicsocket *qs) { - gnutls_datum_t alpn = {NULL, 0}; + gnutls_datum_t alpn[2]; /* this will need some attention when HTTPS proxy over QUIC get fixed */ const char * const hostname = qs->conn->host.name; int rc; @@ -439,12 +464,10 @@ static int quic_init_ssl(struct quicsocket *qs) gnutls_alert_set_read_function(qs->ssl, alert_read_func); rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters", - 0xffa5, GNUTLS_EXT_TLS, - tp_recv_func, tp_send_func, - NULL, NULL, NULL, - GNUTLS_EXT_FLAG_TLS | - GNUTLS_EXT_FLAG_CLIENT_HELLO | - GNUTLS_EXT_FLAG_EE); + NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS, + tp_recv_func, tp_send_func, NULL, NULL, NULL, + GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_EE); if(rc < 0) { H3BUGF(fprintf(stderr, "gnutls_session_ext_register failed: %s\n", gnutls_strerror(rc))); @@ -484,10 +507,12 @@ static int quic_init_ssl(struct quicsocket *qs) } /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ - alpn.data = (unsigned char *)H3_ALPN_H3_29 + 1; - alpn.size = sizeof(H3_ALPN_H3_29) - 2; - if(alpn.data) - gnutls_alpn_set_protocols(qs->ssl, &alpn, 1, 0); + alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; + alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; + alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; + alpn[1].size = sizeof(H3_ALPN_H3) - 2; + + gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY); /* set SNI */ gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname)); @@ -648,6 +673,20 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, return 0; } +static void cb_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, just fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, uint8_t *token, size_t cidlen, void *user_data) @@ -685,7 +724,7 @@ static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_recv_retry_cb, cb_extend_max_local_streams_bidi, NULL, /* extend_max_local_streams_uni */ - NULL, /* rand */ + cb_rand, cb_get_new_connection_id, NULL, /* remove_connection_id */ ngtcp2_crypto_update_key_cb, /* update_key */ @@ -703,7 +742,7 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* recv_datagram */ NULL, /* ack_datagram */ NULL, /* lost_datagram */ - NULL, /* get_path_challenge_data */ + ngtcp2_crypto_get_path_challenge_data_cb, cb_stream_stop_sending }; @@ -776,7 +815,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, ngtcp2_addr_init(&path.remote, addr, addrlen); rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, - NGTCP2_PROTO_VER_MIN, &ng_callbacks, + NGTCP2_PROTO_VER_V1, &ng_callbacks, &qs->settings, &qs->transport_params, NULL, qs); if(rc) return CURLE_QUIC_CONNECT_ERROR; @@ -792,7 +831,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data, void Curl_quic_ver(char *p, size_t len) { const ngtcp2_info *ng2 = ngtcp2_version(0); - nghttp3_info *ht3 = nghttp3_version(0); + const nghttp3_info *ht3 = nghttp3_version(0); (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", ng2->version_str, ht3->version_str); } @@ -1622,8 +1661,10 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data, return sent; } -static void ng_has_connected(struct connectdata *conn, int tempindex) +static CURLcode ng_has_connected(struct Curl_easy *data, + struct connectdata *conn, int tempindex) { + CURLcode result = CURLE_OK; conn->recv[FIRSTSOCKET] = ngh3_stream_recv; conn->send[FIRSTSOCKET] = ngh3_stream_send; conn->handler = &Curl_handler_http3; @@ -1631,6 +1672,27 @@ static void ng_has_connected(struct connectdata *conn, int tempindex) conn->httpversion = 30; conn->bundle->multiuse = BUNDLE_MULTIPLEX; conn->quic = &conn->hequic[tempindex]; + + if(conn->ssl_config.verifyhost) { +#ifdef USE_OPENSSL + X509 *server_cert; + CURLcode result; + server_cert = SSL_get_peer_certificate(conn->quic->ssl); + if(!server_cert) { + return CURLE_PEER_FAILED_VERIFICATION; + } + result = Curl_ossl_verifyhost(data, conn, server_cert); + X509_free(server_cert); + if(result) + return result; + infof(data, "Verified certificate just fine"); +#else + result = Curl_gtls_verifyserver(data, conn, conn->quic->ssl, FIRSTSOCKET); +#endif + } + else + infof(data, "Skipped certificate verification"); + return result; } /* @@ -1654,8 +1716,9 @@ CURLcode Curl_quic_is_connected(struct Curl_easy *data, goto error; if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { - *done = TRUE; - ng_has_connected(conn, sockindex); + result = ng_has_connected(data, conn, sockindex); + if(!result) + *done = TRUE; } return result; @@ -1702,6 +1765,10 @@ static CURLcode ng_process_ingress(struct Curl_easy *data, rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts); if(rv) { /* TODO Send CONNECTION_CLOSE if possible */ + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; return CURLE_RECV_ERROR; } } diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index a772f1f..581bc1b 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -81,6 +81,11 @@ #include "select.h" #include "warnless.h" #include "curl_path.h" +#include "strcase.h" + +#include <curl_base64.h> /* for base64 encoding/decoding */ +#include <curl_sha256.h> + /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -615,40 +620,141 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) struct connectdata *conn = data->conn; struct ssh_conn *sshc = &conn->proto.sshc; const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; - char md5buffer[33]; + const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + + infof(data, "SSH MD5 public key: %s", + pubkey_md5 != NULL ? pubkey_md5 : "NULL"); + infof(data, "SSH SHA256 public key: %s", + pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); - const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session, - LIBSSH2_HOSTKEY_HASH_MD5); + if(pubkey_sha256) { + const char *fingerprint = NULL; + char *fingerprint_b64 = NULL; + size_t fingerprint_b64_len; + size_t pub_pos = 0; + size_t b64_pos = 0; - if(fingerprint) { +#ifdef LIBSSH2_HOSTKEY_HASH_SHA256 /* The fingerprint points to static storage (!), don't free() it. */ - int i; - for(i = 0; i < 16; i++) - msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); - infof(data, "SSH MD5 fingerprint: %s", md5buffer); - } + fingerprint = libssh2_hostkey_hash(sshc->ssh_session, + LIBSSH2_HOSTKEY_HASH_SHA256); +#else + const char *hostkey; + size_t len = 0; + unsigned char hash[32]; + + hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL); + if(hostkey) { + if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len)) + fingerprint = (char *) hash; + } +#endif - /* Before we authenticate we check the hostkey's MD5 fingerprint - * against a known fingerprint, if available. - */ - if(pubkey_md5 && strlen(pubkey_md5) == 32) { - if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) { - if(fingerprint) - failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " - "Remote %s is not equal to %s", md5buffer, pubkey_md5); - else - failf(data, - "Denied establishing ssh session: md5 fingerprint not available"); + if(!fingerprint) { + failf(data, + "Denied establishing ssh session: sha256 fingerprint " + "not available"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + /* The length of fingerprint is 32 bytes for SHA256. + * See libssh2_hostkey_hash documentation. */ + if(Curl_base64_encode(data, fingerprint, 32, &fingerprint_b64, + &fingerprint_b64_len) != CURLE_OK) { + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + if(!fingerprint_b64) { + failf(data, "sha256 fingerprint could not be encoded"); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + + infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + + /* Find the position of any = padding characters in the public key */ + while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { + pub_pos++; + } + + /* Find the position of any = padding characters in the base64 coded + * hostkey fingerprint */ + while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { + b64_pos++; + } + + /* Before we authenticate we check the hostkey's sha256 fingerprint + * against a known fingerprint, if available. + */ + if((pub_pos != b64_pos) || + Curl_strncasecompare(fingerprint_b64, pubkey_sha256, pub_pos) != 1) { + free(fingerprint_b64); + + failf(data, + "Denied establishing ssh session: mismatch sha256 fingerprint. " + "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); state(data, SSH_SESSION_FREE); sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; return sshc->actualcode; } - infof(data, "MD5 checksum match!"); + + free(fingerprint_b64); + + infof(data, "SHA256 checksum match!"); + } + + if(pubkey_md5) { + char md5buffer[33]; + const char *fingerprint = NULL; + + fingerprint = libssh2_hostkey_hash(sshc->ssh_session, + LIBSSH2_HOSTKEY_HASH_MD5); + + if(fingerprint) { + /* The fingerprint points to static storage (!), don't free() it. */ + int i; + for(i = 0; i < 16; i++) { + msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); + } + + infof(data, "SSH MD5 fingerprint: %s", md5buffer); + } + + /* Before we authenticate we check the hostkey's MD5 fingerprint + * against a known fingerprint, if available. + */ + if(pubkey_md5 && strlen(pubkey_md5) == 32) { + if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) { + if(fingerprint) { + failf(data, + "Denied establishing ssh session: mismatch md5 fingerprint. " + "Remote %s is not equal to %s", md5buffer, pubkey_md5); + } + else { + failf(data, + "Denied establishing ssh session: md5 fingerprint " + "not available"); + } + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + infof(data, "MD5 checksum match!"); + } + } + + if(!pubkey_md5 && !pubkey_sha256) { + return ssh_knownhost(data); + } + else { /* as we already matched, we skip the check for known hosts */ return CURLE_OK; } - return ssh_knownhost(data); } /* @@ -3610,7 +3716,7 @@ void Curl_ssh_cleanup(void) void Curl_ssh_version(char *buffer, size_t buflen) { - (void)msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION); + (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION); } /* The SSH session is associated with the *CONNECTION* but the callback user diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 4b1e2ec..5b4cde9 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -449,7 +449,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) switch(sshc->state) { case SSH_INIT: state(data, SSH_S_STARTUP); - /* FALLTHROUGH */ + break; + case SSH_S_STARTUP: rc = wolfSSH_connect(sshc->ssh_session); if(rc != WS_SUCCESS) @@ -838,7 +839,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) break; } state(data, SSH_SFTP_READDIR); - /* FALLTHROUGH */ + break; + case SSH_SFTP_READDIR: name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path); if(!name) diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index e87649e..9b772d0 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -608,6 +608,7 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data, if(SSL_SET_OPTION(primary.sessionid)) { bool incache; + bool added = FALSE; void *oldsession; br_ssl_session_parameters *session; @@ -623,10 +624,11 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data, Curl_ssl_delsessionid(data, oldsession); ret = Curl_ssl_addsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - session, 0, sockindex); + session, 0, sockindex, &added); Curl_ssl_sessionid_unlock(data); - if(ret) { + if(!added) free(session); + if(ret) { return CURLE_OUT_OF_MEMORY; } } diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 1b145d8..18864aa 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -404,6 +404,7 @@ gtls_connect_step1(struct Curl_easy *data, const char * const hostname = SSL_HOST_NAME(); long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult); const char *tls13support; + CURLcode result; if(connssl->state == ssl_connection_complete) /* to make us tolerant against being called more than once for the @@ -496,6 +497,7 @@ gtls_connect_step1(struct Curl_easy *data, /* use system ca certificate store as fallback */ if(SSL_CONN_CONFIG(verifypeer) && !(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath))) { + /* this ignores errors on purpose */ gnutls_certificate_set_x509_system_trust(backend->cred); } #endif @@ -557,31 +559,25 @@ gtls_connect_step1(struct Curl_easy *data, /* Ensure +SRP comes at the *end* of all relevant strings so that it can be * removed if a run-time error indicates that SRP is not supported by this * GnuTLS version */ - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_TLSv1_3: - if(!tls13support) { - failf(data, "This GnuTLS installation does not support TLS 1.3"); - return CURLE_SSL_CONNECT_ERROR; - } - /* FALLTHROUGH */ - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: { - CURLcode result = set_ssl_version_min_max(data, &prioritylist, - tls13support); - if(result) - return result; - break; - } - case CURL_SSLVERSION_SSLv2: - case CURL_SSLVERSION_SSLv3: - default: - failf(data, "GnuTLS does not support SSLv2 or SSLv3"); + + if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2 || + SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) { + failf(data, "GnuTLS does not support SSLv2 or SSLv3"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_TLSv1_3) { + if(!tls13support) { + failf(data, "This GnuTLS installation does not support TLS 1.3"); return CURLE_SSL_CONNECT_ERROR; + } } + /* At this point we know we have a supported TLS version, so set it */ + result = set_ssl_version_min_max(data, &prioritylist, tls13support); + if(result) + return result; + #ifdef HAVE_GNUTLS_SRP /* Only add SRP to the cipher list if SRP is requested. Otherwise * GnuTLS will disable TLS 1.3 support. */ @@ -636,7 +632,10 @@ gtls_connect_step1(struct Curl_easy *data, cur++; infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); - gnutls_alpn_set_protocols(session, protocols, cur, 0); + if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) { + failf(data, "failed setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } } if(SSL_SET_OPTION(primary.clientcert)) { @@ -762,10 +761,10 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; /* if a path wasn't specified, don't pin */ - if(NULL == pinnedpubkey) + if(!pinnedpubkey) return CURLE_OK; - if(NULL == cert) + if(!cert) return result; do { @@ -783,7 +782,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, break; /* failed */ buff1 = malloc(len1); - if(NULL == buff1) + if(!buff1) break; /* failed */ len2 = len1; @@ -798,7 +797,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); } while(0); - if(NULL != key) + if(key) gnutls_pubkey_deinit(key); Curl_safefree(buff1); @@ -809,10 +808,11 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, static Curl_recv gtls_recv; static Curl_send gtls_send; -static CURLcode -gtls_connect_step3(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +CURLcode +Curl_gtls_verifyserver(struct Curl_easy *data, + struct connectdata *conn, + gnutls_session_t session, + int sockindex) { unsigned int cert_list_size; const gnutls_datum_t *chainp; @@ -824,9 +824,6 @@ gtls_connect_step3(struct Curl_easy *data, size_t size; time_t certclock; const char *ptr; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session = backend->session; int rc; gnutls_datum_t proto; CURLcode result = CURLE_OK; @@ -1270,8 +1267,6 @@ gtls_connect_step3(struct Curl_easy *data, } conn->ssl[sockindex].state = ssl_connection_complete; - conn->recv[sockindex] = gtls_recv; - conn->send[sockindex] = gtls_send; if(SSL_SET_OPTION(primary.sessionid)) { /* we always unconditionally get the session id here, as even if we @@ -1287,6 +1282,7 @@ gtls_connect_step3(struct Curl_easy *data, if(connect_sessionid) { bool incache; + bool added = FALSE; void *ssl_sessionid; /* extract session ID to the allocated buffer */ @@ -1306,10 +1302,11 @@ gtls_connect_step3(struct Curl_easy *data, result = Curl_ssl_addsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, connect_sessionid, connect_idsize, - sockindex); + sockindex, &added); Curl_ssl_sessionid_unlock(data); - if(result) { + if(!added) free(connect_sessionid); + if(result) { result = CURLE_OUT_OF_MEMORY; } } @@ -1354,9 +1351,13 @@ gtls_connect_common(struct Curl_easy *data, /* Finish connecting once the handshake is done */ if(ssl_connect_1 == connssl->connecting_state) { - rc = gtls_connect_step3(data, conn, sockindex); + struct ssl_backend_data *backend = connssl->backend; + gnutls_session_t session = backend->session; + rc = Curl_gtls_verifyserver(data, conn, session, sockindex); if(rc) return rc; + conn->recv[sockindex] = gtls_recv; + conn->send[sockindex] = gtls_send; } *done = ssl_connect_1 == connssl->connecting_state; diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index 1a146a3..642d5f0 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,7 +27,11 @@ #ifdef USE_GNUTLS #include "urldata.h" - +#include <gnutls/gnutls.h> +CURLcode +Curl_gtls_verifyserver(struct Curl_easy *data, struct connectdata *conn, + gnutls_session_t session, + int sockindex); extern const struct Curl_ssl Curl_ssl_gnutls; #endif /* USE_GNUTLS */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 780d13e..1d209b2 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> * * This software is licensed as described in the file COPYING, which @@ -270,7 +270,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char * const ssl_capath = SSL_CONN_CONFIG(CApath); char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); @@ -316,16 +319,34 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, /* Load the trusted CA */ mbedtls_x509_crt_init(&backend->cacert); - if(ssl_cafile) { + if(ca_info_blob && verifypeer) { + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null + terminated even when provided the exact length, forcing us to waste + extra memory here. */ + unsigned char *newblob = malloc(ca_info_blob->len + 1); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + memcpy(newblob, ca_info_blob->data, ca_info_blob->len); + newblob[ca_info_blob->len] = 0; /* null terminate */ + ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, + ca_info_blob->len + 1); + free(newblob); + if(ret<0) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", + -ret, errorbuf); + return ret; + } + } + + if(ssl_cafile && verifypeer) { ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); if(ret<0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", ssl_cafile, -ret, errorbuf); - - if(verifypeer) - return CURLE_SSL_CACERT_BADFILE; + return CURLE_SSL_CACERT_BADFILE; } } @@ -358,10 +379,17 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, } if(ssl_cert_blob) { - const unsigned char *blob_data = - (const unsigned char *)ssl_cert_blob->data; - ret = mbedtls_x509_crt_parse(&backend->clicert, blob_data, + /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null + terminated even when provided the exact length, forcing us to waste + extra memory here. */ + unsigned char *newblob = malloc(ssl_cert_blob->len + 1); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len); + newblob[ssl_cert_blob->len] = 0; /* null terminate */ + ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, ssl_cert_blob->len); + free(newblob); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); @@ -671,7 +699,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, mbedtls_x509_crt *p = NULL; unsigned char *pubkey = NULL; -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#if MBEDTLS_VERSION_NUMBER == 0x03000000 if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) { #else @@ -698,7 +726,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der needs a non-const key, for now. https://github.com/ARMmbed/mbedtls/issues/396 */ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#if MBEDTLS_VERSION_NUMBER == 0x03000000 if(mbedtls_x509_crt_parse_der(p, peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) { @@ -710,7 +738,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, goto pinnedpubkey_error; } -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#if MBEDTLS_VERSION_NUMBER == 0x03000000 size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey, PUB_DER_MAX_BYTES); #else @@ -784,6 +812,7 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, mbedtls_ssl_session *our_ssl_sessionid; void *old_ssl_sessionid = NULL; bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; + bool added = FALSE; our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); if(!our_ssl_sessionid) @@ -807,11 +836,13 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, Curl_ssl_delsessionid(data, old_ssl_sessionid); retcode = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, - 0, sockindex); + 0, sockindex, &added); Curl_ssl_sessionid_unlock(data); - if(retcode) { + if(!added) { mbedtls_ssl_session_free(our_ssl_sessionid); free(our_ssl_sessionid); + } + if(retcode) { failf(data, "failed to store ssl session"); return retcode; } @@ -1151,6 +1182,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */ SSLSUPP_CA_PATH | + SSLSUPP_CAINFO_BLOB | SSLSUPP_PINNEDPUBKEY | SSLSUPP_SSL_CTX, diff --git a/Utilities/cmcurl/lib/vtls/mesalink.c b/Utilities/cmcurl/lib/vtls/mesalink.c index 3db9184..35a9165 100644 --- a/Utilities/cmcurl/lib/vtls/mesalink.c +++ b/Utilities/cmcurl/lib/vtls/mesalink.c @@ -68,8 +68,6 @@ struct ssl_backend_data SSL *handle; }; -#define BACKEND connssl->backend - static Curl_recv mesalink_recv; static Curl_send mesalink_send; @@ -100,9 +98,9 @@ mesalink_connect_step1(struct Curl_easy *data, #endif const char * const hostname = SSL_HOST_NAME(); size_t hostname_len = strlen(hostname); - SSL_METHOD *req_method = NULL; curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_backend_data *backend = connssl->backend; if(connssl->state == ssl_connection_complete) return CURLE_OK; @@ -139,22 +137,22 @@ mesalink_connect_step1(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(BACKEND->ctx) - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = SSL_CTX_new(req_method); + if(backend->ctx) + SSL_CTX_free(backend->ctx); + backend->ctx = SSL_CTX_new(req_method); - if(!BACKEND->ctx) { + if(!backend->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } SSL_CTX_set_verify( - BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ? + backend->ctx, SSL_CONN_CONFIG(verifypeer) ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) { - if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile), - SSL_CONN_CONFIG(CApath))) { + if(!SSL_CTX_load_verify_locations(backend->ctx, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(CApath))) { if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "error setting certificate verify locations: " @@ -181,7 +179,7 @@ mesalink_connect_step1(struct Curl_easy *data, if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) { int file_type = do_file_type(SSL_SET_OPTION(cert_type)); - if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx, + if(SSL_CTX_use_certificate_chain_file(backend->ctx, SSL_SET_OPTION(primary.clientcert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" @@ -190,8 +188,8 @@ mesalink_connect_step1(struct Curl_easy *data, } file_type = do_file_type(SSL_SET_OPTION(key_type)); - if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), - file_type) != 1) { + if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key), + file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } @@ -204,7 +202,7 @@ mesalink_connect_step1(struct Curl_easy *data, ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { #ifdef MESALINK_HAVE_CIPHER - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -212,10 +210,10 @@ mesalink_connect_step1(struct Curl_easy *data, infof(data, "Cipher selection: %s", ciphers); } - if(BACKEND->handle) - SSL_free(BACKEND->handle); - BACKEND->handle = SSL_new(BACKEND->ctx); - if(!BACKEND->handle) { + if(backend->handle) + SSL_free(backend->handle); + backend->handle = SSL_new(backend->ctx); + if(!backend->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } @@ -227,7 +225,7 @@ mesalink_connect_step1(struct Curl_easy *data, #endif ) { /* hostname is not a valid IP address */ - if(SSL_set_tlsext_host_name(BACKEND->handle, hostname) != SSL_SUCCESS) { + if(SSL_set_tlsext_host_name(backend->handle, hostname) != SSL_SUCCESS) { failf(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); @@ -244,7 +242,7 @@ mesalink_connect_step1(struct Curl_easy *data, || strncmp(hostname, "[::1]", 5) == 0 #endif ) { - SSL_set_tlsext_host_name(BACKEND->handle, "localhost"); + SSL_set_tlsext_host_name(backend->handle, "localhost"); } else #endif @@ -264,12 +262,12 @@ mesalink_connect_step1(struct Curl_easy *data, SSL_IS_PROXY() ? TRUE : FALSE, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ - if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { + if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(data); failf( data, "SSL: SSL_set_session failed: %s", - ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer)); + ERR_error_string(SSL_get_error(backend->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ @@ -279,7 +277,7 @@ mesalink_connect_step1(struct Curl_easy *data, } #endif /* MESALINK_HAVE_SESSION */ - if(SSL_set_fd(BACKEND->handle, (int)sockfd) != SSL_SUCCESS) { + if(SSL_set_fd(backend->handle, (int)sockfd) != SSL_SUCCESS) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -294,13 +292,14 @@ mesalink_connect_step2(struct Curl_easy *data, { int ret = -1; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; conn->recv[sockindex] = mesalink_recv; conn->send[sockindex] = mesalink_send; - ret = SSL_connect(BACKEND->handle); + ret = SSL_connect(backend->handle); if(ret != SSL_SUCCESS) { - int detail = SSL_get_error(BACKEND->handle, ret); + int detail = SSL_get_error(backend->handle, ret); if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -327,8 +326,8 @@ mesalink_connect_step2(struct Curl_easy *data, connssl->connecting_state = ssl_connect_3; infof(data, "SSL connection using %s / %s", - SSL_get_version(BACKEND->handle), - SSL_get_cipher_name(BACKEND->handle)); + SSL_get_version(backend->handle), + SSL_get_cipher_name(backend->handle)); return CURLE_OK; } @@ -347,8 +346,9 @@ mesalink_connect_step3(struct connectdata *conn, int sockindex) SSL_SESSION *our_ssl_sessionid; void *old_ssl_sessionid = NULL; bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; + struct ssl_backend_data *backend = connssl->backend; - our_ssl_sessionid = SSL_get_session(BACKEND->handle); + our_ssl_sessionid = SSL_get_session(backend->handle); Curl_ssl_sessionid_lock(data); incache = @@ -365,7 +365,7 @@ mesalink_connect_step3(struct connectdata *conn, int sockindex) if(!incache) { result = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0, - sockindex); + sockindex, NULL); if(result) { Curl_ssl_sessionid_unlock(data); failf(data, "failed to store ssl session"); @@ -387,12 +387,13 @@ mesalink_send(struct Curl_easy *data, int sockindex, const void *mem, { struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; char error_buffer[MESALINK_MAX_ERROR_SZ]; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - int rc = SSL_write(BACKEND->handle, mem, memlen); + int rc = SSL_write(backend->handle, mem, memlen); if(rc < 0) { - int err = SSL_get_error(BACKEND->handle, rc); + int err = SSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: @@ -415,17 +416,18 @@ static void mesalink_close(struct Curl_easy *data, struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; (void) data; - if(BACKEND->handle) { - (void)SSL_shutdown(BACKEND->handle); - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + if(backend->handle) { + (void)SSL_shutdown(backend->handle); + SSL_free(backend->handle); + backend->handle = NULL; } - if(BACKEND->ctx) { - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = NULL; + if(backend->ctx) { + SSL_CTX_free(backend->ctx); + backend->ctx = NULL; } } @@ -435,12 +437,13 @@ mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize, { struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; char error_buffer[MESALINK_MAX_ERROR_SZ]; int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - int nread = SSL_read(BACKEND->handle, buf, buffsize); + int nread = SSL_read(backend->handle, buf, buffsize); if(nread <= 0) { - int err = SSL_get_error(BACKEND->handle, nread); + int err = SSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ @@ -485,12 +488,13 @@ mesalink_shutdown(struct Curl_easy *data, { int retval = 0; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; (void) data; - if(BACKEND->handle) { - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + if(backend->handle) { + SSL_free(backend->handle); + backend->handle = NULL; } return retval; } @@ -636,8 +640,9 @@ static void * mesalink_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return BACKEND->handle; + return backend->handle; } const struct Curl_ssl Curl_ssl_mesalink = { diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index cf65789..2b44f05 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -304,13 +304,14 @@ static char *nss_sslver_to_name(PRUint16 nssver) } } -static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model, - char *cipher_list) +/* the longest cipher name this supports */ +#define MAX_CIPHER_LENGTH 128 + +static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model, + const char *cipher_list) { unsigned int i; - PRBool cipher_state[NUM_OF_CIPHERS]; - PRBool found; - char *cipher; + const char *cipher; /* use accessors to avoid dynamic linking issues after an update of NSS */ const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); @@ -326,51 +327,52 @@ static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model, SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); } - /* Set every entry in our list to false */ - for(i = 0; i < NUM_OF_CIPHERS; i++) { - cipher_state[i] = PR_FALSE; - } - cipher = cipher_list; - while(cipher_list && (cipher_list[0])) { + while(cipher && cipher[0]) { + const char *end; + char name[MAX_CIPHER_LENGTH + 1]; + size_t len; + bool found = FALSE; while((*cipher) && (ISSPACE(*cipher))) ++cipher; - cipher_list = strpbrk(cipher, ":, "); - if(cipher_list) { - *cipher_list++ = '\0'; - } - - found = PR_FALSE; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - if(strcasecompare(cipher, cipherlist[i].name)) { - cipher_state[i] = PR_TRUE; - found = PR_TRUE; - break; - } - } + end = strpbrk(cipher, ":, "); + if(end) + len = end - cipher; + else + len = strlen(cipher); - if(found == PR_FALSE) { - failf(data, "Unknown cipher in list: %s", cipher); + if(len > MAX_CIPHER_LENGTH) { + failf(data, "Bad cipher list"); return SECFailure; } - - if(cipher_list) { - cipher = cipher_list; + else if(len) { + memcpy(name, cipher, len); + name[len] = 0; + + for(i = 0; i<NUM_OF_CIPHERS; i++) { + if(strcasecompare(name, cipherlist[i].name)) { + /* Enable the selected cipher */ + if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != + SECSuccess) { + failf(data, "cipher-suite not supported by NSS: %s", name); + return SECFailure; + } + found = TRUE; + break; + } + } } - } - - /* Finally actually enable the selected ciphers */ - for(i = 0; i<NUM_OF_CIPHERS; i++) { - if(!cipher_state[i]) - continue; - if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != SECSuccess) { - failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name); + if(!found && len) { + failf(data, "Unknown cipher: %s", name); return SECFailure; } + if(end) + cipher = ++end; + else + break; } return SECSuccess; @@ -782,7 +784,7 @@ static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) { (void)slot; /* unused */ - if(retry || NULL == arg) + if(retry || !arg) return NULL; else return (char *)PORT_Strdup((char *)arg); @@ -955,7 +957,7 @@ static void display_cert_info(struct Curl_easy *data, subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); common_name = CERT_GetCommonName(&cert->subject); - infof(data, "subject: %s\n", subject); + infof(data, "subject: %s", subject); CERT_GetCertTimes(cert, ¬Before, ¬After); PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); @@ -1168,7 +1170,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct SECKEYPrivateKeyStr *key; PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); - if(NULL == slot) { + if(!slot) { failf(data, "NSS: PK11 slot not found: %s", pem_slotname); return SECFailure; } @@ -1182,7 +1184,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); SECITEM_FreeItem(&cert_der, PR_FALSE); - if(NULL == cert) { + if(!cert) { failf(data, "NSS: client certificate from file not found"); PK11_FreeSlot(slot); return SECFailure; @@ -1190,7 +1192,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); PK11_FreeSlot(slot); - if(NULL == key) { + if(!key) { failf(data, "NSS: private key from file not found"); CERT_DestroyCertificate(cert); return SECFailure; @@ -1207,9 +1209,9 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, /* use the default NSS hook */ if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, pRetCert, pRetKey) - || NULL == *pRetCert) { + || !*pRetCert) { - if(NULL == nickname) + if(!nickname) failf(data, "NSS: client certificate not found (nickname not " "specified)"); else @@ -1220,7 +1222,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, /* get certificate nickname if any */ nickname = (*pRetCert)->nickname; - if(NULL == nickname) + if(!nickname) nickname = "[unknown]"; if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { @@ -1229,7 +1231,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECFailure; } - if(NULL == *pRetKey) { + if(!*pRetKey) { failf(data, "NSS: private key not found for certificate: %s", nickname); return SECFailure; } @@ -1344,7 +1346,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) PRErrorCode err; const char *err_name; - if(nss_context != NULL) + if(nss_context) return CURLE_OK; memset((void *) &initparams, '\0', sizeof(initparams)); @@ -1360,7 +1362,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); free(certpath); - if(nss_context != NULL) + if(nss_context) return CURLE_OK; err = PR_GetError(); @@ -1372,7 +1374,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); - if(nss_context != NULL) + if(nss_context) return CURLE_OK; err = PR_GetError(); @@ -2250,10 +2252,11 @@ static CURLcode nss_connect_common(struct Curl_easy *data, case CURLE_OK: break; case CURLE_AGAIN: + /* CURLE_AGAIN in non-blocking mode is not an error */ if(!blocking) - /* CURLE_AGAIN in non-blocking mode is not an error */ return CURLE_OK; - /* FALLTHROUGH */ + else + return result; default: return result; } diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 87f4b02..f836c63 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -171,6 +171,21 @@ #define OPENSSL_load_builtin_modules(x) #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#define HAVE_EVP_PKEY_GET_PARAMS 1 +#else +#define SSL_get1_peer_certificate SSL_get_peer_certificate +#endif + +#ifdef HAVE_EVP_PKEY_GET_PARAMS +#include <openssl/core_names.h> +#define DECLARE_PKEY_PARAM_BIGNUM(name) BIGNUM *name = NULL +#define FREE_PKEY_PARAM_BIGNUM(name) BN_clear_free(name) +#else +#define DECLARE_PKEY_PARAM_BIGNUM(name) const BIGNUM *name +#define FREE_PKEY_PARAM_BIGNUM(name) +#endif + /* * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 @@ -227,6 +242,17 @@ #endif #endif +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#define HAVE_RANDOM_INIT_BY_DEFAULT 1 +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \ + !defined(OPENSSL_IS_BORINGSSL) +#define HAVE_OPENSSL_VERSION +#endif + struct ssl_backend_data { struct Curl_easy *logger; /* transfer handle to pass trace logs to, only using sockindex 0 */ @@ -435,18 +461,21 @@ static bool rand_enough(void) static CURLcode ossl_seed(struct Curl_easy *data) { - char fname[256]; - /* This might get called before it has been added to a multi handle */ if(data->multi && data->multi->ssl_seeded) return CURLE_OK; if(rand_enough()) { - /* OpenSSL 1.1.0+ will return here */ + /* OpenSSL 1.1.0+ should return here */ if(data->multi) data->multi->ssl_seeded = TRUE; return CURLE_OK; } +#ifdef HAVE_RANDOM_INIT_BY_DEFAULT + /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */ + failf(data, "Insufficient randomness"); + return CURLE_SSL_CONNECT_ERROR; +#else #ifndef RANDOM_FILE /* if RANDOM_FILE isn't defined, we only perform this if an option tells @@ -507,19 +536,23 @@ static CURLcode ossl_seed(struct Curl_easy *data) RAND_add(randb, (int)len, (double)len/2); } while(!rand_enough()); - /* generates a default path for the random seed file */ - fname[0] = 0; /* blank it first */ - RAND_file_name(fname, sizeof(fname)); - if(fname[0]) { - /* we got a file name to try */ - RAND_load_file(fname, RAND_LOAD_LENGTH); - if(rand_enough()) - return CURLE_OK; + { + /* generates a default path for the random seed file */ + char fname[256]; + fname[0] = 0; /* blank it first */ + RAND_file_name(fname, sizeof(fname)); + if(fname[0]) { + /* we got a file name to try */ + RAND_load_file(fname, RAND_LOAD_LENGTH); + if(rand_enough()) + return CURLE_OK; + } } infof(data, "libcurl is now using a weak random seed!"); return (rand_enough() ? CURLE_OK : - CURLE_SSL_CONNECT_ERROR /* confusing error code */); + CURLE_SSL_CONNECT_ERROR /* confusing error code */); +#endif } #ifndef SSL_FILETYPE_ENGINE @@ -1088,7 +1121,8 @@ int cert_stuff(struct Curl_easy *data, EVP_PKEY_free(pktmp); } -#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) { /* If RSA is used, don't check the private key if its flags indicate * it doesn't support it. */ @@ -1401,6 +1435,12 @@ static void ossl_closeone(struct Curl_easy *data, if(backend->handle) { char buf[32]; set_logger(conn, data); + /* + * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make + * sure the socket is not closed before calling OpenSSL functions that + * will use it. + */ + DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD); /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ @@ -1639,9 +1679,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI. + This function is now used from ngtcp2 (QUIC) as well. */ -static CURLcode verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -1926,7 +1967,7 @@ static CURLcode verifystatus(struct Curl_easy *data, } /* Compute the certificate's ID */ - cert = SSL_get_peer_certificate(backend->handle); + cert = SSL_get1_peer_certificate(backend->handle); if(!cert) { failf(data, "Error getting peer certificate"); result = CURLE_SSL_INVALIDCERTSTATUS; @@ -2493,6 +2534,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(SSL_SET_OPTION(primary.sessionid)) { bool incache; + bool added = FALSE; void *old_ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); @@ -2511,9 +2553,11 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(!incache) { if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, - 0 /* unknown size */, sockindex)) { - /* the session has been put into the session cache */ - res = 1; + 0 /* unknown size */, sockindex, &added)) { + if(added) { + /* the session has been put into the session cache */ + res = 1; + } } else failf(data, "failed to store ssl session"); @@ -2936,7 +2980,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, NULL, cert_name, sizeof(cert_name))) { strcpy(cert_name, "Unknown"); } - infof(data, "SSL: Checking cert %s\"\n", cert_name); + infof(data, "SSL: Checking cert \"%s\"", cert_name); #endif encoded_cert = (const unsigned char *)pContext->pbCertEncoded; @@ -3052,60 +3096,36 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } } + if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - { - if(ssl_cafile) { - if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { - if(verifypeer && !imported_native_ca) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - /* Continue with warning if certificate verification isn't required. */ - infof(data, "error setting certificate file, continuing anyway"); - } - infof(data, " CAfile: %s", ssl_cafile); + if(ssl_cafile && + !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; } - if(ssl_capath) { - if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { - if(verifypeer && !imported_native_ca) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } - /* Continue with warning if certificate verification isn't required. */ - infof(data, "error setting certificate path, continuing anyway"); - } - infof(data, " CApath: %s", ssl_capath); + if(ssl_capath && + !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; } - } #else - if(ssl_cafile || ssl_capath) { - /* tell SSL where to find CA certificates that are used to verify - the server's certificate. */ + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { - if(verifypeer && !imported_native_ca) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - /* Just continue with a warning if no strict certificate verification - is required. */ - infof(data, "error setting certificate verify locations," - " continuing anyway:"); - } - else { - /* Everything is fine. */ - infof(data, "successfully set certificate verify locations:"); + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; } +#endif infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); } -#endif #ifdef CURL_CA_FALLBACK if(verifypeer && @@ -3476,10 +3496,7 @@ static void pubkey_show(struct Curl_easy *data, int num, const char *type, const char *name, -#ifdef HAVE_OPAQUE_RSA_DSA_DH - const -#endif - BIGNUM *bn) + const BIGNUM *bn) { char *ptr; char namebuf[32]; @@ -3544,12 +3561,6 @@ typedef size_t numcert_t; typedef int numcert_t; #endif -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) -#define OSSL3_CONST const -#else -#define OSSL3_CONST -#endif - static CURLcode get_cert_chain(struct Curl_easy *data, struct ssl_connect_data *connssl) { @@ -3573,6 +3584,9 @@ static CURLcode get_cert_chain(struct Curl_easy *data, } mem = BIO_new(BIO_s_mem()); + if(!mem) { + return CURLE_OUT_OF_MEMORY; + } for(i = 0; i < (int)numcerts; i++) { ASN1_INTEGER *num; @@ -3657,92 +3671,115 @@ static CURLcode get_cert_chain(struct Curl_easy *data, switch(pktype) { case EVP_PKEY_RSA: { - OSSL3_CONST RSA *rsa; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + RSA *rsa; #ifdef HAVE_OPAQUE_EVP_PKEY rsa = EVP_PKEY_get0_RSA(pubkey); #else rsa = pubkey->pkey.rsa; -#endif +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ -#ifdef HAVE_OPAQUE_RSA_DSA_DH { - const BIGNUM *n; - const BIGNUM *e; - +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(n); + DECLARE_PKEY_PARAM_BIGNUM(e); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e); +#else RSA_get0_key(rsa, &n, &e, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ BIO_printf(mem, "%d", BN_num_bits(n)); +#else + BIO_printf(mem, "%d", BN_num_bits(rsa->n)); +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ push_certinfo("RSA Public Key", i); print_pubkey_BN(rsa, n, i); print_pubkey_BN(rsa, e, i); + FREE_PKEY_PARAM_BIGNUM(n); + FREE_PKEY_PARAM_BIGNUM(e); } -#else - BIO_printf(mem, "%d", BN_num_bits(rsa->n)); - push_certinfo("RSA Public Key", i); - print_pubkey_BN(rsa, n, i); - print_pubkey_BN(rsa, e, i); -#endif break; } case EVP_PKEY_DSA: { #ifndef OPENSSL_NO_DSA - OSSL3_CONST DSA *dsa; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DSA *dsa; #ifdef HAVE_OPAQUE_EVP_PKEY dsa = EVP_PKEY_get0_DSA(pubkey); #else dsa = pubkey->pkey.dsa; -#endif -#ifdef HAVE_OPAQUE_RSA_DSA_DH +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ { - const BIGNUM *p; - const BIGNUM *q; - const BIGNUM *g; - const BIGNUM *pub_key; - +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else DSA_get0_pqg(dsa, &p, &q, &g); DSA_get0_key(dsa, &pub_key, NULL); - +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ print_pubkey_BN(dsa, p, i); print_pubkey_BN(dsa, q, i); print_pubkey_BN(dsa, g, i); print_pubkey_BN(dsa, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); } -#else - print_pubkey_BN(dsa, p, i); - print_pubkey_BN(dsa, q, i); - print_pubkey_BN(dsa, g, i); - print_pubkey_BN(dsa, pub_key, i); -#endif #endif /* !OPENSSL_NO_DSA */ break; } case EVP_PKEY_DH: { - OSSL3_CONST DH *dh; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DH *dh; #ifdef HAVE_OPAQUE_EVP_PKEY dh = EVP_PKEY_get0_DH(pubkey); #else dh = pubkey->pkey.dh; -#endif -#ifdef HAVE_OPAQUE_RSA_DSA_DH +#endif /* HAVE_OPAQUE_EVP_PKEY */ +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ { - const BIGNUM *p; - const BIGNUM *q; - const BIGNUM *g; - const BIGNUM *pub_key; +#ifdef HAVE_OPAQUE_RSA_DSA_DH + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else DH_get0_pqg(dh, &p, &q, &g); DH_get0_key(dh, &pub_key, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ print_pubkey_BN(dh, p, i); print_pubkey_BN(dh, q, i); print_pubkey_BN(dh, g, i); +#else + print_pubkey_BN(dh, p, i); + print_pubkey_BN(dh, g, i); +#endif /* HAVE_OPAQUE_RSA_DSA_DH */ print_pubkey_BN(dh, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); } -#else - print_pubkey_BN(dh, p, i); - print_pubkey_BN(dh, g, i); - print_pubkey_BN(dh, pub_key, i); -#endif break; } } @@ -3846,11 +3883,20 @@ static CURLcode servercert(struct Curl_easy *data, BIO *mem = BIO_new(BIO_s_mem()); struct ssl_backend_data *backend = connssl->backend; + if(!mem) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return CURLE_OUT_OF_MEMORY; + } + if(data->set.ssl.certinfo) /* we've been asked to gather certificate info! */ (void)get_cert_chain(data, connssl); - backend->server_cert = SSL_get_peer_certificate(backend->handle); + backend->server_cert = SSL_get1_peer_certificate(backend->handle); if(!backend->server_cert) { BIO_free(mem); if(!strict) @@ -3884,7 +3930,7 @@ static CURLcode servercert(struct Curl_easy *data, BIO_free(mem); if(SSL_CONN_CONFIG(verifyhost)) { - result = verifyhost(data, conn, backend->server_cert); + result = Curl_ossl_verifyhost(data, conn, backend->server_cert); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4364,13 +4410,7 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ static size_t ossl_version(char *buffer, size_t size) { #ifdef LIBRESSL_VERSION_NUMBER -#if LIBRESSL_VERSION_NUMBER < 0x2070100fL - return msnprintf(buffer, size, "%s/%lx.%lx.%lx", - OSSL_PACKAGE, - (LIBRESSL_VERSION_NUMBER>>28)&0xf, - (LIBRESSL_VERSION_NUMBER>>20)&0xff, - (LIBRESSL_VERSION_NUMBER>>12)&0xff); -#else /* OpenSSL_version() first appeared in LibreSSL 2.7.1 */ +#ifdef HAVE_OPENSSL_VERSION char *p; int count; const char *ver = OpenSSL_version(OPENSSL_VERSION); @@ -4384,6 +4424,12 @@ static size_t ossl_version(char *buffer, size_t size) *p = '_'; } return count; +#else + return msnprintf(buffer, size, "%s/%lx.%lx.%lx", + OSSL_PACKAGE, + (LIBRESSL_VERSION_NUMBER>>28)&0xf, + (LIBRESSL_VERSION_NUMBER>>20)&0xff, + (LIBRESSL_VERSION_NUMBER>>12)&0xff); #endif #elif defined(OPENSSL_IS_BORINGSSL) return msnprintf(buffer, size, OSSL_PACKAGE); diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index 2f6e1b2..2805845 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,11 +26,15 @@ #ifdef USE_OPENSSL /* - * This header should only be needed to get included by vtls.c and openssl.c + * This header should only be needed to get included by vtls.c, openssl.c + * and ngtcp2.c */ +#include <openssl/x509v3.h> #include "urldata.h" +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; #endif /* USE_OPENSSL */ diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index 2ac97ce..6dbb1ef 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -27,7 +27,7 @@ #include "curl_printf.h" #include <errno.h> -#include <crustls.h> +#include <rustls.h> #include "inet_pton.h" #include "urldata.h" @@ -138,11 +138,6 @@ cr_recv(struct Curl_easy *data, int sockindex, *err = CURLE_READ_ERROR; return -1; } - else if(tls_bytes_read == 0) { - failf(data, "connection closed without TLS close_notify alert"); - *err = CURLE_READ_ERROR; - return -1; - } infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read); @@ -161,22 +156,21 @@ cr_recv(struct Curl_easy *data, int sockindex, (uint8_t *)plainbuf + plain_bytes_copied, plainlen - plain_bytes_copied, &n); - if(rresult == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) { - *err = CURLE_OK; - return 0; + if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { + infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later."); + backend->data_pending = FALSE; + break; } else if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_connection_read"); + /* n always equals 0 in this case, don't need to check it */ + failf(data, "error in rustls_connection_read: %d", rresult); *err = CURLE_READ_ERROR; return -1; } else if(n == 0) { - /* rustls returns 0 from connection_read to mean "all currently - available data has been read." If we bring in more ciphertext with - read_tls, more plaintext will become available. So don't tell curl - this is an EOF. Instead, say "come back later." */ - infof(data, "cr_recv got 0 bytes of plaintext"); - backend->data_pending = FALSE; + /* n == 0 indicates clean EOF, but we may have read some other + plaintext bytes before we reached this. Break out of the loop + so we can figure out whether to return success or EOF. */ break; } else { @@ -185,15 +179,23 @@ cr_recv(struct Curl_easy *data, int sockindex, } } - /* If we wrote out 0 plaintext bytes, it might just mean we haven't yet - read a full TLS record. Return CURLE_AGAIN so curl doesn't treat this - as EOF. */ - if(plain_bytes_copied == 0) { + if(plain_bytes_copied) { + *err = CURLE_OK; + return plain_bytes_copied; + } + + /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF, + OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY. + If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */ + if(!backend->data_pending) { *err = CURLE_AGAIN; return -1; } - return plain_bytes_copied; + /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP + connection was cleanly closed (with a close_notify alert). */ + *err = CURLE_OK; + return 0; } /* @@ -309,10 +311,10 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, config_builder = rustls_client_config_builder_new(); #ifdef USE_HTTP2 infof(data, "offering ALPN for HTTP/1.1 and HTTP/2"); - rustls_client_config_builder_set_protocols(config_builder, alpn, 2); + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2); #else infof(data, "offering ALPN for HTTP/1.1 only"); - rustls_client_config_builder_set_protocols(config_builder, alpn, 1); + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1); #endif if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( @@ -358,7 +360,7 @@ cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, size_t len = 0; rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - if(NULL == protocol) { + if(!protocol) { infof(data, "ALPN, server did not agree to a protocol"); return; } @@ -414,9 +416,6 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, /* * Connection has been established according to rustls. Set send/recv * handlers, and update the state machine. - * This check has to come last because is_handshaking starts out false, - * then becomes true when we first write data, then becomes false again - * once the handshake is done. */ if(!rustls_connection_is_handshaking(rconn)) { infof(data, "Done handshaking"); @@ -543,6 +542,12 @@ cr_close(struct Curl_easy *data, struct connectdata *conn, } } +static size_t cr_version(char *buffer, size_t size) +{ + struct rustls_str ver = rustls_version(); + return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); +} + const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, SSLSUPP_TLS13_CIPHERSUITES, /* supports */ @@ -550,7 +555,7 @@ const struct Curl_ssl Curl_ssl_rustls = { Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ - rustls_version, /* version */ + cr_version, /* version */ Curl_none_check_cxn, /* check_cxn */ Curl_none_shutdown, /* shutdown */ cr_data_pending, /* data_pending */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 722a937..0a8e606 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -147,8 +147,6 @@ #define ALG_CLASS_DHASH ALG_CLASS_HASH #endif -#define BACKEND connssl->backend - static Curl_recv schannel_recv; static Curl_send schannel_send; @@ -423,6 +421,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; + struct ssl_backend_data *backend = connssl->backend; /* setup Schannel API options */ memset(&schannel_cred, 0, sizeof(schannel_cred)); @@ -430,7 +429,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, if(conn->ssl_config.verifypeer) { #ifdef HAS_MANUAL_VERIFY_API - if(BACKEND->use_manual_cred_validation) + if(backend->use_manual_cred_validation) schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; else #endif @@ -503,7 +502,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, if(SSL_CONN_CONFIG(cipher_list)) { result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list), - BACKEND->algIds); + backend->algIds); if(CURLE_OK != result) { failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); return result; @@ -600,7 +599,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, datablob.pbData = (BYTE*)certdata; datablob.cbData = (DWORD)certsize; - if(data->set.ssl.key_passwd != NULL) + if(data->set.ssl.key_passwd) pwd_len = strlen(data->set.ssl.key_passwd); pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); if(pszPassword) { @@ -704,9 +703,9 @@ schannel_acquire_credential_handle(struct Curl_easy *data, #endif /* allocate memory for the re-usable credential handle */ - BACKEND->cred = (struct Curl_schannel_cred *) + backend->cred = (struct Curl_schannel_cred *) calloc(1, sizeof(struct Curl_schannel_cred)); - if(!BACKEND->cred) { + if(!backend->cred) { failf(data, "schannel: unable to allocate memory"); if(client_certs[0]) @@ -714,16 +713,14 @@ schannel_acquire_credential_handle(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - BACKEND->cred->refcount = 1; + backend->cred->refcount = 1; - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx - */ sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, - &BACKEND->cred->cred_handle, - &BACKEND->cred->time_stamp); + &backend->cred->cred_handle, + &backend->cred->time_stamp); if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); @@ -732,7 +729,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, char buffer[STRERROR_LEN]; failf(data, "schannel: AcquireCredentialsHandle failed: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - Curl_safefree(BACKEND->cred); + Curl_safefree(backend->cred); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: return CURLE_OUT_OF_MEMORY; @@ -771,12 +768,13 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, TCHAR *host_name; CURLcode result; char * const hostname = SSL_HOST_NAME(); + struct ssl_backend_data *backend = connssl->backend; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)", hostname, conn->remote_port)); - if(curlx_verify_windows_version(5, 1, PLATFORM_WINNT, + if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and algorithms that may not be supported by all servers. */ @@ -787,29 +785,29 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, #ifdef HAS_ALPN /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. Also it doesn't seem to be supported for Wine, see curl bug #983. */ - BACKEND->use_alpn = conn->bits.tls_enable_alpn && + backend->use_alpn = conn->bits.tls_enable_alpn && !GetProcAddress(GetModuleHandle(TEXT("ntdll")), "wine_get_version") && - curlx_verify_windows_version(6, 3, PLATFORM_WINNT, + curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL); #else - BACKEND->use_alpn = false; + backend->use_alpn = false; #endif #ifdef _WIN32_WCE #ifdef HAS_MANUAL_VERIFY_API /* certificate validation on CE doesn't seem to work right; we'll * do it following a more manual process. */ - BACKEND->use_manual_cred_validation = true; + backend->use_manual_cred_validation = true; #else #error "compiler too old to support requisite manual cert verify for Win CE" #endif #else #ifdef HAS_MANUAL_VERIFY_API if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { - if(curlx_verify_windows_version(6, 1, PLATFORM_WINNT, + if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { - BACKEND->use_manual_cred_validation = true; + backend->use_manual_cred_validation = true; } else { failf(data, "schannel: this version of Windows is too old to support " @@ -818,7 +816,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, } } else - BACKEND->use_manual_cred_validation = false; + backend->use_manual_cred_validation = false; #else if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { failf(data, "schannel: CA cert support not built in"); @@ -827,7 +825,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, #endif #endif - BACKEND->cred = NULL; + backend->cred = NULL; /* check for an existing re-usable credential handle */ if(SSL_SET_OPTION(primary.sessionid)) { @@ -835,19 +833,19 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, (void **)&old_cred, NULL, sockindex)) { - BACKEND->cred = old_cred; + backend->cred = old_cred; DEBUGF(infof(data, "schannel: re-using existing credential handle")); /* increment the reference counter of the credential/session handle */ - BACKEND->cred->refcount++; + backend->cred->refcount++; DEBUGF(infof(data, "schannel: incremented credential handle refcount = %d", - BACKEND->cred->refcount)); + backend->cred->refcount)); } Curl_ssl_sessionid_unlock(data); } - if(!BACKEND->cred) { + if(!backend->cred) { result = schannel_acquire_credential_handle(data, conn, sockindex); if(result != CURLE_OK) { return result; @@ -864,7 +862,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, } #ifdef HAS_ALPN - if(BACKEND->use_alpn) { + if(backend->use_alpn) { int cur = 0; int list_start_index = 0; unsigned int *extension_len = NULL; @@ -922,18 +920,18 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, InitSecBufferDesc(&outbuf_desc, &outbuf, 1); /* security request flags */ - BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; if(!SSL_SET_OPTION(auto_client_cert)) { - BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; } /* allocate memory for the security context handle */ - BACKEND->ctxt = (struct Curl_schannel_ctxt *) + backend->ctxt = (struct Curl_schannel_ctxt *) calloc(1, sizeof(struct Curl_schannel_ctxt)); - if(!BACKEND->ctxt) { + if(!backend->ctxt) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } @@ -950,16 +948,16 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, us problems with inbuf regardless. https://github.com/curl/curl/issues/983 */ sspi_status = s_pSecFn->InitializeSecurityContext( - &BACKEND->cred->cred_handle, NULL, host_name, BACKEND->req_flags, 0, 0, - (BACKEND->use_alpn ? &inbuf_desc : NULL), - 0, &BACKEND->ctxt->ctxt_handle, - &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); + &backend->cred->cred_handle, NULL, host_name, backend->req_flags, 0, 0, + (backend->use_alpn ? &inbuf_desc : NULL), + 0, &backend->ctxt->ctxt_handle, + &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); curlx_unicodefree(host_name); if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; - Curl_safefree(BACKEND->ctxt); + Curl_safefree(backend->ctxt); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: failf(data, "schannel: initial InitializeSecurityContext failed: %s", @@ -1003,10 +1001,10 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, DEBUGF(infof(data, "schannel: sent initial handshake data: " "sent %zd bytes", written)); - BACKEND->recv_unrecoverable_err = CURLE_OK; - BACKEND->recv_sspi_close_notify = false; - BACKEND->recv_connection_closed = false; - BACKEND->encdata_is_incomplete = false; + backend->recv_unrecoverable_err = CURLE_OK; + backend->recv_sspi_close_notify = false; + backend->recv_connection_closed = false; + backend->encdata_is_incomplete = false; /* continue to second handshake step */ connssl->connecting_state = ssl_connect_2; @@ -1031,6 +1029,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, bool doread; char * const hostname = SSL_HOST_NAME(); const char *pubkey_ptr; + struct ssl_backend_data *backend = connssl->backend; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; @@ -1038,39 +1037,39 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, "schannel: SSL/TLS connection with %s port %hu (step 2/3)", hostname, conn->remote_port)); - if(!BACKEND->cred || !BACKEND->ctxt) + if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; /* buffer to store previously received and decrypted data */ - if(!BACKEND->decdata_buffer) { - BACKEND->decdata_offset = 0; - BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - BACKEND->decdata_buffer = malloc(BACKEND->decdata_length); - if(!BACKEND->decdata_buffer) { + if(!backend->decdata_buffer) { + backend->decdata_offset = 0; + backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->decdata_buffer = malloc(backend->decdata_length); + if(!backend->decdata_buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } /* buffer to store previously received and encrypted data */ - if(!BACKEND->encdata_buffer) { - BACKEND->encdata_is_incomplete = false; - BACKEND->encdata_offset = 0; - BACKEND->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - BACKEND->encdata_buffer = malloc(BACKEND->encdata_length); - if(!BACKEND->encdata_buffer) { + if(!backend->encdata_buffer) { + backend->encdata_is_incomplete = false; + backend->encdata_offset = 0; + backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; + backend->encdata_buffer = malloc(backend->encdata_length); + if(!backend->encdata_buffer) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } } /* if we need a bigger buffer to read a full message, increase buffer now */ - if(BACKEND->encdata_length - BACKEND->encdata_offset < + if(backend->encdata_length - backend->encdata_offset < CURL_SCHANNEL_BUFFER_FREE_SIZE) { /* increase internal encrypted data buffer */ - size_t reallocated_length = BACKEND->encdata_offset + + size_t reallocated_length = backend->encdata_offset + CURL_SCHANNEL_BUFFER_FREE_SIZE; - reallocated_buffer = realloc(BACKEND->encdata_buffer, + reallocated_buffer = realloc(backend->encdata_buffer, reallocated_length); if(!reallocated_buffer) { @@ -1078,8 +1077,8 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } else { - BACKEND->encdata_buffer = reallocated_buffer; - BACKEND->encdata_length = reallocated_length; + backend->encdata_buffer = reallocated_buffer; + backend->encdata_length = reallocated_length; } } @@ -1088,10 +1087,10 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, if(doread) { /* read encrypted handshake data from socket */ result = Curl_read_plain(conn->sock[sockindex], - (char *) (BACKEND->encdata_buffer + - BACKEND->encdata_offset), - BACKEND->encdata_length - - BACKEND->encdata_offset, + (char *) (backend->encdata_buffer + + backend->encdata_offset), + backend->encdata_length - + backend->encdata_offset, &nread); if(result == CURLE_AGAIN) { if(connssl->connecting_state != ssl_connect_2_writing) @@ -1107,18 +1106,18 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } /* increase encrypted data buffer offset */ - BACKEND->encdata_offset += nread; - BACKEND->encdata_is_incomplete = false; + backend->encdata_offset += nread; + backend->encdata_is_incomplete = false; DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); } DEBUGF(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - BACKEND->encdata_offset, BACKEND->encdata_length)); + backend->encdata_offset, backend->encdata_length)); /* setup input buffers */ - InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset), - curlx_uztoul(BACKEND->encdata_offset)); + InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), + curlx_uztoul(backend->encdata_offset)); InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, inbuf, 2); @@ -1134,19 +1133,17 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } /* copy received handshake data into input buffer */ - memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer, - BACKEND->encdata_offset); + memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, + backend->encdata_offset); host_name = curlx_convert_UTF8_to_tchar(hostname); if(!host_name) return CURLE_OUT_OF_MEMORY; - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - */ sspi_status = s_pSecFn->InitializeSecurityContext( - &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle, - host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL, - &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); + &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, + host_name, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL, + &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); curlx_unicodefree(host_name); @@ -1155,7 +1152,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, /* check if the handshake was incomplete */ if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - BACKEND->encdata_is_incomplete = true; + backend->encdata_is_incomplete = true; connssl->connecting_state = ssl_connect_2_reading; DEBUGF(infof(data, "schannel: received incomplete message, need more data")); @@ -1166,8 +1163,8 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, the handshake without one. This will allow connections to servers which request a client certificate but do not require it. */ if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && - !(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { - BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { + backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; connssl->connecting_state = ssl_connect_2_writing; DEBUGF(infof(data, "schannel: a client certificate has been requested")); @@ -1195,7 +1192,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } /* free obsolete buffer */ - if(outbuf[i].pvBuffer != NULL) { + if(outbuf[i].pvBuffer) { s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); } } @@ -1249,11 +1246,11 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, */ /* check if the remaining data is less than the total amount and therefore begins after the already processed data */ - if(BACKEND->encdata_offset > inbuf[1].cbBuffer) { - memmove(BACKEND->encdata_buffer, - (BACKEND->encdata_buffer + BACKEND->encdata_offset) - + if(backend->encdata_offset > inbuf[1].cbBuffer) { + memmove(backend->encdata_buffer, + (backend->encdata_buffer + backend->encdata_offset) - inbuf[1].cbBuffer, inbuf[1].cbBuffer); - BACKEND->encdata_offset = inbuf[1].cbBuffer; + backend->encdata_offset = inbuf[1].cbBuffer; if(sspi_status == SEC_I_CONTINUE_NEEDED) { doread = FALSE; continue; @@ -1261,7 +1258,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } } else { - BACKEND->encdata_offset = 0; + backend->encdata_offset = 0; } break; } @@ -1288,7 +1285,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } #ifdef HAS_MANUAL_VERIFY_API - if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) { + if(conn->ssl_config.verifypeer && backend->use_manual_cred_validation) { return Curl_verify_certificate(data, conn, sockindex); } #endif @@ -1370,6 +1367,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, #ifdef HAS_ALPN SecPkgContext_ApplicationProtocol alpn_result; #endif + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -1377,28 +1375,28 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, "schannel: SSL/TLS connection with %s port %hu (step 3/3)", hostname, conn->remote_port)); - if(!BACKEND->cred) + if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; /* check if the required context attributes are met */ - if(BACKEND->ret_flags != BACKEND->req_flags) { - if(!(BACKEND->ret_flags & ISC_RET_SEQUENCE_DETECT)) + if(backend->ret_flags != backend->req_flags) { + if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT)) failf(data, "schannel: failed to setup sequence detection"); - if(!(BACKEND->ret_flags & ISC_RET_REPLAY_DETECT)) + if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT)) failf(data, "schannel: failed to setup replay detection"); - if(!(BACKEND->ret_flags & ISC_RET_CONFIDENTIALITY)) + if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY)) failf(data, "schannel: failed to setup confidentiality"); - if(!(BACKEND->ret_flags & ISC_RET_ALLOCATED_MEMORY)) + if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY)) failf(data, "schannel: failed to setup memory allocation"); - if(!(BACKEND->ret_flags & ISC_RET_STREAM)) + if(!(backend->ret_flags & ISC_RET_STREAM)) failf(data, "schannel: failed to setup stream orientation"); return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN - if(BACKEND->use_alpn) { + if(backend->use_alpn) { sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result); @@ -1436,13 +1434,14 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, /* save the current session data for possible re-use */ if(SSL_SET_OPTION(primary.sessionid)) { bool incache; + bool added = FALSE; struct Curl_schannel_cred *old_cred = NULL; Curl_ssl_sessionid_lock(data); incache = !(Curl_ssl_getsessionid(data, conn, isproxy, (void **)&old_cred, NULL, sockindex)); if(incache) { - if(old_cred != BACKEND->cred) { + if(old_cred != backend->cred) { DEBUGF(infof(data, "schannel: old credential handle is stale, removing")); /* we're not taking old_cred ownership here, no refcount++ is needed */ @@ -1451,17 +1450,17 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, } } if(!incache) { - result = Curl_ssl_addsessionid(data, conn, isproxy, BACKEND->cred, + result = Curl_ssl_addsessionid(data, conn, isproxy, backend->cred, sizeof(struct Curl_schannel_cred), - sockindex); + sockindex, &added); if(result) { Curl_ssl_sessionid_unlock(data); failf(data, "schannel: failed to store credential handle"); return result; } - else { + else if(added) { /* this cred session is now also referenced by sessionid cache */ - BACKEND->cred->refcount++; + backend->cred->refcount++; DEBUGF(infof(data, "schannel: stored credential handle in session cache")); } @@ -1472,7 +1471,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, if(data->set.ssl.certinfo) { int certs_count = 0; sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context); @@ -1609,7 +1608,10 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, * binding to pass the IIS extended protection checks. * Available on Windows 7 or later. */ - conn->sslContext = &BACKEND->ctxt->ctxt_handle; + { + struct ssl_backend_data *backend = connssl->backend; + conn->sslContext = &backend->ctxt->ctxt_handle; + } #endif *done = TRUE; @@ -1636,13 +1638,14 @@ schannel_send(struct Curl_easy *data, int sockindex, SecBufferDesc outbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; + struct ssl_backend_data *backend = connssl->backend; /* check if the maximum stream sizes were queried */ - if(BACKEND->stream_sizes.cbMaximumMessage == 0) { + if(backend->stream_sizes.cbMaximumMessage == 0) { sspi_status = s_pSecFn->QueryContextAttributes( - &BACKEND->ctxt->ctxt_handle, + &backend->ctxt->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, - &BACKEND->stream_sizes); + &backend->stream_sizes); if(sspi_status != SEC_E_OK) { *err = CURLE_SEND_ERROR; return -1; @@ -1650,13 +1653,13 @@ schannel_send(struct Curl_easy *data, int sockindex, } /* check if the buffer is longer than the maximum message length */ - if(len > BACKEND->stream_sizes.cbMaximumMessage) { - len = BACKEND->stream_sizes.cbMaximumMessage; + if(len > backend->stream_sizes.cbMaximumMessage) { + len = backend->stream_sizes.cbMaximumMessage; } /* calculate the complete message length and allocate a buffer for it */ - data_len = BACKEND->stream_sizes.cbHeader + len + - BACKEND->stream_sizes.cbTrailer; + data_len = backend->stream_sizes.cbHeader + len + + backend->stream_sizes.cbTrailer; ptr = (unsigned char *) malloc(data_len); if(!ptr) { *err = CURLE_OUT_OF_MEMORY; @@ -1665,12 +1668,12 @@ schannel_send(struct Curl_easy *data, int sockindex, /* setup output buffers (header, data, trailer, empty) */ InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, - ptr, BACKEND->stream_sizes.cbHeader); + ptr, backend->stream_sizes.cbHeader); InitSecBuffer(&outbuf[1], SECBUFFER_DATA, - ptr + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len)); + ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len)); InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, - ptr + BACKEND->stream_sizes.cbHeader + len, - BACKEND->stream_sizes.cbTrailer); + ptr + backend->stream_sizes.cbHeader + len, + backend->stream_sizes.cbTrailer); InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&outbuf_desc, outbuf, 4); @@ -1678,7 +1681,7 @@ schannel_send(struct Curl_easy *data, int sockindex, memcpy(outbuf[1].pvBuffer, buf, len); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ - sspi_status = s_pSecFn->EncryptMessage(&BACKEND->ctxt->ctxt_handle, 0, + sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, &outbuf_desc, 0); /* check if the message was encrypted */ @@ -1783,9 +1786,10 @@ schannel_recv(struct Curl_easy *data, int sockindex, /* we want the length of the encrypted buffer to be at least large enough that it can hold all the bytes requested and some TLS record overhead. */ size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; + struct ssl_backend_data *backend = connssl->backend; /**************************************************************************** - * Don't return or set BACKEND->recv_unrecoverable_err unless in the cleanup. + * Don't return or set backend->recv_unrecoverable_err unless in the cleanup. * The pattern for return error is set *err, optional infof, goto cleanup. * * Our priority is to always return as much decrypted data to the caller as @@ -1797,16 +1801,16 @@ schannel_recv(struct Curl_easy *data, int sockindex, DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); *err = CURLE_OK; - if(len && len <= BACKEND->decdata_offset) { + if(len && len <= backend->decdata_offset) { infof(data, "schannel: enough decrypted data is already available"); goto cleanup; } - else if(BACKEND->recv_unrecoverable_err) { - *err = BACKEND->recv_unrecoverable_err; + else if(backend->recv_unrecoverable_err) { + *err = backend->recv_unrecoverable_err; infof(data, "schannel: an unrecoverable error occurred in a prior call"); goto cleanup; } - else if(BACKEND->recv_sspi_close_notify) { + else if(backend->recv_sspi_close_notify) { /* once a server has indicated shutdown there is no more encrypted data */ infof(data, "schannel: server indicated shutdown in a prior call"); goto cleanup; @@ -1816,17 +1820,17 @@ schannel_recv(struct Curl_easy *data, int sockindex, immediately because there may be data to decrypt (in the case we want to decrypt all encrypted cached data) so handle !len later in cleanup. */ - else if(len && !BACKEND->recv_connection_closed) { + else if(len && !backend->recv_connection_closed) { /* increase enc buffer in order to fit the requested amount of data */ - size = BACKEND->encdata_length - BACKEND->encdata_offset; + size = backend->encdata_length - backend->encdata_offset; if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || - BACKEND->encdata_length < min_encdata_length) { - reallocated_length = BACKEND->encdata_offset + + backend->encdata_length < min_encdata_length) { + reallocated_length = backend->encdata_offset + CURL_SCHANNEL_BUFFER_FREE_SIZE; if(reallocated_length < min_encdata_length) { reallocated_length = min_encdata_length; } - reallocated_buffer = realloc(BACKEND->encdata_buffer, + reallocated_buffer = realloc(backend->encdata_buffer, reallocated_length); if(!reallocated_buffer) { *err = CURLE_OUT_OF_MEMORY; @@ -1834,21 +1838,21 @@ schannel_recv(struct Curl_easy *data, int sockindex, goto cleanup; } - BACKEND->encdata_buffer = reallocated_buffer; - BACKEND->encdata_length = reallocated_length; - size = BACKEND->encdata_length - BACKEND->encdata_offset; + backend->encdata_buffer = reallocated_buffer; + backend->encdata_length = reallocated_length; + size = backend->encdata_length - backend->encdata_offset; DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", - BACKEND->encdata_length)); + backend->encdata_length)); } DEBUGF(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - BACKEND->encdata_offset, BACKEND->encdata_length)); + backend->encdata_offset, backend->encdata_length)); /* read encrypted data from socket */ *err = Curl_read_plain(conn->sock[sockindex], - (char *)(BACKEND->encdata_buffer + - BACKEND->encdata_offset), + (char *)(backend->encdata_buffer + + backend->encdata_offset), size, &nread); if(*err) { nread = -1; @@ -1861,27 +1865,27 @@ schannel_recv(struct Curl_easy *data, int sockindex, infof(data, "schannel: Curl_read_plain returned error %d", *err); } else if(nread == 0) { - BACKEND->recv_connection_closed = true; + backend->recv_connection_closed = true; DEBUGF(infof(data, "schannel: server closed the connection")); } else if(nread > 0) { - BACKEND->encdata_offset += (size_t)nread; - BACKEND->encdata_is_incomplete = false; + backend->encdata_offset += (size_t)nread; + backend->encdata_is_incomplete = false; DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); } } DEBUGF(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - BACKEND->encdata_offset, BACKEND->encdata_length)); + backend->encdata_offset, backend->encdata_length)); /* decrypt loop */ - while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK && - (!len || BACKEND->decdata_offset < len || - BACKEND->recv_connection_closed)) { + while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && + (!len || backend->decdata_offset < len || + backend->recv_connection_closed)) { /* prepare data buffer for DecryptMessage call */ - InitSecBuffer(&inbuf[0], SECBUFFER_DATA, BACKEND->encdata_buffer, - curlx_uztoul(BACKEND->encdata_offset)); + InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, + curlx_uztoul(backend->encdata_offset)); /* we need 3 more empty input buffers for possible output */ InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); @@ -1891,7 +1895,7 @@ schannel_recv(struct Curl_easy *data, int sockindex, /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */ - sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle, + sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); /* check if everything went fine (server may want to renegotiate @@ -1907,37 +1911,37 @@ schannel_recv(struct Curl_easy *data, int sockindex, /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(BACKEND->decdata_length - BACKEND->decdata_offset < size || - BACKEND->decdata_length < len) { + if(backend->decdata_length - backend->decdata_offset < size || + backend->decdata_length < len) { /* increase internal decrypted data buffer */ - reallocated_length = BACKEND->decdata_offset + size; + reallocated_length = backend->decdata_offset + size; /* make sure that the requested amount of data fits */ if(reallocated_length < len) { reallocated_length = len; } - reallocated_buffer = realloc(BACKEND->decdata_buffer, + reallocated_buffer = realloc(backend->decdata_buffer, reallocated_length); if(!reallocated_buffer) { *err = CURLE_OUT_OF_MEMORY; failf(data, "schannel: unable to re-allocate memory"); goto cleanup; } - BACKEND->decdata_buffer = reallocated_buffer; - BACKEND->decdata_length = reallocated_length; + backend->decdata_buffer = reallocated_buffer; + backend->decdata_length = reallocated_length; } /* copy decrypted data to internal buffer */ size = inbuf[1].cbBuffer; if(size) { - memcpy(BACKEND->decdata_buffer + BACKEND->decdata_offset, + memcpy(backend->decdata_buffer + backend->decdata_offset, inbuf[1].pvBuffer, size); - BACKEND->decdata_offset += size; + backend->decdata_offset += size; } DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); DEBUGF(infof(data, "schannel: decrypted cached: offset %zu length %zu", - BACKEND->decdata_offset, BACKEND->decdata_length)); + backend->decdata_offset, backend->decdata_length)); } /* check for remaining encrypted data */ @@ -1948,34 +1952,34 @@ schannel_recv(struct Curl_easy *data, int sockindex, /* check if the remaining data is less than the total amount * and therefore begins after the already processed data */ - if(BACKEND->encdata_offset > inbuf[3].cbBuffer) { + if(backend->encdata_offset > inbuf[3].cbBuffer) { /* move remaining encrypted data forward to the beginning of buffer */ - memmove(BACKEND->encdata_buffer, - (BACKEND->encdata_buffer + BACKEND->encdata_offset) - + memmove(backend->encdata_buffer, + (backend->encdata_buffer + backend->encdata_offset) - inbuf[3].cbBuffer, inbuf[3].cbBuffer); - BACKEND->encdata_offset = inbuf[3].cbBuffer; + backend->encdata_offset = inbuf[3].cbBuffer; } DEBUGF(infof(data, "schannel: encrypted cached: offset %zu length %zu", - BACKEND->encdata_offset, BACKEND->encdata_length)); + backend->encdata_offset, backend->encdata_length)); } else { /* reset encrypted buffer offset, because there is no data remaining */ - BACKEND->encdata_offset = 0; + backend->encdata_offset = 0; } /* check if server wants to renegotiate the connection context */ if(sspi_status == SEC_I_RENEGOTIATE) { infof(data, "schannel: remote party requests renegotiation"); if(*err && *err != CURLE_AGAIN) { - infof(data, "schannel: can't renogotiate, an error is pending"); + infof(data, "schannel: can't renegotiate, an error is pending"); goto cleanup; } - if(BACKEND->encdata_offset) { + if(backend->encdata_offset) { *err = CURLE_RECV_ERROR; - infof(data, "schannel: can't renogotiate, " + infof(data, "schannel: can't renegotiate, " "encrypted data available"); goto cleanup; } @@ -1997,16 +2001,16 @@ schannel_recv(struct Curl_easy *data, int sockindex, else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not returned so we have to work around that in cleanup. */ - BACKEND->recv_sspi_close_notify = true; - if(!BACKEND->recv_connection_closed) { - BACKEND->recv_connection_closed = true; + backend->recv_sspi_close_notify = true; + if(!backend->recv_connection_closed) { + backend->recv_connection_closed = true; infof(data, "schannel: server closed the connection"); } goto cleanup; } } else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - BACKEND->encdata_is_incomplete = true; + backend->encdata_is_incomplete = true; if(!*err) *err = CURLE_AGAIN; infof(data, "schannel: failed to decrypt data, need more data"); @@ -2025,11 +2029,11 @@ schannel_recv(struct Curl_easy *data, int sockindex, DEBUGF(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", - BACKEND->encdata_offset, BACKEND->encdata_length)); + backend->encdata_offset, backend->encdata_length)); DEBUGF(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - BACKEND->decdata_offset, BACKEND->decdata_length)); + backend->decdata_offset, backend->decdata_length)); cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ @@ -2046,13 +2050,13 @@ schannel_recv(struct Curl_easy *data, int sockindex, assume it was graceful (close_notify) since there doesn't seem to be a way to tell. */ - if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed && - !BACKEND->recv_sspi_close_notify) { - bool isWin2k = curlx_verify_windows_version(5, 0, PLATFORM_WINNT, + if(len && !backend->decdata_offset && backend->recv_connection_closed && + !backend->recv_sspi_close_notify) { + bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT, VERSION_EQUAL); if(isWin2k && sspi_status == SEC_E_OK) - BACKEND->recv_sspi_close_notify = true; + backend->recv_sspi_close_notify = true; else { *err = CURLE_RECV_ERROR; infof(data, "schannel: server closed abruptly (missing close_notify)"); @@ -2061,23 +2065,23 @@ schannel_recv(struct Curl_easy *data, int sockindex, /* Any error other than CURLE_AGAIN is an unrecoverable error. */ if(*err && *err != CURLE_AGAIN) - BACKEND->recv_unrecoverable_err = *err; + backend->recv_unrecoverable_err = *err; - size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset; + size = len < backend->decdata_offset ? len : backend->decdata_offset; if(size) { - memcpy(buf, BACKEND->decdata_buffer, size); - memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size, - BACKEND->decdata_offset - size); - BACKEND->decdata_offset -= size; + memcpy(buf, backend->decdata_buffer, size); + memmove(backend->decdata_buffer, backend->decdata_buffer + size, + backend->decdata_offset - size); + backend->decdata_offset -= size; DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); DEBUGF(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", - BACKEND->decdata_offset, BACKEND->decdata_length)); + backend->decdata_offset, backend->decdata_length)); *err = CURLE_OK; return (ssize_t)size; } - if(!*err && !BACKEND->recv_connection_closed) + if(!*err && !backend->recv_connection_closed) *err = CURLE_AGAIN; /* It's debatable what to return when !len. We could return whatever error @@ -2116,34 +2120,32 @@ static bool schannel_data_pending(const struct connectdata *conn, int sockindex) { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; if(connssl->use) /* SSL/TLS is in use */ - return (BACKEND->decdata_offset > 0 || - (BACKEND->encdata_offset > 0 && !BACKEND->encdata_is_incomplete)); + return (backend->decdata_offset > 0 || + (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); else return FALSE; } -static void schannel_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - if(conn->ssl[sockindex].use) - /* if the SSL/TLS channel hasn't been shut down yet, do that now. */ - Curl_ssl_shutdown(data, conn, sockindex); -} - static void schannel_session_free(void *ptr) { /* this is expected to be called under sessionid lock */ struct Curl_schannel_cred *cred = ptr; - cred->refcount--; - if(cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - Curl_safefree(cred); + if(cred) { + cred->refcount--; + if(cred->refcount == 0) { + s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + Curl_safefree(cred); + } } } +/* shut down the SSL connection and clean up related memory. + this function can be called multiple times on the same connection including + if the SSL connection failed (eg connection made but failed handshake). */ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, int sockindex) { @@ -2152,13 +2154,16 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, */ struct ssl_connect_data *connssl = &conn->ssl[sockindex]; char * const hostname = SSL_HOST_NAME(); + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(data); - infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", - hostname, conn->remote_port); + if(connssl->use) { + infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", + hostname, conn->remote_port); + } - if(BACKEND->cred && BACKEND->ctxt) { + if(connssl->use && backend->cred && backend->ctxt) { SecBufferDesc BuffDesc; SecBuffer Buffer; SECURITY_STATUS sspi_status; @@ -2171,7 +2176,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); InitSecBufferDesc(&BuffDesc, &Buffer, 1); - sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle, + sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, &BuffDesc); if(sspi_status != SEC_E_OK) { @@ -2189,18 +2194,18 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, InitSecBufferDesc(&outbuf_desc, &outbuf, 1); sspi_status = s_pSecFn->InitializeSecurityContext( - &BACKEND->cred->cred_handle, - &BACKEND->ctxt->ctxt_handle, + &backend->cred->cred_handle, + &backend->ctxt->ctxt_handle, host_name, - BACKEND->req_flags, + backend->req_flags, 0, 0, NULL, 0, - &BACKEND->ctxt->ctxt_handle, + &backend->ctxt->ctxt_handle, &outbuf_desc, - &BACKEND->ret_flags, - &BACKEND->ctxt->time_stamp); + &backend->ret_flags, + &backend->ctxt->time_stamp); curlx_unicodefree(host_name); @@ -2219,38 +2224,48 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, } /* free SSPI Schannel API security context handle */ - if(BACKEND->ctxt) { + if(backend->ctxt) { DEBUGF(infof(data, "schannel: clear security context handle")); - s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle); - Curl_safefree(BACKEND->ctxt); + s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); + Curl_safefree(backend->ctxt); } /* free SSPI Schannel API credential handle */ - if(BACKEND->cred) { + if(backend->cred) { Curl_ssl_sessionid_lock(data); - schannel_session_free(BACKEND->cred); + schannel_session_free(backend->cred); Curl_ssl_sessionid_unlock(data); - BACKEND->cred = NULL; + backend->cred = NULL; } /* free internal buffer for received encrypted data */ - if(BACKEND->encdata_buffer != NULL) { - Curl_safefree(BACKEND->encdata_buffer); - BACKEND->encdata_length = 0; - BACKEND->encdata_offset = 0; - BACKEND->encdata_is_incomplete = false; + if(backend->encdata_buffer) { + Curl_safefree(backend->encdata_buffer); + backend->encdata_length = 0; + backend->encdata_offset = 0; + backend->encdata_is_incomplete = false; } /* free internal buffer for received decrypted data */ - if(BACKEND->decdata_buffer != NULL) { - Curl_safefree(BACKEND->decdata_buffer); - BACKEND->decdata_length = 0; - BACKEND->decdata_offset = 0; + if(backend->decdata_buffer) { + Curl_safefree(backend->decdata_buffer); + backend->decdata_length = 0; + backend->decdata_offset = 0; } return CURLE_OK; } +static void schannel_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) +{ + if(conn->ssl[sockindex].use) + /* Curl_ssl_shutdown resets the socket state and calls schannel_shutdown */ + Curl_ssl_shutdown(data, conn, sockindex); + else + schannel_shutdown(data, conn, sockindex); +} + static int schannel_init(void) { return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); @@ -2293,6 +2308,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, const char *pinnedpubkey) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; CERT_CONTEXT *pCertContextServer = NULL; /* Result is returned to caller */ @@ -2310,7 +2326,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, struct Curl_asn1Element *pubkey; sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); @@ -2416,8 +2432,9 @@ static CURLcode schannel_sha256sum(const unsigned char *input, static void *schannel_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { + struct ssl_backend_data *backend = connssl->backend; (void)info; - return &BACKEND->ctxt->ctxt_handle; + return &backend->ctxt->ctxt_handle; } const struct Curl_ssl Curl_ssl_schannel = { diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 1b283d0..4966cd4 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -355,7 +355,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, DWORD i; /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ - if(curlx_verify_windows_version(6, 2, PLATFORM_WINNT, + if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { #ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CertGetNameString will provide the 8-bit character string without @@ -597,7 +597,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, * trusted certificates. This is only supported on Windows 7+. */ - if(curlx_verify_windows_version(6, 1, PLATFORM_WINNT, VERSION_LESS_THAN)) { + if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, + VERSION_LESS_THAN)) { failf(data, "schannel: this version of Windows is too old to support " "certificate verification via CA bundle file."); result = CURLE_SSL_CACERT_BADFILE; diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 1e6ed5f..f7a20b2 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -997,14 +997,14 @@ CF_INLINE CFStringRef getsubject(SecCertificateRef cert) #else #if CURL_BUILD_MAC_10_7 /* Lion & later: Get the long description if we can. */ - if(SecCertificateCopyLongDescription != NULL) + if(SecCertificateCopyLongDescription) server_cert_summary = SecCertificateCopyLongDescription(NULL, cert, NULL); else #endif /* CURL_BUILD_MAC_10_7 */ #if CURL_BUILD_MAC_10_6 /* Snow Leopard: Get the certificate summary. */ - if(SecCertificateCopySubjectSummary != NULL) + if(SecCertificateCopySubjectSummary) server_cert_summary = SecCertificateCopySubjectSummary(cert); else #endif /* CURL_BUILD_MAC_10_6 */ @@ -1118,7 +1118,7 @@ static OSStatus CopyIdentityWithLabel(char *label, /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. kSecClassIdentity was introduced in Lion. If both exist, let's use them to find the certificate. */ - if(SecItemCopyMatching != NULL && kSecClassIdentity != NULL) { + if(SecItemCopyMatching && kSecClassIdentity) { CFTypeRef keys[5]; CFTypeRef values[5]; CFDictionaryRef query_dict; @@ -1248,7 +1248,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, password ? 1L : 0L, NULL, NULL); - if(options != NULL) { + if(options) { status = SecPKCS12Import(pkcs_data, options, &items); CFRelease(options); } @@ -1406,7 +1406,7 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, } #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax != NULL) { + if(SSLSetProtocolVersionMax) { SSLProtocol darwin_ver_min = kTLSProtocol1; SSLProtocol darwin_ver_max = kTLSProtocol1; CURLcode result = sectransp_version_from_curl(&darwin_ver_min, @@ -1608,7 +1608,7 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, if(tls_name) { table_cipher_name = ciphertable[i].name; } - else if(ciphertable[i].alias_name != NULL) { + else if(ciphertable[i].alias_name) { table_cipher_name = ciphertable[i].alias_name; } else { @@ -1688,7 +1688,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, #endif /* CURL_BUILD_MAC */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext != NULL) { /* use the newer API if available */ + if(SSLCreateContext) { /* use the newer API if available */ if(backend->ssl_ctx) CFRelease(backend->ssl_ctx); backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); @@ -1722,7 +1722,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, /* check to see if we've been told to use an explicit SSL/TLS version */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax != NULL) { + if(SSLSetProtocolVersionMax) { switch(conn->ssl_config.version) { case CURL_SSLVERSION_TLSv1: (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); @@ -1980,9 +1980,9 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, Darwin 15.x.x is El Capitan (10.11) */ #if CURL_BUILD_MAC - if(SSLSetSessionOption != NULL && darwinver_maj >= 13) { + if(SSLSetSessionOption && darwinver_maj >= 13) { #else - if(SSLSetSessionOption != NULL) { + if(SSLSetSessionOption) { #endif /* CURL_BUILD_MAC */ bool break_on_auth = !conn->ssl_config.verifypeer || ssl_cafile || ssl_cablob; @@ -2065,7 +2065,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 /* We want to enable 1/n-1 when using a CBC cipher unless the user specifically doesn't want us doing that: */ - if(SSLSetSessionOption != NULL) { + if(SSLSetSessionOption) { SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, !SSL_SET_OPTION(enable_beast)); SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, @@ -2109,7 +2109,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, } result = Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, - ssl_sessionid_len, sockindex); + ssl_sessionid_len, sockindex, NULL); Curl_ssl_sessionid_unlock(data); if(result) { failf(data, "failed to store ssl session"); @@ -2521,7 +2521,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, } while(0); Curl_safefree(realpubkey); - if(publicKeyBits != NULL) + if(publicKeyBits) CFRelease(publicKeyBits); return result; @@ -2947,7 +2947,7 @@ collect_server_cert(struct Curl_easy *data, private API and doesn't work as expected. So we have to look for a different symbol to make sure this code is only executed under Lion or later. */ - if(SecTrustEvaluateAsync != NULL) { + if(SecTrustEvaluateAsync) { #pragma unused(server_certs) err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); /* For some reason, SSLCopyPeerTrust() can return noErr and yet return @@ -3165,7 +3165,7 @@ static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, if(backend->ssl_ctx) { (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext != NULL) + if(SSLCreateContext) CFRelease(backend->ssl_ctx); #if CURL_SUPPORT_MAC_10_8 else @@ -3329,7 +3329,7 @@ static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ static bool sectransp_false_start(void) { #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - if(SSLSetSessionOption != NULL) + if(SSLSetSessionOption) return TRUE; #endif return FALSE; diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index e5bbe1f..6007bbb 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -516,7 +516,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, const bool isProxy, void *ssl_sessionid, size_t idsize, - int sockindex) + int sockindex, + bool *added) { size_t i; struct Curl_ssl_session *store; @@ -536,6 +537,10 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, const char *hostname = conn->host.name; #endif (void)sockindex; + + if(added) + *added = FALSE; + if(!data->state.session) return CURLE_OK; @@ -609,6 +614,9 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } + if(added) + *added = TRUE; + DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]", store->scheme, store->name, store->remote_port, isProxy ? "PROXY" : "server")); diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index beaa83d..c7bbba0 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -261,7 +261,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, const bool isProxy, void *ssl_sessionid, size_t idsize, - int sockindex); + int sockindex, + bool *added); /* Kill a single session ID entry in the cache * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). * This will call engine-specific curlssl_session_free function, which must diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 16fbb89..8c5b915 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -202,6 +202,43 @@ static int do_file_type(const char *type) return -1; } +#ifdef HAVE_LIBOQS +struct group_name_map { + const word16 group; + const char *name; +}; + +static const struct group_name_map gnm[] = { + { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, + { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, + { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, + { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" }, + { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" }, + { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" }, + { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" }, + { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" }, + { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" }, + { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" }, + { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" }, + { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" }, + { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" }, + { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" }, + { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" }, + { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" }, + { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" }, + { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" }, + { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" }, + { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" }, + { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, + { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, + { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, + { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" }, + { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" }, + { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" }, + { 0, NULL } +}; +#endif + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -210,11 +247,15 @@ static CURLcode wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { - char *ciphers; + char *ciphers, *curves; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; SSL_METHOD* req_method = NULL; curl_socket_t sockfd = conn->sock[sockindex]; +#ifdef HAVE_LIBOQS + word16 oqsAlg = 0; + size_t idx = 0; +#endif #ifdef HAVE_SNI bool sni = FALSE; #define use_sni(x) sni = (x) @@ -327,6 +368,26 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, infof(data, "Cipher selection: %s", ciphers); } + curves = SSL_CONN_CONFIG(curves); + if(curves) { + +#ifdef HAVE_LIBOQS + for(idx = 0; gnm[idx].name != NULL; idx++) { + if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { + oqsAlg = gnm[idx].group; + break; + } + } + + if(oqsAlg == 0) +#endif + { + if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + failf(data, "failed setting curves list: '%s'", curves); + return CURLE_SSL_CIPHER; + } + } + } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(SSL_CONN_CONFIG(CAfile)) { @@ -439,6 +500,14 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, 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"); + } + } +#endif + #ifdef HAVE_ALPN if(conn->bits.tls_enable_alpn) { char protocols[128]; @@ -495,7 +564,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); - infof(data, "Can't use session ID, going on without\n"); + infof(data, "Can't use session ID, going on without"); } else infof(data, "SSL re-using session ID"); @@ -749,7 +818,7 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn, if(!incache) { result = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, - 0, sockindex); + 0, sockindex, NULL); if(result) { Curl_ssl_sessionid_unlock(data); failf(data, "failed to store ssl session"); diff --git a/Utilities/cmcurl/lib/x509asn1.c b/Utilities/cmcurl/lib/x509asn1.c index 1bdaead..0341543 100644 --- a/Utilities/cmcurl/lib/x509asn1.c +++ b/Utilities/cmcurl/lib/x509asn1.c @@ -608,7 +608,10 @@ static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) /* * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at - * `buf'. Return the total string length, even if larger than `buflen'. + * `buf'. + * + * Returns the total string length, even if larger than `buflen' or -1 on + * error. */ static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn) { @@ -692,7 +695,10 @@ static const char *DNtostr(struct Curl_asn1Element *dn) if(buflen >= 0) { buf = malloc(buflen + 1); if(buf) { - encodeDN(buf, buflen + 1, dn); + if(encodeDN(buf, buflen + 1, dn) == -1) { + free(buf); + return NULL; + } buf[buflen] = '\0'; } } @@ -855,26 +861,30 @@ static const char *dumpAlgo(struct Curl_asn1Element *param, return OID2str(oid.beg, oid.end, TRUE); } -static void do_pubkey_field(struct Curl_easy *data, int certnum, - const char *label, struct Curl_asn1Element *elem) +/* return 0 on success, 1 on error */ +static int do_pubkey_field(struct Curl_easy *data, int certnum, + const char *label, struct Curl_asn1Element *elem) { const char *output; + CURLcode result = CURLE_OK; /* Generate a certificate information record for the public key. */ output = ASN1tostr(elem, 0); if(output) { if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, label, output); - if(!certnum) + result = Curl_ssl_push_certinfo(data, certnum, label, output); + if(!certnum && !result) infof(data, " %s: %s", label, output); free((char *) output); } + return result ? 1 : 0; } -static void do_pubkey(struct Curl_easy *data, int certnum, - const char *algo, struct Curl_asn1Element *param, - struct Curl_asn1Element *pubkey) +/* return 0 on success, 1 on error */ +static int do_pubkey(struct Curl_easy *data, int certnum, + const char *algo, struct Curl_asn1Element *param, + struct Curl_asn1Element *pubkey) { struct Curl_asn1Element elem; struct Curl_asn1Element pk; @@ -884,7 +894,7 @@ static void do_pubkey(struct Curl_easy *data, int certnum, /* Get the public key (single element). */ if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) - return; + return 1; if(strcasecompare(algo, "rsaEncryption")) { const char *q; @@ -892,7 +902,7 @@ static void do_pubkey(struct Curl_easy *data, int certnum, p = getASN1Element(&elem, pk.beg, pk.end); if(!p) - return; + return 1; /* Compute key length. */ for(q = elem.beg; !*q && q < elem.end; q++) @@ -910,26 +920,35 @@ static void do_pubkey(struct Curl_easy *data, int certnum, if(data->set.ssl.certinfo) { q = curl_maprintf("%lu", len); if(q) { - Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); + CURLcode result = + Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); free((char *) q); + if(result) + return 1; } } /* Generate coefficients. */ - do_pubkey_field(data, certnum, "rsa(n)", &elem); + if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) + return 1; if(!getASN1Element(&elem, p, pk.end)) - return; - do_pubkey_field(data, certnum, "rsa(e)", &elem); + return 1; + if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) + return 1; } else if(strcasecompare(algo, "dsa")) { p = getASN1Element(&elem, param->beg, param->end); if(p) { - do_pubkey_field(data, certnum, "dsa(p)", &elem); + if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) + return 1; p = getASN1Element(&elem, p, param->end); if(p) { - do_pubkey_field(data, certnum, "dsa(q)", &elem); + if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) + return 1; if(getASN1Element(&elem, p, param->end)) { - do_pubkey_field(data, certnum, "dsa(g)", &elem); - do_pubkey_field(data, certnum, "dsa(pub_key)", &pk); + if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) + return 1; } } } @@ -937,13 +956,17 @@ static void do_pubkey(struct Curl_easy *data, int certnum, else if(strcasecompare(algo, "dhpublicnumber")) { p = getASN1Element(&elem, param->beg, param->end); if(p) { - do_pubkey_field(data, certnum, "dh(p)", &elem); + if(do_pubkey_field(data, certnum, "dh(p)", &elem)) + return 1; if(getASN1Element(&elem, param->beg, param->end)) { - do_pubkey_field(data, certnum, "dh(g)", &elem); - do_pubkey_field(data, certnum, "dh(pub_key)", &pk); + if(do_pubkey_field(data, certnum, "dh(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) + return 1; } } } + return 0; } CURLcode Curl_extract_certinfo(struct Curl_easy *data, @@ -957,7 +980,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, char *cp1; size_t cl1; char *cp2; - CURLcode result; + CURLcode result = CURLE_OK; unsigned long version; size_t i; size_t j; @@ -976,8 +999,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, ccp = DNtostr(&cert.subject); if(!ccp) return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); + if(data->set.ssl.certinfo) { + result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); + if(result) + return result; + } if(!certnum) infof(data, "%2d Subject: %s", certnum, ccp); free((char *) ccp); @@ -986,11 +1012,14 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, ccp = DNtostr(&cert.issuer); if(!ccp) return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); + if(data->set.ssl.certinfo) { + result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); + } if(!certnum) infof(data, " Issuer: %s", ccp); free((char *) ccp); + if(result) + return result; /* Version (always fits in less than 32 bits). */ version = 0; @@ -1000,8 +1029,10 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, ccp = curl_maprintf("%lx", version); if(!ccp) return CURLE_OUT_OF_MEMORY; - Curl_ssl_push_certinfo(data, certnum, "Version", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp); free((char *) ccp); + if(result) + return result; } if(!certnum) infof(data, " Version: %lu (0x%lx)", version + 1, version); @@ -1011,10 +1042,12 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); if(!certnum) infof(data, " Serial Number: %s", ccp); free((char *) ccp); + if(result) + return result; /* Signature algorithm .*/ ccp = dumpAlgo(¶m, cert.signatureAlgorithm.beg, @@ -1022,30 +1055,36 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); if(!certnum) infof(data, " Signature Algorithm: %s", ccp); free((char *) ccp); + if(result) + return result; /* Start Date. */ ccp = ASN1tostr(&cert.notBefore, 0); if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); if(!certnum) infof(data, " Start Date: %s", ccp); free((char *) ccp); + if(result) + return result; /* Expire Date. */ ccp = ASN1tostr(&cert.notAfter, 0); if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); if(!certnum) infof(data, " Expire Date: %s", ccp); free((char *) ccp); + if(result) + return result; /* Public Key Algorithm. */ ccp = dumpAlgo(¶m, cert.subjectPublicKeyAlgorithm.beg, @@ -1053,21 +1092,31 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp); - if(!certnum) - infof(data, " Public Key Algorithm: %s", ccp); - do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey); + result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", + ccp); + if(!result) { + int ret; + if(!certnum) + infof(data, " Public Key Algorithm: %s", ccp); + ret = do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey); + if(ret) + result = CURLE_OUT_OF_MEMORY; /* the most likely error */ + } free((char *) ccp); + if(result) + return result; /* Signature. */ ccp = ASN1tostr(&cert.signature, 0); if(!ccp) return CURLE_OUT_OF_MEMORY; if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); + result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); if(!certnum) infof(data, " Signature: %s", ccp); free((char *) ccp); + if(result) + return result; /* Generate PEM certificate. */ result = Curl_base64_encode(data, cert.certificate.beg, @@ -1097,11 +1146,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, cp2[i] = '\0'; free(cp1); if(data->set.ssl.certinfo) - Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); + result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); if(!certnum) infof(data, "%s", cp2); free(cp2); - return CURLE_OK; + return result; } #endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL |